summaryrefslogtreecommitdiff
path: root/StoneIsland/plugins/cordova-plugin-file
diff options
context:
space:
mode:
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-file')
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/.jshintrc30
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/.ratignore1
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/CONTRIBUTING.md37
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/LICENSE202
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/NOTICE5
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/README.md833
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/RELEASENOTES.md496
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/doc/plugins.md120
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/package.json87
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/plugin.xml260
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/AssetFilesystem.java294
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/ContentFilesystem.java223
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/DirectoryManager.java134
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/EncodingException.java29
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/FileExistsException.java29
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/FileUtils.java1225
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/Filesystem.java331
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/InvalidModificationException.java30
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystem.java513
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java64
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java29
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/PendingRequests.java94
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/TypeMismatchException.java30
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/android/build-extras.gradle47
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/browser/FileProxy.js1059
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h30
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m253
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.h157
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.m1119
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h32
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m750
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.h189
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.m1056
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h32
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m733
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/src/windows/FileProxy.js1190
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/tests/package.json17
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/tests/plugin.xml43
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java93
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/tests/tests.js4163
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/tests/www/fixtures/asset-test/asset-test.txt1
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/types/index.d.ts378
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/DirectoryEntry.js117
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/DirectoryReader.js72
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/Entry.js260
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/File.js78
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileEntry.js92
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileError.js46
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileReader.js298
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileSystem.js55
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileUploadOptions.js41
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileUploadResult.js30
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/FileWriter.js325
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/Flags.js36
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/LocalFileSystem.js23
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/Metadata.js40
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/ProgressEvent.js67
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/android/FileSystem.js48
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/browser/FileSystem.js30
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/browser/Preparing.js192
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/browser/isChrome.js26
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/fileSystemPaths.js62
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/fileSystems-roots.js46
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/fileSystems.js25
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/ios/FileSystem.js29
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/osx/FileSystem.js29
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/requestFileSystem.js81
-rw-r--r--StoneIsland/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js91
68 files changed, 18677 insertions, 0 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-file/.jshintrc b/StoneIsland/plugins/cordova-plugin-file/.jshintrc
new file mode 100644
index 00000000..177bbf92
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/.jshintrc
@@ -0,0 +1,30 @@
+{
+ "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,
+ "cordova": false,
+ "File": true,
+ "FileSystem": true,
+ "FileReader": true,
+ "FileWriter": true,
+ "FileError": true,
+ "LocalFileSystem": true,
+ "Metadata": true,
+ "Flags": true,
+ "DirectoryEntry": true,
+ "resolveLocalFileSystemURL": false,
+ "requestFileSystem": true,
+ "FILESYSTEM_PREFIX": true,
+ "FILESYSTEM_PROTOCOL": true
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/.ratignore b/StoneIsland/plugins/cordova-plugin-file/.ratignore
new file mode 100644
index 00000000..1bc8940f
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/.ratignore
@@ -0,0 +1 @@
+asset-test.txt
diff --git a/StoneIsland/plugins/cordova-plugin-file/CONTRIBUTING.md b/StoneIsland/plugins/cordova-plugin-file/CONTRIBUTING.md
new file mode 100644
index 00000000..4c8e6a5e
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Contributing to Apache Cordova
+
+Anyone can contribute to Cordova. And we need your contributions.
+
+There are multiple ways to contribute: report bugs, improve the docs, and
+contribute code.
+
+For instructions on this, start with the
+[contribution overview](http://cordova.apache.org/contribute/).
+
+The details are explained there, but the important items are:
+ - Sign and submit an Apache ICLA (Contributor License Agreement).
+ - Have a Jira issue open that corresponds to your contribution.
+ - Run the tests so your patch doesn't break existing functionality.
+
+We look forward to your contributions!
diff --git a/StoneIsland/plugins/cordova-plugin-file/LICENSE b/StoneIsland/plugins/cordova-plugin-file/LICENSE
new file mode 100644
index 00000000..7a4a3ea2
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. \ No newline at end of file
diff --git a/StoneIsland/plugins/cordova-plugin-file/NOTICE b/StoneIsland/plugins/cordova-plugin-file/NOTICE
new file mode 100644
index 00000000..8ec56a52
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/NOTICE
@@ -0,0 +1,5 @@
+Apache Cordova
+Copyright 2012 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/StoneIsland/plugins/cordova-plugin-file/README.md b/StoneIsland/plugins/cordova-plugin-file/README.md
new file mode 100644
index 00000000..33057c71
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/README.md
@@ -0,0 +1,833 @@
+---
+title: File
+description: Read/write files on the device.
+---
+<!--
+# 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.
+-->
+
+|AppVeyor|Travis CI|
+|:-:|:-:|
+|[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-plugin-file?branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-file)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-file)|
+
+# cordova-plugin-file
+
+This plugin implements a File API allowing read/write access to files residing on the device.
+
+This plugin is based on several specs, including :
+The HTML5 File API
+[http://www.w3.org/TR/FileAPI/](http://www.w3.org/TR/FileAPI/)
+
+The Directories and System extensions
+Latest:
+[http://www.w3.org/TR/2012/WD-file-system-api-20120417/](http://www.w3.org/TR/2012/WD-file-system-api-20120417/)
+Although most of the plugin code was written when an earlier spec was current:
+[http://www.w3.org/TR/2011/WD-file-system-api-20110419/](http://www.w3.org/TR/2011/WD-file-system-api-20110419/)
+
+It also implements the FileWriter spec :
+[http://dev.w3.org/2009/dap/file-system/file-writer.html](http://dev.w3.org/2009/dap/file-system/file-writer.html)
+
+>*Note* While the W3C FileSystem spec is deprecated for web browsers, the FileSystem APIs are supported in Cordova applications with this plugin for the platforms listed in the _Supported Platforms_ list, with the exception of the Browser platform.
+
+To get a few ideas how to use the plugin, check out the [sample](#sample) at the bottom of this page. For additional examples (browser focused), see the HTML5 Rocks' [FileSystem article.](http://www.html5rocks.com/en/tutorials/file/filesystem/)
+
+For an overview of other storage options, refer to Cordova's
+[storage guide](http://cordova.apache.org/docs/en/latest/cordova/storage/storage.html).
+
+This plugin defines global `cordova.file` object.
+
+Although in the global scope, it is not available until after the `deviceready` event.
+
+ document.addEventListener("deviceready", onDeviceReady, false);
+ function onDeviceReady() {
+ console.log(cordova.file);
+ }
+
+## Installation
+
+ cordova plugin add cordova-plugin-file
+
+## Supported Platforms
+
+- Android
+- iOS
+- OS X
+- Windows*
+- Browser
+
+\* _These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`._
+
+## Where to Store Files
+
+As of v1.2.0, URLs to important file-system directories are provided.
+Each URL is in the form _file:///path/to/spot/_, and can be converted to a
+`DirectoryEntry` using `window.resolveLocalFileSystemURL()`.
+
+* `cordova.file.applicationDirectory` - Read-only directory where the application
+ is installed. (_iOS_, _Android_, _BlackBerry 10_, _OSX_, _windows_)
+
+* `cordova.file.applicationStorageDirectory` - Root directory of the application's
+ sandbox; on iOS & windows this location is read-only (but specific subdirectories [like
+ `/Documents` on iOS or `/localState` on windows] are read-write). All data contained within
+ is private to the app. (_iOS_, _Android_, _BlackBerry 10_, _OSX_)
+
+* `cordova.file.dataDirectory` - Persistent and private data storage within the
+ application's sandbox using internal memory (on Android, if you need to use
+ external memory, use `.externalDataDirectory`). On iOS, this directory is not
+ synced with iCloud (use `.syncedDataDirectory`). (_iOS_, _Android_, _BlackBerry 10_, _windows_)
+
+* `cordova.file.cacheDirectory` - Directory for cached data files or any files
+ that your app can re-create easily. The OS may delete these files when the device
+ runs low on storage, nevertheless, apps should not rely on the OS to delete files
+ in here. (_iOS_, _Android_, _BlackBerry 10_, _OSX_, _windows_)
+
+* `cordova.file.externalApplicationStorageDirectory` - Application space on
+ external storage. (_Android_)
+
+* `cordova.file.externalDataDirectory` - Where to put app-specific data files on
+ external storage. (_Android_)
+
+* `cordova.file.externalCacheDirectory` - Application cache on external storage.
+ (_Android_)
+
+* `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_)
+
+* `cordova.file.tempDirectory` - Temp directory that the OS can clear at will. Do not
+ rely on the OS to clear this directory; your app should always remove files as
+ applicable. (_iOS_, _OSX_, _windows_)
+
+* `cordova.file.syncedDataDirectory` - Holds app-specific files that should be synced
+ (e.g. to iCloud). (_iOS_, _windows_)
+
+* `cordova.file.documentsDirectory` - Files private to the app, but that are meaningful
+ to other application (e.g. Office files). Note that for _OSX_ this is the user's `~/Documents` directory. (_iOS_, _OSX_)
+
+* `cordova.file.sharedDirectory` - Files globally available to all applications (_BlackBerry 10_)
+
+## File System Layouts
+
+Although technically an implementation detail, it can be very useful to know how
+the `cordova.file.*` properties map to physical paths on a real device.
+
+### iOS File System Layout
+
+| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | persistent? | OS clears | sync | private |
+|:-----------------------------------------------|:----------------------------|:----------------------|:----:|:-----------:|:---------:|:----:|:-------:|
+| `/var/mobile/Applications/<UUID>/` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Yes |
+| &nbsp;&nbsp;&nbsp;`appname.app/` | applicationDirectory | bundle | r | N/A | N/A | N/A | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`www/` | - | - | r | N/A | N/A | N/A | Yes |
+| &nbsp;&nbsp;&nbsp;`Documents/` | documentsDirectory | documents | r/w | Yes | No | Yes | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`NoCloud/` | - | documents-nosync | r/w | Yes | No | No | Yes |
+| &nbsp;&nbsp;&nbsp;`Library` | - | library | r/w | Yes | No | Yes? | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`NoCloud/` | dataDirectory | library-nosync | r/w | Yes | No | No | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Cloud/` | syncedDataDirectory | - | r/w | Yes | No | Yes | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Caches/` | cacheDirectory | cache | r/w | Yes* | Yes\*\*\*| No | Yes |
+| &nbsp;&nbsp;&nbsp;`tmp/` | tempDirectory | - | r/w | No\*\* | Yes\*\*\*| No | Yes |
+
+
+ \* Files persist across app restarts and upgrades, but this directory can
+ be cleared whenever the OS desires. Your app should be able to recreate any
+ content that might be deleted.
+
+\*\* Files may persist across app restarts, but do not rely on this behavior. Files
+ are not guaranteed to persist across updates. Your app should remove files from
+ this directory when it is applicable, as the OS does not guarantee when (or even
+ if) these files are removed.
+
+\*\*\* The OS may clear the contents of this directory whenever it feels it is
+ necessary, but do not rely on this. You should clear this directory as
+ appropriate for your application.
+
+### Android File System Layout
+
+| Device Path | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistent? | OS clears | private |
+|:------------------------------------------------|:----------------------------|:--------------------------|:----:|:-----------:|:---------:|:-------:|
+| `file:///android_asset/` | applicationDirectory | assets | r | N/A | N/A | Yes |
+| `/data/data/<app-id>/` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes |
+| &nbsp;&nbsp;&nbsp;`cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes |
+| &nbsp;&nbsp;&nbsp;`files` | dataDirectory | files | r/w | Yes | No | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Documents` | | documents | r/w | Yes | No | Yes |
+| `<sdcard>/` | externalRootDirectory | sdcard | r/w | Yes | No | No |
+| &nbsp;&nbsp;&nbsp;`Android/data/<app-id>/` | externalApplicationStorageDirectory | - | r/w | Yes | No | No |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No |
+| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`files` | externalDataDirectory | files-external | r/w | Yes | No | No |
+
+\* The OS may periodically clear this directory, but do not rely on this behavior. Clear
+ the contents of this directory as appropriate for your application. Should a user
+ purge the cache manually, the contents of this directory are removed.
+
+\*\* The OS does not clear this directory automatically; you are responsible for managing
+ the contents yourself. Should the user purge the cache manually, the contents of the
+ directory are removed.
+
+**Note**: If external storage can't be mounted, the `cordova.file.external*`
+properties are `null`.
+
+### OS X File System Layout
+
+| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | OS clears | private |
+|:-------------------------------------------------|:----------------------------|:----------------------|:----:|:---------:|:-------:|
+| `/Applications/<appname>.app/` | - | bundle | r | N/A | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;`Content/Resources/` | applicationDirectory | - | r | N/A | Yes |
+| `~/Library/Application Support/<bundle-id>/` | applicationStorageDirectory | - | r/w | No | Yes |
+| &nbsp;&nbsp;&nbsp;&nbsp;`files/` | dataDirectory | - | r/w | No | Yes |
+| `~/Documents/` | documentsDirectory | documents | r/w | No | No |
+| `~/Library/Caches/<bundle-id>/` | cacheDirectory | cache | r/w | No | Yes |
+| `/tmp/` | tempDirectory | - | r/w | Yes\* | Yes |
+| `/` | rootDirectory | root | r/w | No\*\* | No |
+
+**Note**: This is the layout for non sandboxed applications. I you enable sandboxing, the `applicationStorageDirectory` will be below ` ~/Library/Containers/<bundle-id>/Data/Library/Application Support`.
+
+\* Files persist across app restarts and upgrades, but this directory can
+ be cleared whenever the OS desires. Your app should be able to recreate any
+ content that might be deleted. You should clear this directory as
+ appropriate for your application.
+
+\*\* Allows access to the entire file system. This is only available for non sandboxed apps.
+
+### Windows File System Layout
+
+| Device Path | `cordova.file.*` | r/w? | persistent? | OS clears | private |
+|:------------------------------------------------------|:----------------------------|:----:|:-----------:|:---------:|:-------:|
+| `ms-appdata:///` | applicationDirectory | r | N/A | N/A | Yes |
+| &nbsp;&nbsp;&nbsp;`local/` | dataDirectory | r/w | Yes | No | Yes |
+| &nbsp;&nbsp;&nbsp;`temp/` | cacheDirectory | r/w | No | Yes\* | Yes |
+| &nbsp;&nbsp;&nbsp;`temp/` | tempDirectory | r/w | No | Yes\* | Yes |
+| &nbsp;&nbsp;&nbsp;`roaming/` | syncedDataDirectory | r/w | Yes | No | Yes |
+
+\* The OS may periodically clear this directory
+
+
+## Android Quirks
+
+### Android Persistent storage location
+
+There are multiple valid locations to store persistent files on an Android
+device. See [this page](http://developer.android.com/guide/topics/data/data-storage.html)
+for an extensive discussion of the various possibilities.
+
+Previous versions of the plugin would choose the location of the temporary and
+persistent files on startup, based on whether the device claimed that the SD
+Card (or equivalent storage partition) was mounted. If the SD Card was mounted,
+or if a large internal storage partition was available (such as on Nexus
+devices,) then the persistent files would be stored in the root of that space.
+This meant that all Cordova apps could see all of the files available on the
+card.
+
+If the SD card was not available, then previous versions would store data under
+`/data/data/<packageId>`, which isolates apps from each other, but may still
+cause data to be shared between users.
+
+It is now possible to choose whether to store files in the internal file
+storage location, or using the previous logic, with a preference in your
+application's `config.xml` file. To do this, add one of these two lines to
+`config.xml`:
+
+ <preference name="AndroidPersistentFileLocation" value="Internal" />
+
+ <preference name="AndroidPersistentFileLocation" value="Compatibility" />
+
+Without this line, the File plugin will use `Internal` as the default. If
+a preference tag is present, and is not one of these values, the application
+will not start.
+
+If your application has previously been shipped to users, using an older (pre-
+3.0.0) version of this plugin, and has stored files in the persistent filesystem,
+then you should set the preference to `Compatibility` if your config.xml does not specify a location for the persistent filesystem. Switching the location to
+"Internal" would mean that existing users who upgrade their application may be
+unable to access their previously-stored files, depending on their device.
+
+If your application is new, or has never previously stored files in the
+persistent filesystem, then the `Internal` setting is generally recommended.
+
+### Slow recursive operations for /android_asset
+
+Listing asset directories is really slow on Android. You can speed it up though, by
+adding `src/android/build-extras.gradle` to the root of your android project (also
+requires cordova-android@4.0.0 or greater).
+
+### Permisson to write to external storage when it's not mounted on Marshmallow
+
+Marshmallow requires the apps to ask for permissions when reading/writing to external locations. By
+[default](http://developer.android.com/guide/topics/data/data-storage.html#filesExternal), your app has permission to write to
+`cordova.file.applicationStorageDirectory` and `cordova.file.externalApplicationStorageDirectory`, and the plugin doesn't request permission
+for these two directories unless external storage is not mounted. However due to a limitation, when external storage is not mounted, it would ask for
+permission to write to `cordova.file.externalApplicationStorageDirectory`.
+
+## iOS Quirks
+
+- `cordova.file.applicationStorageDirectory` is read-only; attempting to store
+ files within the root directory will fail. Use one of the other `cordova.file.*`
+ properties defined for iOS (only `applicationDirectory` and `applicationStorageDirectory` are
+ read-only).
+- `FileReader.readAsText(blob, encoding)`
+ - The `encoding` parameter is not supported, and UTF-8 encoding is always in effect.
+
+### iOS Persistent storage location
+
+There are two valid locations to store persistent files on an iOS device: the
+Documents directory and the Library directory. Previous versions of the plugin
+only ever stored persistent files in the Documents directory. This had the
+side-effect of making all of an application's files visible in iTunes, which
+was often unintended, especially for applications which handle lots of small
+files, rather than producing complete documents for export, which is the
+intended purpose of the directory.
+
+It is now possible to choose whether to store files in the documents or library
+directory, with a preference in your application's `config.xml` file. To do this,
+add one of these two lines to `config.xml`:
+
+ <preference name="iosPersistentFileLocation" value="Library" />
+
+ <preference name="iosPersistentFileLocation" value="Compatibility" />
+
+Without this line, the File plugin will use `Compatibility` as the default. If
+a preference tag is present, and is not one of these values, the application
+will not start.
+
+If your application has previously been shipped to users, using an older (pre-
+1.0) version of this plugin, and has stored files in the persistent filesystem,
+then you should set the preference to `Compatibility`. Switching the location to
+`Library` would mean that existing users who upgrade their application would be
+unable to access their previously-stored files.
+
+If your application is new, or has never previously stored files in the
+persistent filesystem, then the `Library` setting is generally recommended.
+
+## Browser Quirks
+
+### Common quirks and remarks
+- Each browser uses its own sandboxed filesystem. IE and Firefox use IndexedDB as a base.
+All browsers use forward slash as directory separator in a path.
+- Directory entries have to be created successively.
+For example, the call `fs.root.getDirectory('dir1/dir2', {create:true}, successCallback, errorCallback)`
+will fail if dir1 did not exist.
+- The plugin requests user permission to use persistent storage at the application first start.
+- Plugin supports `cdvfile://localhost` (local resources) only. I.e. external resources are not supported via `cdvfile`.
+- The plugin does not follow ["File System API 8.3 Naming restrictions"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions).
+- Blob and File' `close` function is not supported.
+- `FileSaver` and `BlobBuilder` are not supported by this plugin and don't have stubs.
+- The plugin does not support `requestAllFileSystems`. This function is also missing in the specifications.
+- Entries in directory will not be removed if you use `create: true` flag for existing directory.
+- Files created via constructor are not supported. You should use entry.file method instead.
+- Each browser uses its own form for blob URL references.
+- `readAsDataURL` function is supported, but the mediatype in Chrome depends on entry name extension,
+mediatype in IE is always empty (which is the same as `text-plain` according the specification),
+the mediatype in Firefox is always `application/octet-stream`.
+For example, if the content is `abcdefg` then Firefox returns `data:application/octet-stream;base64,YWJjZGVmZw==`,
+IE returns `data:;base64,YWJjZGVmZw==`, Chrome returns `data:<mediatype depending on extension of entry name>;base64,YWJjZGVmZw==`.
+- `toInternalURL` returns the path in the form `file:///persistent/path/to/entry` (Firefox, IE).
+Chrome returns the path in the form `cdvfile://localhost/persistent/file`.
+
+### Chrome quirks
+- Chrome filesystem is not immediately ready after device ready event. As a workaround you can subscribe to `filePluginIsReady` event.
+Example:
+```javascript
+window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false);
+```
+You can use `window.isFilePluginReadyRaised` function to check whether event was already raised.
+- window.requestFileSystem TEMPORARY and PERSISTENT filesystem quotas are not limited in Chrome.
+- To increase persistent storage in Chrome you need to call `window.initPersistentFileSystem` method. Persistent storage quota is 5 MB by default.
+- Chrome requires `--allow-file-access-from-files` run argument to support API via `file:///` protocol.
+- `File` object will be not changed if you use flag `{create:true}` when getting an existing `Entry`.
+- events `cancelable` property is set to true in Chrome. This is contrary to the [specification](http://dev.w3.org/2009/dap/file-system/file-writer.html).
+- `toURL` function in Chrome returns `filesystem:`-prefixed path depending on application host.
+For example, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`.
+- `toURL` function result does not contain trailing slash in case of directory entry.
+Chrome resolves directories with slash-trailed urls correctly though.
+- `resolveLocalFileSystemURL` method requires the inbound `url` to have `filesystem` prefix. For example, `url` parameter for `resolveLocalFileSystemURL`
+should be in the form `filesystem:file:///persistent/somefile.txt` as opposed to the form `file:///persistent/somefile.txt` in Android.
+- Deprecated `toNativeURL` function is not supported and does not have a stub.
+- `setMetadata` function is not stated in the specifications and not supported.
+- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of SYNTAX_ERR(code: 8) on requesting of a non-existant filesystem.
+- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of PATH_EXISTS_ERR(code: 12) on trying to exclusively create a file or directory, which already exists.
+- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively on the root file system.
+- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NOT_FOUND_ERR(code: 1) on trying to moveTo directory that does not exist.
+
+### IndexedDB-based impl quirks (Firefox and IE)
+- `.` and `..` are not supported.
+- IE does not support `file:///`-mode; only hosted mode is supported (http://localhost:xxxx).
+- Firefox filesystem size is not limited but each 50MB extension will request a user permission.
+IE10 allows up to 10mb of combined AppCache and IndexedDB used in implementation of filesystem without prompting,
+once you hit that level you will be asked if you want to allow it to be increased up to a max of 250mb per site.
+So `size` parameter for `requestFileSystem` function does not affect filesystem in Firefox and IE.
+- `readAsBinaryString` function is not stated in the Specs and not supported in IE and does not have a stub.
+- `file.type` is always null.
+- You should not create entry using DirectoryEntry instance callback result which was deleted.
+Otherwise, you will get a 'hanging entry'.
+- Before you can read a file, which was just written you need to get a new instance of this file.
+- `setMetadata` function, which is not stated in the Specs supports `modificationTime` field change only.
+- `copyTo` and `moveTo` functions do not support directories.
+- Directories metadata is not supported.
+- Both Entry.remove and directoryEntry.removeRecursively don't fail when removing
+non-empty directories - directories being removed are cleaned along with contents instead.
+- `abort` and `truncate` functions are not supported.
+- progress events are not fired. For example, this handler will be not executed:
+```javascript
+writer.onprogress = function() { /*commands*/ };
+```
+
+## Upgrading Notes
+
+In v1.0.0 of this plugin, the `FileEntry` and `DirectoryEntry` structures have changed,
+to be more in line with the published specification.
+
+Previous (pre-1.0.0) versions of the plugin stored the device-absolute-file-location
+in the `fullPath` property of `Entry` objects. These paths would typically look like
+
+ /var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
+ /storage/emulated/0/path/to/file (Android)
+
+These paths were also returned by the `toURL()` method of the `Entry` objects.
+
+With v1.0.0, the `fullPath` attribute is the path to the file, _relative to the root of
+the HTML filesystem_. So, the above paths would now both be represented by a `FileEntry`
+object with a `fullPath` of
+
+ /path/to/file
+
+If your application works with device-absolute-paths, and you previously retrieved those
+paths through the `fullPath` property of `Entry` objects, then you should update your code
+to use `entry.toURL()` instead.
+
+For backwards compatibility, the `resolveLocalFileSystemURL()` method will accept a
+device-absolute-path, and will return an `Entry` object corresponding to it, as long as that
+file exists within either the `TEMPORARY` or `PERSISTENT` filesystems.
+
+This has particularly been an issue with the File-Transfer plugin, which previously used
+device-absolute-paths (and can still accept them). It has been updated to work correctly
+with FileSystem URLs, so replacing `entry.fullPath` with `entry.toURL()` should resolve any
+issues getting that plugin to work with files on the device.
+
+In v1.1.0 the return value of `toURL()` was changed (see [CB-6394](https://issues.apache.org/jira/browse/CB-6394))
+to return an absolute 'file://' URL. wherever possible. To ensure a 'cdvfile:'-URL you can use `toInternalURL()` now.
+This method will now return filesystem URLs of the form
+
+ cdvfile://localhost/persistent/path/to/file
+
+which can be used to identify the file uniquely.
+
+## cdvfile protocol
+**Purpose**
+
+`cdvfile://localhost/persistent|temporary|another-fs-root*/path/to/file` can be used for platform-independent file paths.
+cdvfile paths are supported by core plugins - for example you can download an mp3 file to cdvfile-path via `cordova-plugin-file-transfer` and play it via `cordova-plugin-media`.
+
+__*Note__: See [Where to Store Files](#where-to-store-files), [File System Layouts](#file-system-layouts) and [Configuring the Plugin](#configuring-the-plugin-optional) for more details about available fs roots.
+
+To use `cdvfile` as a tag' `src` you can convert it to native path via `toURL()` method of the resolved fileEntry, which you can get via `resolveLocalFileSystemURL` - see examples below.
+
+You can also use `cdvfile://` paths directly in the DOM, for example:
+```HTML
+<img src="cdvfile://localhost/persistent/img/logo.png" />
+```
+
+__Note__: This method requires following Content Security rules updates:
+* Add `cdvfile:` scheme to `Content-Security-Policy` meta tag of the index page, e.g.:
+ - `<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: `**cdvfile:**` https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">`
+* Add `<access origin="cdvfile://*" />` to `config.xml`.
+
+**Converting cdvfile:// to native path**
+
+```javascript
+resolveLocalFileSystemURL('cdvfile://localhost/temporary/path/to/file.mp4', function(entry) {
+ var nativePath = entry.toURL();
+ console.log('Native URI: ' + nativePath);
+ document.getElementById('video').src = nativePath;
+```
+
+**Converting native path to cdvfile://**
+
+```javascript
+resolveLocalFileSystemURL(nativePath, function(entry) {
+ console.log('cdvfile URI: ' + entry.toInternalURL());
+```
+
+**Using cdvfile in core plugins**
+
+```javascript
+fileTransfer.download(uri, 'cdvfile://localhost/temporary/path/to/file.mp3', function (entry) { ...
+```
+```javascript
+var my_media = new Media('cdvfile://localhost/temporary/path/to/file.mp3', ...);
+my_media.play();
+```
+
+#### cdvfile quirks
+- Using `cdvfile://` paths in the DOM is not supported on Windows platform (a path can be converted to native instead).
+
+
+## List of Error Codes and Meanings
+When an error is thrown, one of the following codes will be used.
+
+| Code | Constant |
+|-----:|:------------------------------|
+| 1 | `NOT_FOUND_ERR` |
+| 2 | `SECURITY_ERR` |
+| 3 | `ABORT_ERR` |
+| 4 | `NOT_READABLE_ERR` |
+| 5 | `ENCODING_ERR` |
+| 6 | `NO_MODIFICATION_ALLOWED_ERR` |
+| 7 | `INVALID_STATE_ERR` |
+| 8 | `SYNTAX_ERR` |
+| 9 | `INVALID_MODIFICATION_ERR` |
+| 10 | `QUOTA_EXCEEDED_ERR` |
+| 11 | `TYPE_MISMATCH_ERR` |
+| 12 | `PATH_EXISTS_ERR` |
+
+## Configuring the Plugin (Optional)
+
+The set of available filesystems can be configured per-platform. Both iOS and
+Android recognize a <preference> tag in `config.xml` which names the
+filesystems to be installed. By default, all file-system roots are enabled.
+
+ <preference name="iosExtraFilesystems" value="library,library-nosync,documents,documents-nosync,cache,bundle,root" />
+ <preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets,root" />
+
+### Android
+
+* `files`: The application's internal file storage directory
+* `files-external`: The application's external file storage directory
+* `sdcard`: The global external file storage directory (this is the root of the SD card, if one is installed). You must have the `android.permission.WRITE_EXTERNAL_STORAGE` permission to use this.
+* `cache`: The application's internal cache directory
+* `cache-external`: The application's external cache directory
+* `assets`: The application's bundle (read-only)
+* `root`: The entire device filesystem
+* `applicationDirectory`: ReadOnly with restricted access. Copying files in this directory is possible, but reading it directly results in 'file not found'.
+Android also supports a special filesystem named "documents", which represents a "/Documents/" subdirectory within the "files" filesystem.
+
+### iOS
+
+* `library`: The application's Library directory
+* `documents`: The application's Documents directory
+* `cache`: The application's Cache directory
+* `bundle`: The application's bundle; the location of the app itself on disk (read-only)
+* `root`: The entire device filesystem
+
+By default, the library and documents directories can be synced to iCloud. You can also request two additional filesystems, `library-nosync` and `documents-nosync`, which represent a special non-synced directory within the `/Library` or `/Documents` filesystem.
+
+## Sample: Create Files and Directories, Write, Read, and Append files <a name="sample"></a>
+
+The File plugin allows you to do things like store files in a temporary or persistent storage location for your app (sandboxed storage) and to store files in other platform-dependent locations. The code snippets in this section demonstrate different tasks including:
+* [Accessing the file system](#persistent)
+* Using cross-platform Cordova file URLs to [store your files](#appendFile) (see _Where to Store Files_ for more info)
+* Creating [files](#persistent) and [directories](#createDir)
+* [Writing to files](#writeFile)
+* [Reading files](#readFile)
+* [Appending files](#appendFile)
+* [Display an image file](#displayImage)
+
+## Create a persistent file <a name="persistent"></a>
+
+Before you use the File plugin APIs, you can get access to the file system using `requestFileSystem`. When you do this, you can request either persistent or temporary storage. Persistent storage will not be removed unless permission is granted by the user.
+
+When you get file system access using `requestFileSystem`, access is granted for the sandboxed file system only (the sandbox limits access to the app itself), not for general access to any file system location on the device. (To access file system locations outside the sandboxed storage, use other methods such as window.resolveLocalFileSystemURL, which support platform-specific locations. For one example of this, see _Append a File_.)
+
+Here is a request for persistent storage.
+
+>*Note* When targeting WebView clients (instead of a browser) or native apps (Windows), you dont need to use `requestQuota` before using persistent storage.
+
+```js
+window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
+
+ console.log('file system open: ' + fs.name);
+ fs.root.getFile("newPersistentFile.txt", { create: true, exclusive: false }, function (fileEntry) {
+
+ console.log("fileEntry is file?" + fileEntry.isFile.toString());
+ // fileEntry.name == 'someFile.txt'
+ // fileEntry.fullPath == '/someFile.txt'
+ writeFile(fileEntry, null);
+
+ }, onErrorCreateFile);
+
+}, onErrorLoadFs);
+```
+
+The success callback receives FileSystem object (fs). Use `fs.root` to return a DirectoryEntry object, which you can use to create or get a file (by calling `getFile`). In this example, `fs.root` is a DirectoryEntry object that represents the persistent storage in the sandboxed file system.
+
+The success callback for `getFile` receives a FileEntry object. You can use this to perform file write and file read operations.
+
+## Create a temporary file
+
+Here is an example of a request for temporary storage. Temporary storage may be deleted by the operating system if the device runs low on memory.
+
+```js
+window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
+
+ console.log('file system open: ' + fs.name);
+ createFile(fs.root, "newTempFile.txt", false);
+
+}, onErrorLoadFs);
+```
+When you are using temporary storage, you can create or get the file by calling `getFile`. As in the persistent storage example, this will give you a FileEntry object that you can use for read or write operations.
+
+```js
+function createFile(dirEntry, fileName, isAppend) {
+ // Creates a new file or returns the file if it already exists.
+ dirEntry.getFile(fileName, {create: true, exclusive: false}, function(fileEntry) {
+
+ writeFile(fileEntry, null, isAppend);
+
+ }, onErrorCreateFile);
+
+}
+```
+
+## Write to a file <a name="writeFile"></a>
+
+Once you have a FileEntry object, you can write to the file by calling `createWriter`, which returns a FileWriter object in the success callback. Call the `write` method of FileWriter to write to the file.
+
+```js
+function writeFile(fileEntry, dataObj) {
+ // Create a FileWriter object for our FileEntry (log.txt).
+ fileEntry.createWriter(function (fileWriter) {
+
+ fileWriter.onwriteend = function() {
+ console.log("Successful file write...");
+ readFile(fileEntry);
+ };
+
+ fileWriter.onerror = function (e) {
+ console.log("Failed file write: " + e.toString());
+ };
+
+ // If data object is not passed in,
+ // create a new Blob instead.
+ if (!dataObj) {
+ dataObj = new Blob(['some file data'], { type: 'text/plain' });
+ }
+
+ fileWriter.write(dataObj);
+ });
+}
+```
+
+## Read a file <a name="readFile"></a>
+
+You also need a FileEntry object to read an existing file. Use the file property of FileEntry to get the file reference, and then create a new FileReader object. You can use methods like `readAsText` to start the read operation. When the read operation is complete, `this.result` stores the result of the read operation.
+
+```js
+function readFile(fileEntry) {
+
+ fileEntry.file(function (file) {
+ var reader = new FileReader();
+
+ reader.onloadend = function() {
+ console.log("Successful file read: " + this.result);
+ displayFileData(fileEntry.fullPath + ": " + this.result);
+ };
+
+ reader.readAsText(file);
+
+ }, onErrorReadFile);
+}
+```
+
+## Append a file using alternative methods <a name="appendFile"></a>
+
+Of course, you will often want to append existing files instead of creating new ones. Here is an example of that. This example shows another way that you can access the file system using window.resolveLocalFileSystemURL. In this example, pass the cross-platform Cordova file URL, cordova.file.dataDirectory, to the function. The success callback receives a DirectoryEntry object, which you can use to do things like create a file.
+
+```js
+window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
+ console.log('file system open: ' + dirEntry.name);
+ var isAppend = true;
+ createFile(dirEntry, "fileToAppend.txt", isAppend);
+}, onErrorLoadFs);
+```
+
+In addition to this usage, you can use `resolveLocalFileSystemURL` to get access to some file system locations that are not part of the sandboxed storage system. See _Where to store Files_ for more information; many of these storage locations are platform-specific. You can also pass cross-platform file system locations to `resolveLocalFileSystemURL` using the _cdvfile protocol_.
+
+For the append operation, there is nothing new in the `createFile` function that is called in the preceding code (see the preceding examples for the actual code). `createFile` calls `writeFile`. In `writeFile`, you check whether an append operation is requested.
+
+Once you have a FileWriter object, call the `seek` method, and pass in the index value for the position where you want to write. In this example, you also test whether the file exists. After calling seek, then call the write method of FileWriter.
+
+```js
+function writeFile(fileEntry, dataObj, isAppend) {
+ // Create a FileWriter object for our FileEntry (log.txt).
+ fileEntry.createWriter(function (fileWriter) {
+
+ fileWriter.onwriteend = function() {
+ console.log("Successful file read...");
+ readFile(fileEntry);
+ };
+
+ fileWriter.onerror = function (e) {
+ console.log("Failed file read: " + e.toString());
+ };
+
+ // If we are appending data to file, go to the end of the file.
+ if (isAppend) {
+ try {
+ fileWriter.seek(fileWriter.length);
+ }
+ catch (e) {
+ console.log("file doesn't exist!");
+ }
+ }
+ fileWriter.write(dataObj);
+ });
+}
+```
+
+## Store an existing binary file <a name="binaryFile"></a>
+
+We already showed how to write to a file that you just created in the sandboxed file system. What if you need to get access to an existing file and convert that to something you can store on your device? In this example, you obtain a file using an xhr request, and then save it to the cache in the sandboxed file system.
+
+Before you get the file, get a FileSystem reference using `requestFileSystem`. By passing window.TEMPORARY in the method call (same as before), the returned FileSystem object (fs) represents the cache in the sandboxed file system. Use `fs.root` to get the DirectoryEntry object that you need.
+
+```js
+window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
+
+ console.log('file system open: ' + fs.name);
+ getSampleFile(fs.root);
+
+}, onErrorLoadFs);
+```
+
+For completeness, here is the xhr request to get a Blob image. There is nothing Cordova-specific in this code, except that you forward the DirectoryEntry reference that you already obtained as an argument to the saveFile function. You will save the blob image and display it later after reading the file (to validate the operation).
+
+```js
+function getSampleFile(dirEntry) {
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'http://cordova.apache.org/static/img/cordova_bot.png', true);
+ xhr.responseType = 'blob';
+
+ xhr.onload = function() {
+ if (this.status == 200) {
+
+ var blob = new Blob([this.response], { type: 'image/png' });
+ saveFile(dirEntry, blob, "downloadedImage.png");
+ }
+ };
+ xhr.send();
+}
+```
+>*Note* For Cordova 5 security, the preceding code requires that you add the domain name, http://cordova.apache.org, to the Content-Security-Policy <meta> element in index.html.
+
+After getting the file, copy the contents to a new file. The current DirectoryEntry object is already associated with the app cache.
+
+```js
+function saveFile(dirEntry, fileData, fileName) {
+
+ dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
+
+ writeFile(fileEntry, fileData);
+
+ }, onErrorCreateFile);
+}
+```
+
+In writeFile, you pass in the Blob object as the dataObj and you will save that in the new file.
+
+```js
+function writeFile(fileEntry, dataObj, isAppend) {
+
+ // Create a FileWriter object for our FileEntry (log.txt).
+ fileEntry.createWriter(function (fileWriter) {
+
+ fileWriter.onwriteend = function() {
+ console.log("Successful file write...");
+ if (dataObj.type == "image/png") {
+ readBinaryFile(fileEntry);
+ }
+ else {
+ readFile(fileEntry);
+ }
+ };
+
+ fileWriter.onerror = function(e) {
+ console.log("Failed file write: " + e.toString());
+ };
+
+ fileWriter.write(dataObj);
+ });
+}
+```
+
+After writing to the file, read it and display it. You saved the image as binary data, so you can read it using FileReader.readAsArrayBuffer.
+
+```js
+function readBinaryFile(fileEntry) {
+
+ fileEntry.file(function (file) {
+ var reader = new FileReader();
+
+ reader.onloadend = function() {
+
+ console.log("Successful file write: " + this.result);
+ displayFileData(fileEntry.fullPath + ": " + this.result);
+
+ var blob = new Blob([new Uint8Array(this.result)], { type: "image/png" });
+ displayImage(blob);
+ };
+
+ reader.readAsArrayBuffer(file);
+
+ }, onErrorReadFile);
+}
+```
+
+After reading the data, you can display the image using code like this. Use window.URL.createObjectURL to get a DOM string for the Blob image.
+
+```js
+function displayImage(blob) {
+
+ // Displays image if result is a valid DOM string for an image.
+ var elem = document.getElementById('imageFile');
+ // Note: Use window.URL.revokeObjectURL when finished with image.
+ elem.src = window.URL.createObjectURL(blob);
+}
+```
+
+## Display an image file <a name="displayImage"></a>
+
+To display an image using a FileEntry, you can call the `toURL` method.
+
+```js
+function displayImageByFileURL(fileEntry) {
+ var elem = document.getElementById('imageFile');
+ elem.src = fileEntry.toURL();
+}
+```
+
+If you are using some platform-specific URIs instead of a FileEntry and you want to display an image, you may need to include the main part of the URI in the Content-Security-Policy <meta> element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your <meta> element. Here is an example.
+
+```html
+<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
+```
+
+## Create Directories <a name="createDir"></a>
+
+In the code here, you create directories in the root of the app storage location. You could use this code with any writable storage location (that is, any DirectoryEntry). Here, you write to the application cache (assuming that you used window.TEMPORARY to get your FileSystem object) by passing fs.root into this function.
+
+This code creates the /NewDirInRoot/images folder in the application cache. For platform-specific values, look at _File System Layouts_.
+
+```js
+function createDirectory(rootDirEntry) {
+ rootDirEntry.getDirectory('NewDirInRoot', { create: true }, function (dirEntry) {
+ dirEntry.getDirectory('images', { create: true }, function (subDirEntry) {
+
+ createFile(subDirEntry, "fileInNewSubDir.txt");
+
+ }, onErrorGetDir);
+ }, onErrorGetDir);
+}
+```
+
+When creating subfolders, you need to create each folder separately as shown in the preceding code.
diff --git a/StoneIsland/plugins/cordova-plugin-file/RELEASENOTES.md b/StoneIsland/plugins/cordova-plugin-file/RELEASENOTES.md
new file mode 100644
index 00000000..3853525a
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/RELEASENOTES.md
@@ -0,0 +1,496 @@
+<!--
+#
+# 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.
+#
+-->
+# Release Notes
+
+### 6.0.2 (Jun 27, 2019)
+
+- fix: manually fix line endings of some files that were using CRLF ([`e8e06c3`](https://github.com/apache/cordova-plugin-file/commit/e8e06c3))
+- chore: fix repo and issue urls and license in package.json and plugin.xml ([`1bca166`](https://github.com/apache/cordova-plugin-file/commit/1bca166))
+- docs: remove outdated translations ([`a898b85`](https://github.com/apache/cordova-plugin-file/commit/a898b85))
+- build: add `.gitattributes` to force LF (instead of possible CRLF on Windows) ([`d691a04`](https://github.com/apache/cordova-plugin-file/commit/d691a04))
+- build: add `.npmignore` to remove unneeded files from npm package ([`a255202`](https://github.com/apache/cordova-plugin-file/commit/a255202))
+- ci(travis): Update Travis CI configuration for new paramedic ([#314](https://github.com/apache/cordova-plugin-file/issues/314)) ([`2b93a67`](https://github.com/apache/cordova-plugin-file/commit/2b93a67))
+- fix(browser): support safari private browsing mode by using base64 text instead of blob ([#301](https://github.com/apache/cordova-plugin-file/issues/301)) ([`c609ff6`](https://github.com/apache/cordova-plugin-file/commit/c609ff6))
+- chore(github): Add or update GitHub pull request and issue template ([`b762743`](https://github.com/apache/cordova-plugin-file/commit/b762743))
+- fix(types): CB-13569: fix inverted LocalFileSystem enum ([#274](https://github.com/apache/cordova-plugin-file/issues/274)) ([`d135cd0`](https://github.com/apache/cordova-plugin-file/commit/d135cd0))
+- docs: remove JIRA link ([`341fa9c`](https://github.com/apache/cordova-plugin-file/commit/341fa9c))
+- fix(types): CB-13850: Fix spelling in typed-file: property name filesystem (wrong… ([#229](https://github.com/apache/cordova-plugin-file/issues/229)) ([`4642fde`](https://github.com/apache/cordova-plugin-file/commit/4642fde))
+- fix(types): CB-13960: fix FileWriter.write argument type definition for typescript ([#231](https://github.com/apache/cordova-plugin-file/issues/231)) ([`5353b84`](https://github.com/apache/cordova-plugin-file/commit/5353b84))
+- ci(travis): also accept terms for android sdk `android-27` ([`6e7871b`](https://github.com/apache/cordova-plugin-file/commit/6e7871b))
+- docs: add details for `applicationDirectory` on android ([#234](https://github.com/apache/cordova-plugin-file/issues/234)) ([`c2f5216`](https://github.com/apache/cordova-plugin-file/commit/c2f5216))
+- fix: require FileReader in FileWriter to fix 'write' exec not being called ([#237](https://github.com/apache/cordova-plugin-file/issues/237)) ([`4a92bbb`](https://github.com/apache/cordova-plugin-file/commit/4a92bbb))
+- fix(android): CB-14181: (android) Fix bug - Cannot read property 'filesystemName' of null ([#235](https://github.com/apache/cordova-plugin-file/issues/235)) ([`cc3aedb`](https://github.com/apache/cordova-plugin-file/commit/cc3aedb))
+- ci(travis): CB-13753: Add build-tools-26.0.2 to travis ([#228](https://github.com/apache/cordova-plugin-file/issues/228)) ([`d8cc0fd`](https://github.com/apache/cordova-plugin-file/commit/d8cc0fd), [`5e12b5e`](https://github.com/apache/cordova-plugin-file/commit/5e12b5e))
+- chore: Fix release notes ([#227](https://github.com/apache/cordova-plugin-file/issues/227)) ([`c248827`](https://github.com/apache/cordova-plugin-file/commit/c248827))
+
+
+### 6.0.1 (Dec 27, 2017)
+* [CB-13704](https://issues.apache.org/jira/browse/CB-13704) Fix to allow 6.0.0 version install
+
+### 6.0.0 (Dec 15, 2017)
+* [CB-13668](https://issues.apache.org/jira/browse/CB-13668) Remove deprecated platforms
+
+### 5.0.0 (Nov 06, 2017)
+* [CB-13481](https://issues.apache.org/jira/browse/CB-13481) (android) Don't ask for permission to read `file:///android_asset/`
+* [CB-13518](https://issues.apache.org/jira/browse/CB-13518) Add 'protective' entry to `cordovaDependencies`
+* [CB-13472](https://issues.apache.org/jira/browse/CB-13472) (CI) Fixed Travis **Android** builds again
+* [CB-13294](https://issues.apache.org/jira/browse/CB-13294) Remove `cordova-plugin-compat`
+* fixing `README` in use of `window.resolveLocalFileSystemURL`
+* [CB-12895](https://issues.apache.org/jira/browse/CB-12895) setup `eslint` and took out `jshint`
+* [CB-13028](https://issues.apache.org/jira/browse/CB-13028) (CI) **Browser** builds on Travis
+* [CB-13000](https://issues.apache.org/jira/browse/CB-13000) (CI) Speed up **Android** builds
+* [CB-12355](https://issues.apache.org/jira/browse/CB-12355) (iOS) add desciption about the `mimeTypeForFileAtPath` method
+* [CB-12355](https://issues.apache.org/jira/browse/CB-12355) (iOS) fix `FileEntry.file.type`
+* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`.
+
+### 4.3.3 (Apr 27, 2017)
+* [CB-12622](https://issues.apache.org/jira/browse/CB-12622) Added **Android 6.0** build badge to `README`
+* [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder
+
+### 4.3.2 (Feb 28, 2017)
+* [CB-12353](https://issues.apache.org/jira/browse/CB-12353) Corrected merges usage in `plugin.xml`
+* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped`
+* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0**
+* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed **Windows 8.1** build badges
+
+### 4.3.1 (Dec 07, 2016)
+* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 4.3.1
+* [CB-12112](https://issues.apache.org/jira/browse/CB-12112) windows: Make available to move folder trees
+* fix ENCODING_ERR for applicationDirectory
+* [CB-11848](https://issues.apache.org/jira/browse/CB-11848) windows: Remove duplicate slash after file system path
+* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…"
+* [CB-11947](https://issues.apache.org/jira/browse/CB-11947) fixed typo that occurs when adding file-transfer plugin
+* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version.
+
+### 4.3.0 (Sep 08, 2016)
+* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies
+* Add handling for `SecurityException`
+* [CB-11368](https://issues.apache.org/jira/browse/CB-11368) **android**: Resolve content `URLs` produced by contacts plugin
+* Plugin uses `Android Log class` and not `Cordova LOG class`
+* [CB-11693](https://issues.apache.org/jira/browse/CB-11693) **ios**: Run copy and move operations in the background thread
+* [CB-11699](https://issues.apache.org/jira/browse/CB-11699) Read files as Data URLs properly
+* [CB-11305](https://issues.apache.org/jira/browse/CB-11305) Enable `cdvfile: assets fs root` for `DOM` requests
+* [CB-11385](https://issues.apache.org/jira/browse/CB-11385) android: Import java.nio.charset.Charset in LocalFileSystem class
+* Add badges for paramedic builds on Jenkins
+* [CB-11407](https://issues.apache.org/jira/browse/CB-11407) ios: added extern keyword to constants to fix phonegap-webview-ios template issue.
+* [CB-11385](https://issues.apache.org/jira/browse/CB-11385) **android**: Does not pass sonarqube scan
+* Add pull request template.
+* Minor edits to the `README.md`
+* [CB-11142](https://issues.apache.org/jira/browse/CB-11142) Fix the `NeedPermission` code for the case when external media is not mounted in Android
+* [CB-11003](https://issues.apache.org/jira/browse/CB-11003) Adding samples to Readme.
+* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md
+* [CB-11115](https://issues.apache.org/jira/browse/CB-11115) **android**: Removing dependency on FileDescriptor toString in content provider tests
+
+### 4.2.0 (Apr 15, 2016)
+* [CB-10960](https://issues.apache.org/jira/browse/CB-10960) Uncaught `#<FileError>` in `write()` when `readyState != WRITING ?`
+* Replace `PermissionHelper.java` with `cordova-plugin-compat`
+* [CB-10977](https://issues.apache.org/jira/browse/CB-10977) **Android** Removing global state used for permission requests
+* CB-10798, [CB-10384](https://issues.apache.org/jira/browse/CB-10384) Fixing permissions for **Marshmallow**.
+* Fix test failure on **WP 8.1**
+* [CB-10577](https://issues.apache.org/jira/browse/CB-10577) **Windows** `resolveLocalFileSystemURL` should omit trailing slash for file
+* [CB-7862](https://issues.apache.org/jira/browse/CB-7862) `FileReader` reads large files in chunks with progress.
+* [CB-10577](https://issues.apache.org/jira/browse/CB-10577) **Android** `resolveLocalFileSystemURL` should detect directory vs file.
+* [CB-9753](https://issues.apache.org/jira/browse/CB-9753) index out of bounds on `requestFileSystem`.
+* Remove `warning` emoji, as it doesn't correctly display in the docs website: cordova.apache.org/docs/en/dev/cordova-plugin-file/index.html. This closes #166
+* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins
+* [CB-10411](https://issues.apache.org/jira/browse/CB-10411) Error in `file.spec.129` of `cordova-plugin-file`
+
+### 4.1.1 (Feb 09, 2016)
+* Edit package.json license to match SPDX id
+* [CB-10419](https://issues.apache.org/jira/browse/CB-10419) cordova-plugin-file 4.0.0 error with browserify workflow
+
+### 4.1.0 (Jan 15, 2016)
+* added `.ratignore` file
+* [CB-10319](https://issues.apache.org/jira/browse/CB-10319) **android** Adding reflective helper methods for permission requests
+* [CB-10023](https://issues.apache.org/jira/browse/CB-10023) Fix `proxy not found error` on Chrome.
+* [CB-8863](https://issues.apache.org/jira/browse/CB-8863) **ios** Fix block usage of self
+
+### 4.0.0 (Nov 18, 2015)
+* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest
+* [CB-8497](https://issues.apache.org/jira/browse/CB-8497) Fix handling of file paths with `#` character
+* Do not inject default `AndroidPersistentFileLocation` into `config.xml`
+* [CB-9891](https://issues.apache.org/jira/browse/CB-9891): Fix permission errors due to `URI encoding` inconsistency on **Android**
+* Fixed `NullPointer Exception` in **Android 5** and above due to invalid column name on cursor
+* Fix default persistent file location
+* fix `applicationDirectory` to use `ms-appx:///`
+* Add **Windows** paths to `cordova.file` object
+* [CB-9851](https://issues.apache.org/jira/browse/CB-9851) Document `cdvfile` protocol quirk - using `cdvfile://` in the `DOM` is not supported on **Windows**
+* [CB-9752](https://issues.apache.org/jira/browse/CB-9752) `getDirectory` fails on valid directory with assets filesystem
+* [CB-7253](https://issues.apache.org/jira/browse/CB-7253) `requestFileSystem` fails when no external storage is present
+* Adding permissions for **Marshmallow**. Now supports **Anrdoid 6.0**
+* Fixing contribute link.
+* always use setters to fix memory issues without `ARC` for **iOS**
+* [CB-9331](https://issues.apache.org/jira/browse/CB-9331) `getFreeDiskSpace` **iOS**.
+* override `resolveLocalFileSystemURL` by `webkitResolveLocalFileSystemURL` for **browser** platform add `.project` into git ignore list
+* Fail with `FileError.ENCODING_ERR` on encoding exception.
+* [CB-9544](https://issues.apache.org/jira/browse/CB-9544) Add file plugin for **OSX**
+* [CB-9539](https://issues.apache.org/jira/browse/CB-9539) Fixed test failure on **Android** emulator
+* Added docs on `CSP` rules needed for using `cdvfile` in DOM src. This closes #120
+* Added `cdvfile` protocol purpose description and examples
+
+### 3.0.0 (Aug 18, 2015)
+* Make Android default persistent file location internal
+* Fixed issue with file paths not existing when using browserify
+* [CB-9251](https://issues.apache.org/jira/browse/CB-9251): Changed from Intents to Preferences object as per the issue
+* [CB-9215](https://issues.apache.org/jira/browse/CB-9215) Add cordova-plugin-file manual test for windows platform
+
+### 2.1.0 (Jun 17, 2015)
+* added missing license header
+* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-file documentation translation: cordova-plugin-file
+* fix npm md
+* [CB-8844](https://issues.apache.org/jira/browse/CB-8844) Increased timeout for asset tests
+* Updated resolveFileSystem.js so it can be parsed by uglifyJS
+* [CB-8860](https://issues.apache.org/jira/browse/CB-8860) cordova-plugin-file documentation translation: cordova-plugin-file
+* [CB-8792](https://issues.apache.org/jira/browse/CB-8792) Fixes reading of json files using readAsText
+
+### 2.0.0 (Apr 15, 2015)
+* [CB-8849](https://issues.apache.org/jira/browse/CB-8849) Fixed ReadAsArrayBuffer to return ArrayBuffer and not Array on WP8
+* [CB-8819](https://issues.apache.org/jira/browse/CB-8819) Fixed FileReader's readAsBinaryString on wp8
+* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump
+* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) android: Fix broken unit tests from plugin rename
+* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) changed plugin-id to pacakge-name
+* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) properly updated translated docs to use new id
+* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) updated translated docs to use new id
+* Use TRAVIS_BUILD_DIR, install paramedic by npm
+* docs: added Windows to supported platforms
+* [CB-8699](https://issues.apache.org/jira/browse/CB-8699) [CB-6428](https://issues.apache.org/jira/browse/CB-6428) Fix uncompressed assets being copied as zero length files
+* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) android: Fix assets FileEntry having size of -1
+* android: Move URLforFullPath into base class (and rename to localUrlforFullPath)
+* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) Mention build-extras.gradle in README
+* [CB-7109](https://issues.apache.org/jira/browse/CB-7109) android: Parse arguments off of the main thread (close #97)
+* [CB-8695](https://issues.apache.org/jira/browse/CB-8695) ios: Fix `blob.slice()` for `asset-library` URLs (close #105)
+* Tweak build-extras.gradle to just read/write to main `assets/` instead of `build/`
+* [CB-8689](https://issues.apache.org/jira/browse/CB-8689) Fix NPE in makeEntryForNativeUri (was affecting file-transfer)
+* [CB-8675](https://issues.apache.org/jira/browse/CB-8675) Revert "CB-8351 ios: Use base64EncodedStringWithOptions instead of CordovaLib's class extension"
+* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme
+* [CB-8659](https://issues.apache.org/jira/browse/CB-8659): ios: 4.0.x Compatibility: Remove use of initWebView method
+* Add a cache to speed up AssetFilesystem directory listings
+* [CB-8663](https://issues.apache.org/jira/browse/CB-8663) android: Don't notify MediaScanner of private files
+* Don't log stacktrace for normal exceptions (e.g. file not found)
+* android: Don't use LimitedInputStream when reading entire file (optimization)
+* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) android: Add support for directory copies from assets -> filesystem
+* android: Add `listChildren()`: Java-consumable version of `readEntriesAtLocalURL()`
+* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) android: Add support for file:///android_asset URLs
+* [CB-8642](https://issues.apache.org/jira/browse/CB-8642) android: Fix content URIs not working with resolve / copy
+* Tweak tests to fail if deleteEntry fails (rather than time out)
+* android: Ensure LocalFilesystemURL can only be created with "cdvfile" URLs
+* android: Move CordovaResourceApi into Filesystem base class
+* android: Use `CordovaResourceApi.mapUriToFile()` rather than own custom logic in ContentFilesystem
+* android: Use Uri.parse rather than manual parsing in resolveLocalFileSystemURI
+* Tweak test case that failed twice on error rather than just once
+* android: Delete invalid JavaDoc (lint errors)
+* android: Use CordovaResourceApi rather than FileHelper
+* [CB-8032](https://issues.apache.org/jira/browse/CB-8032) - File Plugin - Add nativeURL external method support for CDVFileSystem->makeEntryForPath:isDirectory: (closes #96)
+* [CB-8567](https://issues.apache.org/jira/browse/CB-8567) Integrate TravisCI
+* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-file documentation translation: cordova-plugin-file
+* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file
+* [CB-7956](https://issues.apache.org/jira/browse/CB-7956) Add cordova-plugin-file support for browser platform
+* [CB-8423](https://issues.apache.org/jira/browse/CB-8423) Corrected usage of done() in async tests
+* [CB-8459](https://issues.apache.org/jira/browse/CB-8459) Fixes spec 111 failure due to incorrect relative paths handling
+* Code cleanup, whitespace
+* Added nativeURL property to FileEntry, implemented readAsArrayBuffer and readAsBinaryString
+
+### 1.3.3 (Feb 04, 2015)
+* [CB-7927](https://issues.apache.org/jira/browse/CB-7927) Encoding data to bytes instead of chars when writing a file.
+* ios: Fix compile warning about implicit int conversion
+* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use base64EncodedStringWithOptions instead of CordovaLib's class extension
+* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension
+* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use a local copy of valueForKeyIsNumber rather than CordovaLib's version
+* windows: Handle url's containing absolute windows path starting with drive letter and colon (encoded as %3A) through root FS
+* windows: Rework to use normal url form
+* android: refactor: Make Filesystem base class store its own name, rootUri, and rootEntry
+* android: Simplify code a bit by making makeEntryForPath not throw JSONException
+* [CB-6431](https://issues.apache.org/jira/browse/CB-6431) android: Fix plugin breaking content: URLs
+* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Never create new FileSystem instances (except on windows since they don't implement requestAllFileSystems())
+
+### 1.3.2 (Dec 02, 2014)
+* Gets rid of thread block error in File plugin
+* [CB-7917](https://issues.apache.org/jira/browse/CB-7917) Made tests file.spec.114 - 116 pass for **Windows** platform
+* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs
+* [CB-7602](https://issues.apache.org/jira/browse/CB-7602): Fix `isCopyOnItself` logic
+* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-file documentation translation: cordova-plugin-file
+* Use one proxy for both **Windows** and **Windows8** platforms
+* [CB-6994](https://issues.apache.org/jira/browse/CB-6994) Fixes result, returned by proxy's write method
+* [fxos] update `__format__` to match `pathsPrefix`
+* [CB-6994](https://issues.apache.org/jira/browse/CB-6994) Improves merged code to be able to write a File
+* Optimize `FileProxy` for **Windows** platforms
+* Synchronize changes with **Windows** platform
+* Fix function write for big files on **Windows 8**
+* Write file in background
+* [CB-7487](https://issues.apache.org/jira/browse/CB-7487) **Android** Broadcast file write This allows MTP USB shares to show the file immediately without reboot/manual refresh using 3rd party app.
+* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-file documentation translation: cordova-plugin-file
+* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin
+
+### 1.3.1 (Sep 17, 2014)
+* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-file documentation translation
+* [CB-7272](https://issues.apache.org/jira/browse/CB-7272) Replace confusing "r/o" abbreviation with just "r"
+* [CB-7423](https://issues.apache.org/jira/browse/CB-7423) encode path before attempting to resolve
+* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Fix the filesystem name in resolveLocalFileSystemUri
+* [CB-7445](https://issues.apache.org/jira/browse/CB-7445) [BlackBerry10] resolveLocalFileSystemURI - change DEFAULT_SIZE to MAX_SIZE
+* [CB-7458](https://issues.apache.org/jira/browse/CB-7458) [BlackBerry10] resolveLocalFileSystemURL - add filesystem property
+* [CB-7445](https://issues.apache.org/jira/browse/CB-7445) [BlackBerry10] Add default file system size to prevent quota exceeded error on initial install
+* [CB-7431](https://issues.apache.org/jira/browse/CB-7431) Avoid calling done() twice in file.spec.109 test
+* [CB-7413](https://issues.apache.org/jira/browse/CB-7413) Adds support of 'ms-appdata://' URIs
+* [CB-7422](https://issues.apache.org/jira/browse/CB-7422) [File Tests] Use proper fileSystem to create fullPath
+* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) [Entry] get proper filesystem in Entry
+* Amazon related changes.
+* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Remove leading slash statement from condition
+* Refactored much of the logic in FileMetadata constructor. Directory.size will return 0
+* [CB-7419](https://issues.apache.org/jira/browse/CB-7419) [WP8] Added support to get metada from dir
+* [CB-7418](https://issues.apache.org/jira/browse/CB-7418) [DirectoryEntry] Added fullPath variable as part of condition
+* [CB-7417](https://issues.apache.org/jira/browse/CB-7417) [File tests] added proper matcher to compare fullPath property
+* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Partial revert to resolve WP8 failures
+* Overwrite existing file on getFile when create is true
+* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) [CB-6148](https://issues.apache.org/jira/browse/CB-6148): Ensure that return values from copy and move operations reference the correct filesystem
+* [CB-6724](https://issues.apache.org/jira/browse/CB-6724) changed style detail on documentation
+* Added new js files to amazon-fireos platform.
+* Adds Windows platform
+* Fixes multiple mobilespec tests errors
+* Removed test/tests.js module from main plugin.xml
+* [CB-7094](https://issues.apache.org/jira/browse/CB-7094) renamed folder to tests + added nested plugin.xml
+* added documentation for manual tests
+* [CB-6923](https://issues.apache.org/jira/browse/CB-6923) Adding support to handle relative paths
+* Style improvements on Manual tests
+* [CB-7094](https://issues.apache.org/jira/browse/CB-7094) Ported File manual tests
+
+### 1.3.0 (Aug 06, 2014)
+* **FFOS** Remove unsupported paths from requestAllPaths
+* **FFOS** Support for resolve URI, request all paths and local app directory.
+* [CB-4263](https://issues.apache.org/jira/browse/CB-4263) set ready state to done before onload
+* [CB-7167](https://issues.apache.org/jira/browse/CB-7167) [BlackBerry10] copyTo - return wrapped entry rather than native
+* [CB-7167](https://issues.apache.org/jira/browse/CB-7167) [BlackBerry10] Add directory support to getFileMetadata
+* [CB-7167](https://issues.apache.org/jira/browse/CB-7167) [BlackBerry10] Fix tests detection of blob support (window.Blob is BlobConstructor object)
+* [CB-7161](https://issues.apache.org/jira/browse/CB-7161) [BlackBerry10] Add file system directory paths
+* [CB-7093](https://issues.apache.org/jira/browse/CB-7093) Create separate plugin.xml for new-style tests
+* [CB-7057](https://issues.apache.org/jira/browse/CB-7057) Docs update: elaborate on what directories are for
+* [CB-7093](https://issues.apache.org/jira/browse/CB-7093): Undo the effects of an old bad S&R command
+* [CB-7093](https://issues.apache.org/jira/browse/CB-7093): Remove a bunch of unneeded log messages
+* [CB-7093](https://issues.apache.org/jira/browse/CB-7093): Add JS module to plugin.xml file for auto-tests
+* [CB-7093](https://issues.apache.org/jira/browse/CB-7093) Ported automated file tests
+* **WINDOWS** remove extra function closure, not needed
+* **WINDOWS** remove check for undefined fail(), it is defined by the proxy and always exists
+* **WINDOWS** re-apply readAsBinaryString and readAsArrayBuffer
+* **WINDOWS** Moved similar calls to be the same calls, aliased long namespaced functions
+* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs.
+* [CB-6571](https://issues.apache.org/jira/browse/CB-6571) Fix getParentForLocalURL to work correctly with directories with trailing '/' (This closes #58)
+* UTTypeCopyPreferredTagWithClass returns nil mimetype for css when there is no network
+* updated spec links in docs ( en only )
+* [CB-6571](https://issues.apache.org/jira/browse/CB-6571) add trailing space it is missing in DirectoryEnty constructor.
+* [CB-6980](https://issues.apache.org/jira/browse/CB-6980) Fixing filesystem:null property in Entry
+* Add win8 support for readAsBinaryString and readAsArrayBuffer
+* [FFOS] Update FileProxy.js
+* [CB-6940](https://issues.apache.org/jira/browse/CB-6940): Fixing up commit from dzeims
+* [CB-6940](https://issues.apache.org/jira/browse/CB-6940): Android: cleanup try/catch exception handling
+* [CB-6940](https://issues.apache.org/jira/browse/CB-6940): `context.getExternal*` methods return null if sdcard isn't in mounted state, causing exceptions that prevent startup from reaching readystate
+* Fix mis-handling of filesystem reference in Entry.moveTo ('this' used in closure).
+* [CB-6902](https://issues.apache.org/jira/browse/CB-6902): Use File.lastModified rather than .lastModifiedDate
+* [CB-6922](https://issues.apache.org/jira/browse/CB-6922): Remove unused getMetadata native code
+* [CB-6922](https://issues.apache.org/jira/browse/CB-6922): Use getFileMetadata consistently to get metadata
+* changed fullPath to self.rootDocsPath
+* [CB-6890](https://issues.apache.org/jira/browse/CB-6890): Fix pluginManager access for 4.0.x branch
+
+### 1.2.1
+* [CB-6922](https://issues.apache.org/jira/browse/CB-6922) Fix inconsistent handling of lastModifiedDate and modificationTime
+* [CB-285](https://issues.apache.org/jira/browse/CB-285): Document filesystem root properties
+
+### 1.2.0 (Jun 05, 2014)
+* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #31
+* updated this reference to window
+* Add missing semicolon (copy & paste error)
+* Fix compiler warning about symbol in interface not matching implementation
+* Fix sorting order in supported platforms
+* ubuntu: increase quota value
+* ubuntu: Change FS URL scheme to 'cdvfile'
+* ubuntu: Return size with Entry.getMetadata() method
+* [CB-6803](https://issues.apache.org/jira/browse/CB-6803) Add license
+* Initial implementation for Firefox OS
+* Small wording tweaks
+* Fixed toURL() toInternalURL() information in the doku
+* ios: Don't fail a write of zero-length payload.
+* [CB-285](https://issues.apache.org/jira/browse/CB-285) Docs for cordova.file.\*Directory properties
+* [CB-285](https://issues.apache.org/jira/browse/CB-285) Add cordova.file.\*Directory properties for iOS & Android
+* [CB-3440](https://issues.apache.org/jira/browse/CB-3440) [BlackBerry10] Proxy based implementation
+* Fix typo in docs "app-bundle" -> "bundle"
+* [CB-6583](https://issues.apache.org/jira/browse/CB-6583) ios: Fix failing to create entry when space in parent path
+* [CB-6571](https://issues.apache.org/jira/browse/CB-6571) android: Make DirectoryEntry.toURL() have a trailing /
+* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md
+* [CB-6525](https://issues.apache.org/jira/browse/CB-6525) android, ios: Allow file: URLs in all APIs. Fixes FileTransfer.download not being called.
+* fix the Windows 8 implementation of the getFile method
+* Update File.js for typo: lastModifiedData --> lastModifiedDate (closes #38)
+* Add error codes.
+* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 1.0.0
+* Add NOTICE file
+* [CB-6114](https://issues.apache.org/jira/browse/CB-6114) Updated version and RELEASENOTES.md for release 1.0.1
+* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 1.0.0
+
+### 1.1.0 (Apr 17, 2014)
+* [CB-4965](https://issues.apache.org/jira/browse/CB-4965): Remove tests from file plugin
+* Android: Allow file:/ URLs
+* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy
+* [CB-6249](https://issues.apache.org/jira/browse/CB-6249): [android] Opportunistically resolve content urls to file
+* [CB-6394](https://issues.apache.org/jira/browse/CB-6394): [ios, android] Add extra filesystem roots
+* [CB-6394](https://issues.apache.org/jira/browse/CB-6394): [ios, android] Fix file resolution for the device root case
+* [CB-6394](https://issues.apache.org/jira/browse/CB-6394): [ios] Return ENCODING_ERR when fs name is not valid
+* [CB-6393](https://issues.apache.org/jira/browse/CB-6393): Change behaviour of toURL and toNativeURL
+* ios: Style: plugin initialization
+* ios: Fix handling of file URLs with encoded spaces
+* Always use Android's recommended temp file location for temporary file system
+* [CB-6352](https://issues.apache.org/jira/browse/CB-6352): Allow FileSystem objects to be serialized to JSON
+* [CB-5959](https://issues.apache.org/jira/browse/CB-5959): size is explicitly 0 if not set, file.spec.46&47 are testing the type of size
+* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] Add deprecated version of resolveLocalFileSystemURI
+* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] add file:/// prefix for toURI / toURL
+* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] Polyfill window.requestAnimationFrame for OS < 10.2
+* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] Override window.resolveLocalFileSystemURL
+* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit
+* ios: Don't cache responses from CDVFile's URLProtocol
+* [CB-6199](https://issues.apache.org/jira/browse/CB-6199): [iOS] Fix toNativeURL() not escaping characters properly
+* [CB-6148](https://issues.apache.org/jira/browse/CB-6148): Fix cross-filesystem copy and move
+* fixed setMetadata() to use the formatted fullPath
+* corrected typo which leads to a "comma expression"
+* [CB-4952](https://issues.apache.org/jira/browse/CB-4952): ios: Resolve symlinks in file:// URLs
+* Add docs about the extraFileSystems preference
+* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers
+
+### 1.0.1 (Feb 28, 2014)
+* [CB-6116](https://issues.apache.org/jira/browse/CB-6116) Fix error where resolveLocalFileSystemURL would fail
+* [CB-6106](https://issues.apache.org/jira/browse/CB-6106) Add support for nativeURL attribute on Entry objects
+* [CB-6110](https://issues.apache.org/jira/browse/CB-6110) iOS: Fix typo in filesystemPathForURL: method
+* Android: Use most specific FS match when resolving file: URIs
+* iOS: Update fileSystemURLforLocalPath: to return the most match url.
+* Allow third-party plugin registration, and the total count of fs type is not limited to just 4.
+* [CB-6097](https://issues.apache.org/jira/browse/CB-6097) Added missing files for amazon-fireos platform. Added onLoad flag to true.
+* [CB-6087](https://issues.apache.org/jira/browse/CB-6087) Android, iOS: Load file plugin on startup
+* [CB-6013](https://issues.apache.org/jira/browse/CB-6013) BlackBerry10: wrap webkit prefixed called in requestAnimationFrame
+* Update plugin writers' documentation
+* [CB-6080](https://issues.apache.org/jira/browse/CB-6080) Fix file copy when src and dst are on different local file systems
+* [CB-6057](https://issues.apache.org/jira/browse/CB-6057) Add methods for plugins to convert between URLs and paths
+* [CB-6050](https://issues.apache.org/jira/browse/CB-6050) Public method for returning a FileEntry from a device file path
+* [CB-2432](https://issues.apache.org/jira/browse/CB-2432) [CB-3185](https://issues.apache.org/jira/browse/CB-3185), [CB-5975](https://issues.apache.org/jira/browse/CB-5975): Fix Android handling of content:// URLs
+* [CB-6022](https://issues.apache.org/jira/browse/CB-6022) Add upgrade notes to doc
+* [CB-5233](https://issues.apache.org/jira/browse/CB-5233) Make asset-library urls work properly on iOS
+* [CB-6012](https://issues.apache.org/jira/browse/CB-6012) Preserve query strings on cdvfile:// URLs where necessary
+* [CB-6010](https://issues.apache.org/jira/browse/CB-6010) Test properly for presence of URLforFilesystemPath method
+* [CB-5959](https://issues.apache.org/jira/browse/CB-5959) Entry.getMetadata should return size attribute
+
+### 1.0.0 (Feb 05, 2014)
+* [CB-5974](https://issues.apache.org/jira/browse/CB-5974): Use safe 'Compatibilty' mode by default
+* [CB-5915](https://issues.apache.org/jira/browse/CB-5915): [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Reorganize preference code to make defaults possible
+* [CB-5974](https://issues.apache.org/jira/browse/CB-5974): Android: Don't allow File operations to continue when not configured
+* [CB-5960](https://issues.apache.org/jira/browse/CB-5960): ios: android: Properly handle parent references in getFile/getDirectory
+* [ubuntu] adopt to recent changes
+* Add default FS root to new FS objects
+* [CB-5899](https://issues.apache.org/jira/browse/CB-5899): Make DirectoryReader.readEntries return properly formatted Entry objects
+* Add constuctor params to FileUploadResult related to [CB-2421](https://issues.apache.org/jira/browse/CB-2421)
+* Fill out filesystem attribute of entities returned from resolveLocalFileSystemURL
+* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Create documents directories if they don't exist
+* [CB-5915](https://issues.apache.org/jira/browse/CB-5915): Create documents directories if they don't exist
+* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Android: Fix unfortunate NPE in config check
+* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Android: Add "/files/" to persistent files path
+* [CB-5915](https://issues.apache.org/jira/browse/CB-5915): ios: Update config preference (and docs) to match issue
+* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Android: Add config preference for Android persistent storage location
+* iOS: Add config preference for iOS persistent storage location
+* iOS: Android: Allow third-party plugin registration
+* Android: Expose filePlugin getter so that other plugins can register filesystems
+* Fix typos in deprecation message
+* Add backwards-compatibility shim for file-transfer
+* Android: Allow third-party plugin registration
+* [CB-5810](https://issues.apache.org/jira/browse/CB-5810) [BlackBerry10] resolve local:/// paths (application assets)
+* [CB-5774](https://issues.apache.org/jira/browse/CB-5774): create DirectoryEntry instead of FileEntry
+* Initial fix for [CB-5747](https://issues.apache.org/jira/browse/CB-5747)
+* Change default FS URL scheme to "cdvfile"
+* Android: Properly format content urls
+* Android, iOS: Replace "filesystem" protocol string with constant
+* Android: Allow absolute paths on Entry.getFile / Entry.getDirectory
+* Android: Make clear that getFile takes a path, not just a filename
+* [CB-5008](https://issues.apache.org/jira/browse/CB-5008): Rename resolveLocalFileSystemURI to resolveLocalFileSystemURL; deprecate original
+* Remove old file reference from plugin.xml
+* Android: Refactor File API
+* [CB-4899](https://issues.apache.org/jira/browse/CB-4899) [BlackBerry10] Fix resolve directories
+* [CB-5602](https://issues.apache.org/jira/browse/CB-5602) Windows8. Fix File Api mobile spec tests
+* Android: Better support for content urls and cross-filesystem copy/move ops
+* [CB-5699](https://issues.apache.org/jira/browse/CB-5699) [BlackBerry10] Update resolveLocalFileSystemURI implementation
+* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Update license comment formatting of doc/index.md
+* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc.index.md for File plugin.
+* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs
+* [CB-5403](https://issues.apache.org/jira/browse/CB-5403): Backwards-compatibility with file:// urls where possible
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Fixes for ContentFilesystem
+* Android: Add method for testing backwards-compatibility of filetransfer plugin
+* iOS: Add method for testing backwards-compatiblity of filetransfer plugin
+* Android: Updates to allow FileTransfer to continue to work
+* Android: Clean up unclosed file objects
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Cleanup
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Add new Android source files to plugin.xml
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move read, write and truncate methods into modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move copy/move methods into FS modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move getParent into FS modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move getmetadata methods into FS modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move readdir methods into FS modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move remove methods into FS modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move getFile into FS modules
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Start refactoring android code: Modular filesystems, rfs, rlfsurl
+* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Update android JS to use FS urls
+* [CB-5405](https://issues.apache.org/jira/browse/CB-5405): Use URL formatting for Entry.toURL
+* [CB-5532](https://issues.apache.org/jira/browse/CB-5532) Fix
+* Log file path for File exceptions.
+* Partial fix for iOS File compatibility with previous fileTransfer plugin
+* [CB-5532](https://issues.apache.org/jira/browse/CB-5532) WP8. Add binary data support to FileWriter
+* [CB-5531](https://issues.apache.org/jira/browse/CB-5531) WP8. File Api readAsText incorrectly handles position args
+* Added ubuntu platform support
+* Added amazon-fireos platform support
+* [CB-5118](https://issues.apache.org/jira/browse/CB-5118) [BlackBerry10] Add check for undefined error handler
+* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Extend public API for dependent plugins
+* [CB-5403](https://issues.apache.org/jira/browse/CB-5403): Bump File plugin major version
+* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Split iOS file plugin into modules
+* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Factor out filesystem providers in iOS
+* [CB-5408](https://issues.apache.org/jira/browse/CB-5408): Add handler for filesystem:// urls
+* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Update iOS native code to use filesystem URLs internally
+* [CB-5405](https://issues.apache.org/jira/browse/CB-5405): Update JS code to use URLs exclusively
+* [CB-4816](https://issues.apache.org/jira/browse/CB-4816) Fix file creation outside sandbox for BB10
+
+### 0.2.5 (Oct 28, 2013)
+* [CB-5129](https://issues.apache.org/jira/browse/CB-5129): Add a consistent filesystem attribute to FileEntry and DirectoryEntry objects
+* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for file plugin
+* [CB-5015](https://issues.apache.org/jira/browse/CB-5015) [BlackBerry10] Add missing dependency for File.slice
+* [CB-5010](https://issues.apache.org/jira/browse/CB-5010) Incremented plugin version on dev branch.
+
+### 0.2.4 (Oct 9, 2013)
+* [CB-5020](https://issues.apache.org/jira/browse/CB-5020) - File plugin should execute on a separate thread
+* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch.
+* [CB-4504](https://issues.apache.org/jira/browse/CB-4504): Updating FileUtils.java to compensate for Java porting failures in the Android SDK. This fails because Java knows nothing about android_asset not being an actual filesystem
+
+### 0.2.3 (Sept 25, 2013)
+* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version
+* [CB-4903](https://issues.apache.org/jira/browse/CB-4903) File Plugin not loading Windows8
+* [CB-4903](https://issues.apache.org/jira/browse/CB-4903) File Plugin not loading Windows8
+* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming references
+* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.file to org.apache.cordova.file
+* Rename CHANGELOG.md -> RELEASENOTES.md
+* [CB-4771](https://issues.apache.org/jira/browse/CB-4771) Expose TEMPORARY and PERSISTENT constants on window.
+* Fix compiler/lint warnings
+* [CB-4764](https://issues.apache.org/jira/browse/CB-4764) Move DirectoryManager.java into file plugin
+* [CB-4763](https://issues.apache.org/jira/browse/CB-4763) Copy FileHelper.java into the plugin.
+* [CB-2901](https://issues.apache.org/jira/browse/CB-2901) [BlackBerry10] Automatically unsandbox filesystem if path is not in app sandbox
+* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch.
+
+### 0.2.1 (Sept 5, 2013)
+* [CB-4656](https://issues.apache.org/jira/browse/CB-4656) Don't add newlines in data urls within readAsDataUrl.
+* [CB-4514](https://issues.apache.org/jira/browse/CB-4514) Making DirectoryCopy Recursive
+* [iOS] Simplify the code in resolveLocalFileSystemURI
diff --git a/StoneIsland/plugins/cordova-plugin-file/doc/plugins.md b/StoneIsland/plugins/cordova-plugin-file/doc/plugins.md
new file mode 100644
index 00000000..a3329d67
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/doc/plugins.md
@@ -0,0 +1,120 @@
+<!---
+ 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.
+-->
+
+Notes for plugin developers
+===========================
+
+These notes are primarily intended for Android and iOS developers who want to write plugins which interface with the file system using the File plugin.
+
+Working with Cordova file system URLs
+-------------------------------------
+
+Since version 1.0.0, this plugin has used URLs with a `cdvfile` scheme for all communication over the bridge, rather than exposing raw device file system paths to JavaScript.
+
+On the JavaScript side, this means that FileEntry and DirectoryEntry objects have a fullPath attribute which is relative to the root of the HTML file system. If your plugin's JavaScript API accepts a FileEntry or DirectoryEntry object, you should call `.toURL()` on that object before passing it across the bridge to native code.
+
+### Converting cdvfile:// URLs to fileystem paths
+
+Plugins which need to write to the filesystem may want to convert a received file system URL to an actual filesystem location. There are multiple ways of doing this, depending on the native platform.
+
+It is important to remember that not all `cdvfile://` URLs are mappable to real files on the device. Some URLs can refer to assets on device which are not represented by files, or can even refer to remote resources. Because of these possibilities, plugins should always test whether they get a meaningful result back when trying to convert URLs to paths.
+
+#### Android
+
+On Android, the simplest method to convert a `cdvfile://` URL to a filesystem path is to use `org.apache.cordova.CordovaResourceApi`. `CordovaResourceApi` has several methods which can handle `cdvfile://` URLs:
+
+ // webView is a member of the Plugin class
+ CordovaResourceApi resourceApi = webView.getResourceApi();
+
+ // Obtain a file:/// URL representing this file on the device,
+ // or the same URL unchanged if it cannot be mapped to a file
+ Uri fileURL = resourceApi.remapUri(Uri.parse(cdvfileURL));
+
+It is also possible to use the File plugin directly:
+
+ import org.apache.cordova.file.FileUtils;
+ import org.apache.cordova.file.FileSystem;
+ import java.net.MalformedURLException;
+
+ // Get the File plugin from the plugin manager
+ FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File");
+
+ // Given a URL, get a path for it
+ try {
+ String path = filePlugin.filesystemPathForURL(cdvfileURL);
+ } catch (MalformedURLException e) {
+ // The filesystem url wasn't recognized
+ }
+
+To convert from a path to a `cdvfile://` URL:
+
+ import org.apache.cordova.file.LocalFilesystemURL;
+
+ // Get a LocalFilesystemURL object for a device path,
+ // or null if it cannot be represented as a cdvfile URL.
+ LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path);
+ // Get the string representation of the URL object
+ String cdvfileURL = url.toString();
+
+If your plugin creates a file, and you want to return a FileEntry object for it, use the File plugin:
+
+ // Return a JSON structure suitable for returning to JavaScript,
+ // or null if this file is not representable as a cdvfile URL.
+ JSONObject entry = filePlugin.getEntryForFile(file);
+
+#### iOS
+
+Cordova on iOS does not use the same `CordovaResourceApi` concept as Android. On iOS, you should use the File plugin to convert between URLs and filesystem paths.
+
+ // Get a CDVFilesystem URL object from a URL string
+ CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL];
+ // Get a path for the URL object, or nil if it cannot be mapped to a file
+ NSString* path = [filePlugin filesystemPathForURL:url];
+
+
+ // Get a CDVFilesystem URL object for a device path, or
+ // nil if it cannot be represented as a cdvfile URL.
+ CDVFilesystemURL* url = [filePlugin fileSystemURLforLocalPath:path];
+ // Get the string representation of the URL object
+ NSString* cdvfileURL = [url absoluteString];
+
+If your plugin creates a file, and you want to return a FileEntry object for it, use the File plugin:
+
+ // Get a CDVFilesystem URL object for a device path, or
+ // nil if it cannot be represented as a cdvfile URL.
+ CDVFilesystemURL* url = [filePlugin fileSystemURLforLocalPath:path];
+ // Get a structure to return to JavaScript
+ NSDictionary* entry = [filePlugin makeEntryForLocalURL:url]
+
+#### JavaScript
+
+In JavaScript, to get a `cdvfile://` URL from a FileEntry or DirectoryEntry object, simply call `.toURL()` on it:
+
+ var cdvfileURL = entry.toURL();
+
+In plugin response handlers, to convert from a returned FileEntry structure to an actual Entry object, your handler code should import the File plugin and create a new object:
+
+ // create appropriate Entry object
+ var entry;
+ if (entryStruct.isDirectory) {
+ entry = new DirectoryEntry(entryStruct.name, entryStruct.fullPath, new FileSystem(entryStruct.filesystemName));
+ } else {
+ entry = new FileEntry(entryStruct.name, entryStruct.fullPath, new FileSystem(entryStruct.filesystemName));
+ }
+
diff --git a/StoneIsland/plugins/cordova-plugin-file/package.json b/StoneIsland/plugins/cordova-plugin-file/package.json
new file mode 100644
index 00000000..09d26148
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/package.json
@@ -0,0 +1,87 @@
+{
+ "_from": "cordova-plugin-file@>=2.0.0",
+ "_id": "cordova-plugin-file@6.0.2",
+ "_inBundle": false,
+ "_integrity": "sha512-m7cughw327CjONN/qjzsTpSesLaeybksQh420/gRuSXJX5Zt9NfgsSbqqKDon6jnQ9Mm7h7imgyO2uJ34XMBtA==",
+ "_location": "/cordova-plugin-file",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "cordova-plugin-file@>=2.0.0",
+ "name": "cordova-plugin-file",
+ "escapedName": "cordova-plugin-file",
+ "rawSpec": ">=2.0.0",
+ "saveSpec": null,
+ "fetchSpec": ">=2.0.0"
+ },
+ "_requiredBy": [
+ "#DEV:/",
+ "#USER"
+ ],
+ "_resolved": "https://registry.npmjs.org/cordova-plugin-file/-/cordova-plugin-file-6.0.2.tgz",
+ "_shasum": "f3911479f8357e9aacb5576674f8d95b31a1fb20",
+ "_spec": "cordova-plugin-file@>=2.0.0",
+ "_where": "/Users/user/work/stone-island/StoneIsland",
+ "author": {
+ "name": "Apache Software Foundation"
+ },
+ "bugs": {
+ "url": "https://github.com/apache/cordova-plugin-file/issues"
+ },
+ "bundleDependencies": false,
+ "cordova": {
+ "id": "cordova-plugin-file",
+ "platforms": [
+ "android",
+ "browser",
+ "ios",
+ "osx",
+ "windows"
+ ]
+ },
+ "deprecated": false,
+ "description": "Cordova File Plugin",
+ "devDependencies": {
+ "eslint": "^3.19.0",
+ "eslint-config-semistandard": "^11.0.0",
+ "eslint-config-standard": "^10.2.1",
+ "eslint-plugin-import": "^2.3.0",
+ "eslint-plugin-node": "^5.0.0",
+ "eslint-plugin-promise": "^3.5.0",
+ "eslint-plugin-standard": "^3.0.1"
+ },
+ "engines": {
+ "cordovaDependencies": {
+ "5.0.0": {
+ "cordova-android": ">=6.3.0"
+ },
+ "7.0.0": {
+ "cordova": ">100"
+ }
+ }
+ },
+ "homepage": "https://github.com/apache/cordova-plugin-file#readme",
+ "keywords": [
+ "cordova",
+ "file",
+ "ecosystem:cordova",
+ "cordova-android",
+ "cordova-browser",
+ "cordova-ios",
+ "cordova-osx",
+ "cordova-windows"
+ ],
+ "license": "Apache-2.0",
+ "name": "cordova-plugin-file",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/apache/cordova-plugin-file.git"
+ },
+ "scripts": {
+ "eslint": "node node_modules/eslint/bin/eslint www && node node_modules/eslint/bin/eslint src && node node_modules/eslint/bin/eslint tests",
+ "test": "npm run eslint"
+ },
+ "types": "./types/index.d.ts",
+ "version": "6.0.2"
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/plugin.xml b/StoneIsland/plugins/cordova-plugin-file/plugin.xml
new file mode 100644
index 00000000..74543fba
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/plugin.xml
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+xmlns:android="http://schemas.android.com/apk/res/android"
+ id="cordova-plugin-file"
+ version="6.0.2">
+ <name>File</name>
+ <description>Cordova File Plugin</description>
+ <license>Apache 2.0</license>
+ <keywords>cordova,file</keywords>
+ <repo>https://github.com/apache/cordova-plugin-file</repo>
+ <issue>https://github.com/apache/cordova-plugin-file/issues</issue>
+
+ <engines>
+ <engine name="cordova-android" version=">=6.3.0" />
+ </engines>
+
+ <js-module src="www/DirectoryEntry.js" name="DirectoryEntry">
+ <clobbers target="window.DirectoryEntry" />
+ </js-module>
+
+ <js-module src="www/DirectoryReader.js" name="DirectoryReader">
+ <clobbers target="window.DirectoryReader" />
+ </js-module>
+
+ <js-module src="www/Entry.js" name="Entry">
+ <clobbers target="window.Entry" />
+ </js-module>
+
+ <js-module src="www/File.js" name="File">
+ <clobbers target="window.File" />
+ </js-module>
+
+ <js-module src="www/FileEntry.js" name="FileEntry">
+ <clobbers target="window.FileEntry" />
+ </js-module>
+
+ <js-module src="www/FileError.js" name="FileError">
+ <clobbers target="window.FileError" />
+ </js-module>
+
+ <js-module src="www/FileReader.js" name="FileReader">
+ <clobbers target="window.FileReader" />
+ </js-module>
+
+ <js-module src="www/FileSystem.js" name="FileSystem">
+ <clobbers target="window.FileSystem" />
+ </js-module>
+
+ <js-module src="www/FileUploadOptions.js" name="FileUploadOptions">
+ <clobbers target="window.FileUploadOptions" />
+ </js-module>
+
+ <js-module src="www/FileUploadResult.js" name="FileUploadResult">
+ <clobbers target="window.FileUploadResult" />
+ </js-module>
+
+ <js-module src="www/FileWriter.js" name="FileWriter">
+ <clobbers target="window.FileWriter" />
+ </js-module>
+
+ <js-module src="www/Flags.js" name="Flags">
+ <clobbers target="window.Flags" />
+ </js-module>
+
+ <js-module src="www/LocalFileSystem.js" name="LocalFileSystem">
+ <!-- Non-standards way -->
+ <clobbers target="window.LocalFileSystem" />
+ <!-- Standards-compliant way -->
+ <merges target="window" />
+ </js-module>
+
+ <js-module src="www/Metadata.js" name="Metadata">
+ <clobbers target="window.Metadata" />
+ </js-module>
+
+ <js-module src="www/ProgressEvent.js" name="ProgressEvent">
+ <clobbers target="window.ProgressEvent" />
+ </js-module>
+
+ <js-module src="www/fileSystems.js" name="fileSystems" />
+
+ <js-module src="www/requestFileSystem.js" name="requestFileSystem">
+ <clobbers target="window.requestFileSystem" />
+ </js-module>
+
+ <js-module src="www/resolveLocalFileSystemURI.js" name="resolveLocalFileSystemURI">
+ <merges target="window" />
+ </js-module>
+
+ <!-- Required for browserify: we always link module below as there is conditional reference
+ to this module from requestFileSystem and resolveLocalFileSystemURI modules. -->
+ <js-module src="www/browser/isChrome.js" name="isChrome">
+ <runs />
+ </js-module>
+
+ <!-- android -->
+ <platform name="android">
+ <info>
+The Android Persistent storage location now defaults to "Internal". Please check this plugin's README to see if your application needs any changes in its config.xml.
+
+If this is a new application no changes are required.
+
+If this is an update to an existing application that did not specify an "AndroidPersistentFileLocation" you may need to add:
+
+ "&lt;preference name="AndroidPersistentFileLocation" value="Compatibility" /&gt;"
+
+to config.xml in order for the application to find previously stored files.
+ </info>
+ <config-file target="res/xml/config.xml" parent="/*">
+ <feature name="File" >
+ <param name="android-package" value="org.apache.cordova.file.FileUtils"/>
+ <param name="onload" value="true" />
+ </feature>
+ <allow-navigation href="cdvfile:*" />
+ </config-file>
+
+ <config-file target="AndroidManifest.xml" parent="/*">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ </config-file>
+
+ <source-file src="src/android/EncodingException.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/FileExistsException.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/InvalidModificationException.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/NoModificationAllowedException.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/TypeMismatchException.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/FileUtils.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/DirectoryManager.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/LocalFilesystemURL.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/Filesystem.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/LocalFilesystem.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/ContentFilesystem.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/AssetFilesystem.java" target-dir="src/org/apache/cordova/file" />
+ <source-file src="src/android/PendingRequests.java" target-dir="src/org/apache/cordova/file" />
+
+ <!-- android specific file apis -->
+ <js-module src="www/android/FileSystem.js" name="androidFileSystem">
+ <merges target="FileSystem" />
+ </js-module>
+ <js-module src="www/fileSystems-roots.js" name="fileSystems-roots">
+ <runs/>
+ </js-module>
+ <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+ <merges target="cordova" />
+ <runs/>
+ </js-module>
+ </platform>
+
+ <!-- ios -->
+ <platform name="ios">
+ <config-file target="config.xml" parent="/*">
+ <feature name="File">
+ <param name="ios-package" value="CDVFile" />
+ <param name="onload" value="true" />
+ </feature>
+ </config-file>
+ <header-file src="src/ios/CDVFile.h" />
+ <source-file src="src/ios/CDVFile.m" />
+ <header-file src="src/ios/CDVLocalFilesystem.h" />
+ <source-file src="src/ios/CDVLocalFilesystem.m" />
+ <header-file src="src/ios/CDVAssetLibraryFilesystem.h" />
+ <source-file src="src/ios/CDVAssetLibraryFilesystem.m" />
+
+ <!-- ios specific file apis -->
+ <js-module src="www/ios/FileSystem.js" name="iosFileSystem">
+ <merges target="FileSystem" />
+ </js-module>
+
+ <js-module src="www/fileSystems-roots.js" name="fileSystems-roots">
+ <runs/>
+ </js-module>
+
+ <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+ <merges target="cordova" />
+ <runs/>
+ </js-module>
+
+ <framework src="AssetsLibrary.framework" />
+ <framework src="MobileCoreServices.framework" />
+ </platform>
+
+ <!-- osx -->
+ <platform name="osx">
+ <config-file target="config.xml" parent="/*">
+ <feature name="File">
+ <param name="ios-package" value="CDVFile" />
+ <param name="onload" value="true" />
+ </feature>
+ </config-file>
+ <header-file src="src/osx/CDVFile.h" />
+ <source-file src="src/osx/CDVFile.m" />
+ <header-file src="src/osx/CDVLocalFilesystem.h" />
+ <source-file src="src/osx/CDVLocalFilesystem.m" />
+
+ <!-- osx specific file apis -->
+ <js-module src="www/osx/FileSystem.js" name="osxFileSystem">
+ <merges target="FileSystem" />
+ </js-module>
+
+ <js-module src="www/fileSystems-roots.js" name="fileSystems-roots">
+ <runs/>
+ </js-module>
+
+ <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+ <merges target="cordova" />
+ <runs/>
+ </js-module>
+ </platform>
+
+ <!-- windows -->
+ <platform name="windows">
+ <js-module src="src/windows/FileProxy.js" name="FileProxy">
+ <runs />
+ </js-module>
+
+ <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+ <merges target="cordova" />
+ <runs/>
+ </js-module>
+ </platform>
+
+ <platform name="browser">
+ <!-- File for Chrome -->
+ <js-module src="www/browser/Preparing.js" name="Preparing">
+ <runs />
+ </js-module>
+
+ <js-module src="src/browser/FileProxy.js" name="browserFileProxy">
+ <runs />
+ </js-module>
+
+ <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+ <merges target="cordova" />
+ <runs />
+ </js-module>
+
+ <js-module src="www/browser/FileSystem.js" name="firefoxFileSystem">
+ <merges target="window.FileSystem" />
+ </js-module>
+ </platform>
+
+</plugin>
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/AssetFilesystem.java b/StoneIsland/plugins/cordova-plugin-file/src/android/AssetFilesystem.java
new file mode 100644
index 00000000..b035c40e
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/AssetFilesystem.java
@@ -0,0 +1,294 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file;
+
+import android.content.res.AssetManager;
+import android.net.Uri;
+
+import org.apache.cordova.CordovaResourceApi;
+import org.apache.cordova.LOG;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AssetFilesystem extends Filesystem {
+
+ private final AssetManager assetManager;
+
+ // A custom gradle hook creates the cdvasset.manifest file, which speeds up asset listing a tonne.
+ // See: http://stackoverflow.com/questions/16911558/android-assetmanager-list-incredibly-slow
+ private static Object listCacheLock = new Object();
+ private static boolean listCacheFromFile;
+ private static Map<String, String[]> listCache;
+ private static Map<String, Long> lengthCache;
+
+ private static final String LOG_TAG = "AssetFilesystem";
+
+ private void lazyInitCaches() {
+ synchronized (listCacheLock) {
+ if (listCache == null) {
+ ObjectInputStream ois = null;
+ try {
+ ois = new ObjectInputStream(assetManager.open("cdvasset.manifest"));
+ listCache = (Map<String, String[]>) ois.readObject();
+ lengthCache = (Map<String, Long>) ois.readObject();
+ listCacheFromFile = true;
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ // Asset manifest won't exist if the gradle hook isn't set up correctly.
+ } finally {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch (IOException e) {
+ LOG.d(LOG_TAG, e.getLocalizedMessage());
+ }
+ }
+ }
+ if (listCache == null) {
+ LOG.w("AssetFilesystem", "Asset manifest not found. Recursive copies and directory listing will be slow.");
+ listCache = new HashMap<String, String[]>();
+ }
+ }
+ }
+ }
+
+ private String[] listAssets(String assetPath) throws IOException {
+ if (assetPath.startsWith("/")) {
+ assetPath = assetPath.substring(1);
+ }
+ if (assetPath.endsWith("/")) {
+ assetPath = assetPath.substring(0, assetPath.length() - 1);
+ }
+ lazyInitCaches();
+ String[] ret = listCache.get(assetPath);
+ if (ret == null) {
+ if (listCacheFromFile) {
+ ret = new String[0];
+ } else {
+ ret = assetManager.list(assetPath);
+ listCache.put(assetPath, ret);
+ }
+ }
+ return ret;
+ }
+
+ private long getAssetSize(String assetPath) throws FileNotFoundException {
+ if (assetPath.startsWith("/")) {
+ assetPath = assetPath.substring(1);
+ }
+ lazyInitCaches();
+ if (lengthCache != null) {
+ Long ret = lengthCache.get(assetPath);
+ if (ret == null) {
+ throw new FileNotFoundException("Asset not found: " + assetPath);
+ }
+ return ret;
+ }
+ CordovaResourceApi.OpenForReadResult offr = null;
+ try {
+ offr = resourceApi.openForRead(nativeUriForFullPath(assetPath));
+ long length = offr.length;
+ if (length < 0) {
+ // available() doesn't always yield the file size, but for assets it does.
+ length = offr.inputStream.available();
+ }
+ return length;
+ } catch (IOException e) {
+ FileNotFoundException fnfe = new FileNotFoundException("File not found: " + assetPath);
+ fnfe.initCause(e);
+ throw fnfe;
+ } finally {
+ if (offr != null) {
+ try {
+ offr.inputStream.close();
+ } catch (IOException e) {
+ LOG.d(LOG_TAG, e.getLocalizedMessage());
+ }
+ }
+ }
+ }
+
+ public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi) {
+ super(Uri.parse("file:///android_asset/"), "assets", resourceApi);
+ this.assetManager = assetManager;
+ }
+
+ @Override
+ public Uri toNativeUri(LocalFilesystemURL inputURL) {
+ return nativeUriForFullPath(inputURL.path);
+ }
+
+ @Override
+ public LocalFilesystemURL toLocalUri(Uri inputURL) {
+ if (!"file".equals(inputURL.getScheme())) {
+ return null;
+ }
+ File f = new File(inputURL.getPath());
+ // Removes and duplicate /s (e.g. file:///a//b/c)
+ Uri resolvedUri = Uri.fromFile(f);
+ String rootUriNoTrailingSlash = rootUri.getEncodedPath();
+ rootUriNoTrailingSlash = rootUriNoTrailingSlash.substring(0, rootUriNoTrailingSlash.length() - 1);
+ if (!resolvedUri.getEncodedPath().startsWith(rootUriNoTrailingSlash)) {
+ return null;
+ }
+ String subPath = resolvedUri.getEncodedPath().substring(rootUriNoTrailingSlash.length());
+ // Strip leading slash
+ if (!subPath.isEmpty()) {
+ subPath = subPath.substring(1);
+ }
+ Uri.Builder b = new Uri.Builder()
+ .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
+ .authority("localhost")
+ .path(name);
+ if (!subPath.isEmpty()) {
+ b.appendEncodedPath(subPath);
+ }
+ if (isDirectory(subPath) || inputURL.getPath().endsWith("/")) {
+ // Add trailing / for directories.
+ b.appendEncodedPath("");
+ }
+ return LocalFilesystemURL.parse(b.build());
+ }
+
+ private boolean isDirectory(String assetPath) {
+ try {
+ return listAssets(assetPath).length != 0;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ String pathNoSlashes = inputURL.path.substring(1);
+ if (pathNoSlashes.endsWith("/")) {
+ pathNoSlashes = pathNoSlashes.substring(0, pathNoSlashes.length() - 1);
+ }
+
+ String[] files;
+ try {
+ files = listAssets(pathNoSlashes);
+ } catch (IOException e) {
+ FileNotFoundException fnfe = new FileNotFoundException();
+ fnfe.initCause(e);
+ throw fnfe;
+ }
+
+ LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length];
+ for (int i = 0; i < files.length; ++i) {
+ entries[i] = localUrlforFullPath(new File(inputURL.path, files[i]).getPath());
+ }
+ return entries;
+ }
+
+ @Override
+ public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
+ String path, JSONObject options, boolean directory)
+ throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ if (options != null && options.optBoolean("create")) {
+ throw new UnsupportedOperationException("Assets are read-only");
+ }
+
+ // Check whether the supplied path is absolute or relative
+ if (directory && !path.endsWith("/")) {
+ path += "/";
+ }
+
+ LocalFilesystemURL requestedURL;
+ if (path.startsWith("/")) {
+ requestedURL = localUrlforFullPath(normalizePath(path));
+ } else {
+ requestedURL = localUrlforFullPath(normalizePath(inputURL.path + "/" + path));
+ }
+
+ // Throws a FileNotFoundException if it doesn't exist.
+ getFileMetadataForLocalURL(requestedURL);
+
+ boolean isDir = isDirectory(requestedURL.path);
+ if (directory && !isDir) {
+ throw new TypeMismatchException("path doesn't exist or is file");
+ } else if (!directory && isDir) {
+ throw new TypeMismatchException("path doesn't exist or is directory");
+ }
+
+ // Return the directory
+ return makeEntryForURL(requestedURL);
+ }
+
+ @Override
+ public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ JSONObject metadata = new JSONObject();
+ long size = inputURL.isDirectory ? 0 : getAssetSize(inputURL.path);
+ try {
+ metadata.put("size", size);
+ metadata.put("type", inputURL.isDirectory ? "text/directory" : resourceApi.getMimeType(toNativeUri(inputURL)));
+ metadata.put("name", new File(inputURL.path).getName());
+ metadata.put("fullPath", inputURL.path);
+ metadata.put("lastModifiedDate", 0);
+ } catch (JSONException e) {
+ return null;
+ }
+ return metadata;
+ }
+
+ @Override
+ public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
+ return false;
+ }
+
+ @Override
+ long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws NoModificationAllowedException, IOException {
+ throw new NoModificationAllowedException("Assets are read-only");
+ }
+
+ @Override
+ long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException, NoModificationAllowedException {
+ throw new NoModificationAllowedException("Assets are read-only");
+ }
+
+ @Override
+ String filesystemPathForURL(LocalFilesystemURL url) {
+ return new File(rootUri.getPath(), url.path).toString();
+ }
+
+ @Override
+ LocalFilesystemURL URLforFilesystemPath(String path) {
+ return null;
+ }
+
+ @Override
+ boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException {
+ throw new NoModificationAllowedException("Assets are read-only");
+ }
+
+ @Override
+ boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws NoModificationAllowedException {
+ throw new NoModificationAllowedException("Assets are read-only");
+ }
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/ContentFilesystem.java b/StoneIsland/plugins/cordova-plugin-file/src/android/ContentFilesystem.java
new file mode 100644
index 00000000..6b983c08
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/ContentFilesystem.java
@@ -0,0 +1,223 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+import android.provider.OpenableColumns;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import org.apache.cordova.CordovaResourceApi;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class ContentFilesystem extends Filesystem {
+
+ private final Context context;
+
+ public ContentFilesystem(Context context, CordovaResourceApi resourceApi) {
+ super(Uri.parse("content://"), "content", resourceApi);
+ this.context = context;
+ }
+
+ @Override
+ public Uri toNativeUri(LocalFilesystemURL inputURL) {
+ String authorityAndPath = inputURL.uri.getEncodedPath().substring(this.name.length() + 2);
+ if (authorityAndPath.length() < 2) {
+ return null;
+ }
+ String ret = "content://" + authorityAndPath;
+ String query = inputURL.uri.getEncodedQuery();
+ if (query != null) {
+ ret += '?' + query;
+ }
+ String frag = inputURL.uri.getEncodedFragment();
+ if (frag != null) {
+ ret += '#' + frag;
+ }
+ return Uri.parse(ret);
+ }
+
+ @Override
+ public LocalFilesystemURL toLocalUri(Uri inputURL) {
+ if (!"content".equals(inputURL.getScheme())) {
+ return null;
+ }
+ String subPath = inputURL.getEncodedPath();
+ if (subPath.length() > 0) {
+ subPath = subPath.substring(1);
+ }
+ Uri.Builder b = new Uri.Builder()
+ .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
+ .authority("localhost")
+ .path(name)
+ .appendPath(inputURL.getAuthority());
+ if (subPath.length() > 0) {
+ b.appendEncodedPath(subPath);
+ }
+ Uri localUri = b.encodedQuery(inputURL.getEncodedQuery())
+ .encodedFragment(inputURL.getEncodedFragment())
+ .build();
+ return LocalFilesystemURL.parse(localUri);
+ }
+
+ @Override
+ public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
+ String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
+ throw new UnsupportedOperationException("getFile() not supported for content:. Use resolveLocalFileSystemURL instead.");
+ }
+
+ @Override
+ public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL)
+ throws NoModificationAllowedException {
+ Uri contentUri = toNativeUri(inputURL);
+ try {
+ context.getContentResolver().delete(contentUri, null, null);
+ } catch (UnsupportedOperationException t) {
+ // Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
+ // The ContentResolver applies only when the file was registered in the
+ // first case, which is generally only the case with images.
+ NoModificationAllowedException nmae = new NoModificationAllowedException("Deleting not supported for content uri: " + contentUri);
+ nmae.initCause(t);
+ throw nmae;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL)
+ throws NoModificationAllowedException {
+ throw new NoModificationAllowedException("Cannot remove content url");
+ }
+
+ @Override
+ public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ throw new UnsupportedOperationException("readEntriesAtLocalURL() not supported for content:. Use resolveLocalFileSystemURL instead.");
+ }
+
+ @Override
+ public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ long size = -1;
+ long lastModified = 0;
+ Uri nativeUri = toNativeUri(inputURL);
+ String mimeType = resourceApi.getMimeType(nativeUri);
+ Cursor cursor = openCursorForURL(nativeUri);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ Long sizeForCursor = resourceSizeForCursor(cursor);
+ if (sizeForCursor != null) {
+ size = sizeForCursor.longValue();
+ }
+ Long modified = lastModifiedDateForCursor(cursor);
+ if (modified != null)
+ lastModified = modified.longValue();
+ } else {
+ // Some content providers don't support cursors at all!
+ CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(nativeUri);
+ size = offr.length;
+ }
+ } catch (IOException e) {
+ FileNotFoundException fnfe = new FileNotFoundException();
+ fnfe.initCause(e);
+ throw fnfe;
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+
+ JSONObject metadata = new JSONObject();
+ try {
+ metadata.put("size", size);
+ metadata.put("type", mimeType);
+ metadata.put("name", name);
+ metadata.put("fullPath", inputURL.path);
+ metadata.put("lastModifiedDate", lastModified);
+ } catch (JSONException e) {
+ return null;
+ }
+ return metadata;
+ }
+
+ @Override
+ public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
+ int offset, boolean isBinary) throws NoModificationAllowedException {
+ throw new NoModificationAllowedException("Couldn't write to file given its content URI");
+ }
+ @Override
+ public long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
+ throws NoModificationAllowedException {
+ throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
+ }
+
+ protected Cursor openCursorForURL(Uri nativeUri) {
+ ContentResolver contentResolver = context.getContentResolver();
+ try {
+ return contentResolver.query(nativeUri, null, null, null, null);
+ } catch (UnsupportedOperationException e) {
+ return null;
+ }
+ }
+
+ private Long resourceSizeForCursor(Cursor cursor) {
+ int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
+ if (columnIndex != -1) {
+ String sizeStr = cursor.getString(columnIndex);
+ if (sizeStr != null) {
+ return Long.parseLong(sizeStr);
+ }
+ }
+ return null;
+ }
+
+ protected Long lastModifiedDateForCursor(Cursor cursor) {
+ int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
+ if (columnIndex == -1) {
+ columnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED);
+ }
+ if (columnIndex != -1) {
+ String dateStr = cursor.getString(columnIndex);
+ if (dateStr != null) {
+ return Long.parseLong(dateStr);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String filesystemPathForURL(LocalFilesystemURL url) {
+ File f = resourceApi.mapUriToFile(toNativeUri(url));
+ return f == null ? null : f.getAbsolutePath();
+ }
+
+ @Override
+ public LocalFilesystemURL URLforFilesystemPath(String path) {
+ // Returns null as we don't support reverse mapping back to content:// URLs
+ return null;
+ }
+
+ @Override
+ public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
+ return true;
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/DirectoryManager.java b/StoneIsland/plugins/cordova-plugin-file/src/android/DirectoryManager.java
new file mode 100644
index 00000000..07af5ea2
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/DirectoryManager.java
@@ -0,0 +1,134 @@
+/*
+ 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.
+*/
+package org.apache.cordova.file;
+
+import android.os.Environment;
+import android.os.StatFs;
+
+import java.io.File;
+
+/**
+ * This class provides file directory utilities.
+ * All file operations are performed on the SD card.
+ *
+ * It is used by the FileUtils class.
+ */
+public class DirectoryManager {
+
+ @SuppressWarnings("unused")
+ private static final String LOG_TAG = "DirectoryManager";
+
+ /**
+ * Determine if a file or directory exists.
+ * @param name The name of the file to check.
+ * @return T=exists, F=not found
+ */
+ public static boolean testFileExists(String name) {
+ boolean status;
+
+ // If SD card exists
+ if ((testSaveLocationExists()) && (!name.equals(""))) {
+ File path = Environment.getExternalStorageDirectory();
+ File newPath = constructFilePaths(path.toString(), name);
+ status = newPath.exists();
+ }
+ // If no SD card
+ else {
+ status = false;
+ }
+ return status;
+ }
+
+ /**
+ * Get the free space in external storage
+ *
+ * @return Size in KB or -1 if not available
+ */
+ public static long getFreeExternalStorageSpace() {
+ String status = Environment.getExternalStorageState();
+ long freeSpaceInBytes = 0;
+
+ // Check if external storage exists
+ if (status.equals(Environment.MEDIA_MOUNTED)) {
+ freeSpaceInBytes = getFreeSpaceInBytes(Environment.getExternalStorageDirectory().getPath());
+ } else {
+ // If no external storage then return -1
+ return -1;
+ }
+
+ return freeSpaceInBytes / 1024;
+ }
+
+ /**
+ * Given a path return the number of free bytes in the filesystem containing the path.
+ *
+ * @param path to the file system
+ * @return free space in bytes
+ */
+ public static long getFreeSpaceInBytes(String path) {
+ try {
+ StatFs stat = new StatFs(path);
+ long blockSize = stat.getBlockSize();
+ long availableBlocks = stat.getAvailableBlocks();
+ return availableBlocks * blockSize;
+ } catch (IllegalArgumentException e) {
+ // The path was invalid. Just return 0 free bytes.
+ return 0;
+ }
+ }
+
+ /**
+ * Determine if SD card exists.
+ *
+ * @return T=exists, F=not found
+ */
+ public static boolean testSaveLocationExists() {
+ String sDCardStatus = Environment.getExternalStorageState();
+ boolean status;
+
+ // If SD card is mounted
+ if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) {
+ status = true;
+ }
+
+ // If no SD card
+ else {
+ status = false;
+ }
+ return status;
+ }
+
+ /**
+ * Create a new file object from two file paths.
+ *
+ * @param file1 Base file path
+ * @param file2 Remaining file path
+ * @return File object
+ */
+ private static File constructFilePaths (String file1, String file2) {
+ File newPath;
+ if (file2.startsWith(file1)) {
+ newPath = new File(file2);
+ }
+ else {
+ newPath = new File(file1 + "/" + file2);
+ }
+ return newPath;
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/EncodingException.java b/StoneIsland/plugins/cordova-plugin-file/src/android/EncodingException.java
new file mode 100644
index 00000000..e9e1653b
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/EncodingException.java
@@ -0,0 +1,29 @@
+/*
+ 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.
+*/
+
+package org.apache.cordova.file;
+
+@SuppressWarnings("serial")
+public class EncodingException extends Exception {
+
+ public EncodingException(String message) {
+ super(message);
+ }
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/FileExistsException.java b/StoneIsland/plugins/cordova-plugin-file/src/android/FileExistsException.java
new file mode 100644
index 00000000..5c4d83dc
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/FileExistsException.java
@@ -0,0 +1,29 @@
+/*
+ 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.
+*/
+
+package org.apache.cordova.file;
+
+@SuppressWarnings("serial")
+public class FileExistsException extends Exception {
+
+ public FileExistsException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/FileUtils.java b/StoneIsland/plugins/cordova-plugin-file/src/android/FileUtils.java
new file mode 100644
index 00000000..1d6e61fa
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/FileUtils.java
@@ -0,0 +1,1225 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Base64;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.LOG;
+import org.apache.cordova.PermissionHelper;
+import org.apache.cordova.PluginResult;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * This class provides file and directory services to JavaScript.
+ */
+public class FileUtils extends CordovaPlugin {
+ private static final String LOG_TAG = "FileUtils";
+
+ public static int NOT_FOUND_ERR = 1;
+ public static int SECURITY_ERR = 2;
+ public static int ABORT_ERR = 3;
+
+ public static int NOT_READABLE_ERR = 4;
+ public static int ENCODING_ERR = 5;
+ public static int NO_MODIFICATION_ALLOWED_ERR = 6;
+ public static int INVALID_STATE_ERR = 7;
+ public static int SYNTAX_ERR = 8;
+ public static int INVALID_MODIFICATION_ERR = 9;
+ public static int QUOTA_EXCEEDED_ERR = 10;
+ public static int TYPE_MISMATCH_ERR = 11;
+ public static int PATH_EXISTS_ERR = 12;
+
+ /*
+ * Permission callback codes
+ */
+
+ public static final int ACTION_GET_FILE = 0;
+ public static final int ACTION_WRITE = 1;
+ public static final int ACTION_GET_DIRECTORY = 2;
+
+ public static final int WRITE = 3;
+ public static final int READ = 4;
+
+ public static int UNKNOWN_ERR = 1000;
+
+ private boolean configured = false;
+
+ private PendingRequests pendingRequests;
+
+
+
+ /*
+ * We need both read and write when accessing the storage, I think.
+ */
+
+ private String [] permissions = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE };
+
+ // This field exists only to support getEntry, below, which has been deprecated
+ private static FileUtils filePlugin;
+
+ private interface FileOp {
+ void run(JSONArray args) throws Exception;
+ }
+
+ private ArrayList<Filesystem> filesystems;
+
+ public void registerFilesystem(Filesystem fs) {
+ if (fs != null && filesystemForName(fs.name)== null) {
+ this.filesystems.add(fs);
+ }
+ }
+
+ private Filesystem filesystemForName(String name) {
+ for (Filesystem fs:filesystems) {
+ if (fs != null && fs.name != null && fs.name.equals(name)) {
+ return fs;
+ }
+ }
+ return null;
+ }
+
+ protected String[] getExtraFileSystemsPreference(Activity activity) {
+ String fileSystemsStr = preferences.getString("androidextrafilesystems", "files,files-external,documents,sdcard,cache,cache-external,assets,root");
+ return fileSystemsStr.split(",");
+ }
+
+ protected void registerExtraFileSystems(String[] filesystems, HashMap<String, String> availableFileSystems) {
+ HashSet<String> installedFileSystems = new HashSet<String>();
+
+ /* Register filesystems in order */
+ for (String fsName : filesystems) {
+ if (!installedFileSystems.contains(fsName)) {
+ String fsRoot = availableFileSystems.get(fsName);
+ if (fsRoot != null) {
+ File newRoot = new File(fsRoot);
+ if (newRoot.mkdirs() || newRoot.isDirectory()) {
+ registerFilesystem(new LocalFilesystem(fsName, webView.getContext(), webView.getResourceApi(), newRoot));
+ installedFileSystems.add(fsName);
+ } else {
+ LOG.d(LOG_TAG, "Unable to create root dir for filesystem \"" + fsName + "\", skipping");
+ }
+ } else {
+ LOG.d(LOG_TAG, "Unrecognized extra filesystem identifier: " + fsName);
+ }
+ }
+ }
+ }
+
+ protected HashMap<String, String> getAvailableFileSystems(Activity activity) {
+ Context context = activity.getApplicationContext();
+ HashMap<String, String> availableFileSystems = new HashMap<String,String>();
+
+ availableFileSystems.put("files", context.getFilesDir().getAbsolutePath());
+ availableFileSystems.put("documents", new File(context.getFilesDir(), "Documents").getAbsolutePath());
+ availableFileSystems.put("cache", context.getCacheDir().getAbsolutePath());
+ availableFileSystems.put("root", "/");
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ try {
+ availableFileSystems.put("files-external", context.getExternalFilesDir(null).getAbsolutePath());
+ availableFileSystems.put("sdcard", Environment.getExternalStorageDirectory().getAbsolutePath());
+ availableFileSystems.put("cache-external", context.getExternalCacheDir().getAbsolutePath());
+ }
+ catch(NullPointerException e) {
+ LOG.d(LOG_TAG, "External storage unavailable, check to see if USB Mass Storage Mode is on");
+ }
+ }
+
+ return availableFileSystems;
+ }
+
+ @Override
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+ super.initialize(cordova, webView);
+ this.filesystems = new ArrayList<Filesystem>();
+ this.pendingRequests = new PendingRequests();
+
+ String tempRoot = null;
+ String persistentRoot = null;
+
+ Activity activity = cordova.getActivity();
+ String packageName = activity.getPackageName();
+
+ String location = preferences.getString("androidpersistentfilelocation", "internal");
+
+ tempRoot = activity.getCacheDir().getAbsolutePath();
+ if ("internal".equalsIgnoreCase(location)) {
+ persistentRoot = activity.getFilesDir().getAbsolutePath() + "/files/";
+ this.configured = true;
+ } else if ("compatibility".equalsIgnoreCase(location)) {
+ /*
+ * Fall-back to compatibility mode -- this is the logic implemented in
+ * earlier versions of this plugin, and should be maintained here so
+ * that apps which were originally deployed with older versions of the
+ * plugin can continue to provide access to files stored under those
+ * versions.
+ */
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ persistentRoot = Environment.getExternalStorageDirectory().getAbsolutePath();
+ tempRoot = Environment.getExternalStorageDirectory().getAbsolutePath() +
+ "/Android/data/" + packageName + "/cache/";
+ } else {
+ persistentRoot = "/data/data/" + packageName;
+ }
+ this.configured = true;
+ }
+
+ if (this.configured) {
+ // Create the directories if they don't exist.
+ File tmpRootFile = new File(tempRoot);
+ File persistentRootFile = new File(persistentRoot);
+ tmpRootFile.mkdirs();
+ persistentRootFile.mkdirs();
+
+ // Register initial filesystems
+ // Note: The temporary and persistent filesystems need to be the first two
+ // registered, so that they will match window.TEMPORARY and window.PERSISTENT,
+ // per spec.
+ this.registerFilesystem(new LocalFilesystem("temporary", webView.getContext(), webView.getResourceApi(), tmpRootFile));
+ this.registerFilesystem(new LocalFilesystem("persistent", webView.getContext(), webView.getResourceApi(), persistentRootFile));
+ this.registerFilesystem(new ContentFilesystem(webView.getContext(), webView.getResourceApi()));
+ this.registerFilesystem(new AssetFilesystem(webView.getContext().getAssets(), webView.getResourceApi()));
+
+ registerExtraFileSystems(getExtraFileSystemsPreference(activity), getAvailableFileSystems(activity));
+
+ // Initialize static plugin reference for deprecated getEntry method
+ if (filePlugin == null) {
+ FileUtils.filePlugin = this;
+ }
+ } else {
+ LOG.e(LOG_TAG, "File plugin configuration error: Please set AndroidPersistentFileLocation in config.xml to one of \"internal\" (for new applications) or \"compatibility\" (for compatibility with previous versions)");
+ activity.finish();
+ }
+ }
+
+ public static FileUtils getFilePlugin() {
+ return filePlugin;
+ }
+
+ private Filesystem filesystemForURL(LocalFilesystemURL localURL) {
+ if (localURL == null) return null;
+ return filesystemForName(localURL.fsName);
+ }
+
+ @Override
+ public Uri remapUri(Uri uri) {
+ // Remap only cdvfile: URLs (not content:).
+ if (!LocalFilesystemURL.FILESYSTEM_PROTOCOL.equals(uri.getScheme())) {
+ return null;
+ }
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ return null;
+ }
+ String path = fs.filesystemPathForURL(inputURL);
+ if (path != null) {
+ return Uri.parse("file://" + fs.filesystemPathForURL(inputURL));
+ }
+ return null;
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ public boolean execute(String action, final String rawArgs, final CallbackContext callbackContext) {
+ if (!configured) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "File plugin is not configured. Please see the README.md file for details on how to update config.xml"));
+ return true;
+ }
+ if (action.equals("testSaveLocationExists")) {
+ threadhelper(new FileOp() {
+ public void run(JSONArray args) {
+ boolean b = DirectoryManager.testSaveLocationExists();
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("getFreeDiskSpace")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) {
+ // The getFreeDiskSpace plugin API is not documented, but some apps call it anyway via exec().
+ // For compatibility it always returns free space in the primary external storage, and
+ // does NOT fallback to internal store if external storage is unavailable.
+ long l = DirectoryManager.getFreeExternalStorageSpace();
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l));
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("testFileExists")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException {
+ String fname=args.getString(0);
+ boolean b = DirectoryManager.testFileExists(fname);
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("testDirectoryExists")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException {
+ String fname=args.getString(0);
+ boolean b = DirectoryManager.testFileExists(fname);
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("readAsText")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, MalformedURLException {
+ String encoding = args.getString(1);
+ int start = args.getInt(2);
+ int end = args.getInt(3);
+ String fname=args.getString(0);
+ readFileAs(fname, start, end, callbackContext, encoding, PluginResult.MESSAGE_TYPE_STRING);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("readAsDataURL")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, MalformedURLException {
+ int start = args.getInt(1);
+ int end = args.getInt(2);
+ String fname=args.getString(0);
+ readFileAs(fname, start, end, callbackContext, null, -1);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("readAsArrayBuffer")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, MalformedURLException {
+ int start = args.getInt(1);
+ int end = args.getInt(2);
+ String fname=args.getString(0);
+ readFileAs(fname, start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_ARRAYBUFFER);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("readAsBinaryString")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, MalformedURLException {
+ int start = args.getInt(1);
+ int end = args.getInt(2);
+ String fname=args.getString(0);
+ readFileAs(fname, start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_BINARYSTRING);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("write")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException {
+ String fname=args.getString(0);
+ String nativeURL = resolveLocalFileSystemURI(fname).getString("nativeURL");
+ String data=args.getString(1);
+ int offset=args.getInt(2);
+ Boolean isBinary=args.getBoolean(3);
+
+ if(needPermission(nativeURL, WRITE)) {
+ getWritePermission(rawArgs, ACTION_WRITE, callbackContext);
+ }
+ else {
+ long fileSize = write(fname, data, offset, isBinary);
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
+ }
+
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("truncate")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException {
+ String fname=args.getString(0);
+ int offset=args.getInt(1);
+ long fileSize = truncateFile(fname, offset);
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("requestAllFileSystems")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws IOException, JSONException {
+ callbackContext.success(requestAllFileSystems());
+ }
+ }, rawArgs, callbackContext);
+ } else if (action.equals("requestAllPaths")) {
+ cordova.getThreadPool().execute(
+ new Runnable() {
+ public void run() {
+ try {
+ callbackContext.success(requestAllPaths());
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ );
+ } else if (action.equals("requestFileSystem")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException {
+ int fstype = args.getInt(0);
+ long requiredSize = args.optLong(1);
+ requestFileSystem(fstype, requiredSize, callbackContext);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("resolveLocalFileSystemURI")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws IOException, JSONException {
+ String fname=args.getString(0);
+ JSONObject obj = resolveLocalFileSystemURI(fname);
+ callbackContext.success(obj);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("getFileMetadata")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException {
+ String fname=args.getString(0);
+ JSONObject obj = getFileMetadata(fname);
+ callbackContext.success(obj);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("getParent")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, IOException {
+ String fname=args.getString(0);
+ JSONObject obj = getParent(fname);
+ callbackContext.success(obj);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("getDirectory")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ String dirname = args.getString(0);
+ String path = args.getString(1);
+ String nativeURL = resolveLocalFileSystemURI(dirname).getString("nativeURL");
+ boolean containsCreate = (args.isNull(2)) ? false : args.getJSONObject(2).optBoolean("create", false);
+
+ if(containsCreate && needPermission(nativeURL, WRITE)) {
+ getWritePermission(rawArgs, ACTION_GET_DIRECTORY, callbackContext);
+ }
+ else if(!containsCreate && needPermission(nativeURL, READ)) {
+ getReadPermission(rawArgs, ACTION_GET_DIRECTORY, callbackContext);
+ }
+ else {
+ JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true);
+ callbackContext.success(obj);
+ }
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("getFile")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ String dirname = args.getString(0);
+ String path = args.getString(1);
+ String nativeURL = resolveLocalFileSystemURI(dirname).getString("nativeURL");
+ boolean containsCreate = (args.isNull(2)) ? false : args.getJSONObject(2).optBoolean("create", false);
+
+ if(containsCreate && needPermission(nativeURL, WRITE)) {
+ getWritePermission(rawArgs, ACTION_GET_FILE, callbackContext);
+ }
+ else if(!containsCreate && needPermission(nativeURL, READ)) {
+ getReadPermission(rawArgs, ACTION_GET_FILE, callbackContext);
+ }
+ else {
+ JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false);
+ callbackContext.success(obj);
+ }
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("remove")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, NoModificationAllowedException, InvalidModificationException, MalformedURLException {
+ String fname=args.getString(0);
+ boolean success = remove(fname);
+ if (success) {
+ callbackContext.success();
+ } else {
+ callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("removeRecursively")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, FileExistsException, MalformedURLException, NoModificationAllowedException {
+ String fname=args.getString(0);
+ boolean success = removeRecursively(fname);
+ if (success) {
+ callbackContext.success();
+ } else {
+ callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("moveTo")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException {
+ String fname=args.getString(0);
+ String newParent=args.getString(1);
+ String newName=args.getString(2);
+ JSONObject entry = transferTo(fname, newParent, newName, true);
+ callbackContext.success(entry);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("copyTo")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException {
+ String fname=args.getString(0);
+ String newParent=args.getString(1);
+ String newName=args.getString(2);
+ JSONObject entry = transferTo(fname, newParent, newName, false);
+ callbackContext.success(entry);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("readEntries")) {
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException {
+ String fname=args.getString(0);
+ JSONArray entries = readEntries(fname);
+ callbackContext.success(entries);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else if (action.equals("_getLocalFilesystemPath")) {
+ // Internal method for testing: Get the on-disk location of a local filesystem url.
+ // [Currently used for testing file-transfer]
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException {
+ String localURLstr = args.getString(0);
+ String fname = filesystemPathForURL(localURLstr);
+ callbackContext.success(fname);
+ }
+ }, rawArgs, callbackContext);
+ }
+ else {
+ return false;
+ }
+ return true;
+ }
+
+ private void getReadPermission(String rawArgs, int action, CallbackContext callbackContext) {
+ int requestCode = pendingRequests.createRequest(rawArgs, action, callbackContext);
+ PermissionHelper.requestPermission(this, requestCode, Manifest.permission.READ_EXTERNAL_STORAGE);
+ }
+
+ private void getWritePermission(String rawArgs, int action, CallbackContext callbackContext) {
+ int requestCode = pendingRequests.createRequest(rawArgs, action, callbackContext);
+ PermissionHelper.requestPermission(this, requestCode, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ }
+
+ private boolean hasReadPermission() {
+ return PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
+ }
+
+ private boolean hasWritePermission() {
+ return PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ }
+
+ private boolean needPermission(String nativeURL, int permissionType) throws JSONException {
+ JSONObject j = requestAllPaths();
+ ArrayList<String> allowedStorageDirectories = new ArrayList<String>();
+ allowedStorageDirectories.add(j.getString("applicationDirectory"));
+ allowedStorageDirectories.add(j.getString("applicationStorageDirectory"));
+ if(j.has("externalApplicationStorageDirectory")) {
+ allowedStorageDirectories.add(j.getString("externalApplicationStorageDirectory"));
+ }
+
+ if(permissionType == READ && hasReadPermission()) {
+ return false;
+ }
+ else if(permissionType == WRITE && hasWritePermission()) {
+ return false;
+ }
+
+ // Permission required if the native url lies outside the allowed storage directories
+ for(String directory : allowedStorageDirectories) {
+ if(nativeURL.startsWith(directory)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ public LocalFilesystemURL resolveNativeUri(Uri nativeUri) {
+ LocalFilesystemURL localURL = null;
+
+ // Try all installed filesystems. Return the best matching URL
+ // (determined by the shortest resulting URL)
+ for (Filesystem fs : filesystems) {
+ LocalFilesystemURL url = fs.toLocalUri(nativeUri);
+ if (url != null) {
+ // A shorter fullPath implies that the filesystem is a better
+ // match for the local path than the previous best.
+ if (localURL == null || (url.uri.toString().length() < localURL.toString().length())) {
+ localURL = url;
+ }
+ }
+ }
+ return localURL;
+ }
+
+ /*
+ * These two native-only methods can be used by other plugins to translate between
+ * device file system paths and URLs. By design, there is no direct JavaScript
+ * interface to these methods.
+ */
+
+ public String filesystemPathForURL(String localURLstr) throws MalformedURLException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(localURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.filesystemPathForURL(inputURL);
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+ public LocalFilesystemURL filesystemURLforLocalPath(String localPath) {
+ LocalFilesystemURL localURL = null;
+ int shortestFullPath = 0;
+
+ // Try all installed filesystems. Return the best matching URL
+ // (determined by the shortest resulting URL)
+ for (Filesystem fs: filesystems) {
+ LocalFilesystemURL url = fs.URLforFilesystemPath(localPath);
+ if (url != null) {
+ // A shorter fullPath implies that the filesystem is a better
+ // match for the local path than the previous best.
+ if (localURL == null || (url.path.length() < shortestFullPath)) {
+ localURL = url;
+ shortestFullPath = url.path.length();
+ }
+ }
+ }
+ return localURL;
+ }
+
+
+ /* helper to execute functions async and handle the result codes
+ *
+ */
+ private void threadhelper(final FileOp f, final String rawArgs, final CallbackContext callbackContext){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ JSONArray args = new JSONArray(rawArgs);
+ f.run(args);
+ } catch ( Exception e) {
+ if( e instanceof EncodingException){
+ callbackContext.error(FileUtils.ENCODING_ERR);
+ } else if(e instanceof FileNotFoundException) {
+ callbackContext.error(FileUtils.NOT_FOUND_ERR);
+ } else if(e instanceof FileExistsException) {
+ callbackContext.error(FileUtils.PATH_EXISTS_ERR);
+ } else if(e instanceof NoModificationAllowedException ) {
+ callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
+ } else if(e instanceof InvalidModificationException ) {
+ callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR);
+ } else if(e instanceof MalformedURLException ) {
+ callbackContext.error(FileUtils.ENCODING_ERR);
+ } else if(e instanceof IOException ) {
+ callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR);
+ } else if(e instanceof EncodingException ) {
+ callbackContext.error(FileUtils.ENCODING_ERR);
+ } else if(e instanceof TypeMismatchException ) {
+ callbackContext.error(FileUtils.TYPE_MISMATCH_ERR);
+ } else if(e instanceof JSONException ) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ } else if (e instanceof SecurityException) {
+ callbackContext.error(FileUtils.SECURITY_ERR);
+ } else {
+ e.printStackTrace();
+ callbackContext.error(FileUtils.UNKNOWN_ERR);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Allows the user to look up the Entry for a file or directory referred to by a local URI.
+ *
+ * @param uriString of the file/directory to look up
+ * @return a JSONObject representing a Entry from the filesystem
+ * @throws MalformedURLException if the url is not valid
+ * @throws FileNotFoundException if the file does not exist
+ * @throws IOException if the user can't read the file
+ * @throws JSONException
+ */
+ private JSONObject resolveLocalFileSystemURI(String uriString) throws IOException, JSONException {
+ if (uriString == null) {
+ throw new MalformedURLException("Unrecognized filesystem URL");
+ }
+ Uri uri = Uri.parse(uriString);
+ boolean isNativeUri = false;
+
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri);
+ if (inputURL == null) {
+ /* Check for file://, content:// urls */
+ inputURL = resolveNativeUri(uri);
+ isNativeUri = true;
+ }
+
+ try {
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ if (fs.exists(inputURL)) {
+ if (!isNativeUri) {
+ // If not already resolved as native URI, resolve to a native URI and back to
+ // fix the terminating slash based on whether the entry is a directory or file.
+ inputURL = fs.toLocalUri(fs.toNativeUri(inputURL));
+ }
+
+ return fs.getEntryForLocalURL(inputURL);
+ }
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ throw new FileNotFoundException();
+ }
+
+ /**
+ * Read the list of files from this directory.
+ *
+ * @return a JSONArray containing JSONObjects that represent Entry objects.
+ * @throws FileNotFoundException if the directory is not found.
+ * @throws JSONException
+ * @throws MalformedURLException
+ */
+ private JSONArray readEntries(String baseURLstr) throws FileNotFoundException, JSONException, MalformedURLException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.readEntriesAtLocalURL(inputURL);
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+ /**
+ * A setup method that handles the move/copy of files/directories
+ *
+ * @param newName for the file directory to be called, if null use existing file name
+ * @param move if false do a copy, if true do a move
+ * @return a Entry object
+ * @throws NoModificationAllowedException
+ * @throws IOException
+ * @throws InvalidModificationException
+ * @throws EncodingException
+ * @throws JSONException
+ * @throws FileExistsException
+ */
+ private JSONObject transferTo(String srcURLstr, String destURLstr, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException {
+ if (srcURLstr == null || destURLstr == null) {
+ // either no source or no destination provided
+ throw new FileNotFoundException();
+ }
+
+ LocalFilesystemURL srcURL = LocalFilesystemURL.parse(srcURLstr);
+ LocalFilesystemURL destURL = LocalFilesystemURL.parse(destURLstr);
+
+ Filesystem srcFs = this.filesystemForURL(srcURL);
+ Filesystem destFs = this.filesystemForURL(destURL);
+
+ // Check for invalid file name
+ if (newName != null && newName.contains(":")) {
+ throw new EncodingException("Bad file name");
+ }
+
+ return destFs.copyFileToURL(destURL, newName, srcFs, srcURL, move);
+ }
+
+ /**
+ * Deletes a directory and all of its contents, if any. In the event of an error
+ * [e.g. trying to delete a directory that contains a file that cannot be removed],
+ * some of the contents of the directory may be deleted.
+ * It is an error to attempt to delete the root directory of a filesystem.
+ *
+ * @return a boolean representing success of failure
+ * @throws FileExistsException
+ * @throws NoModificationAllowedException
+ * @throws MalformedURLException
+ */
+ private boolean removeRecursively(String baseURLstr) throws FileExistsException, NoModificationAllowedException, MalformedURLException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
+ // You can't delete the root directory.
+ if ("".equals(inputURL.path) || "/".equals(inputURL.path)) {
+ throw new NoModificationAllowedException("You can't delete the root directory");
+ }
+
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.recursiveRemoveFileAtLocalURL(inputURL);
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+
+ /**
+ * Deletes a file or directory. It is an error to attempt to delete a directory that is not empty.
+ * It is an error to attempt to delete the root directory of a filesystem.
+ *
+ * @return a boolean representing success of failure
+ * @throws NoModificationAllowedException
+ * @throws InvalidModificationException
+ * @throws MalformedURLException
+ */
+ private boolean remove(String baseURLstr) throws NoModificationAllowedException, InvalidModificationException, MalformedURLException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
+ // You can't delete the root directory.
+ if ("".equals(inputURL.path) || "/".equals(inputURL.path)) {
+
+ throw new NoModificationAllowedException("You can't delete the root directory");
+ }
+
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.removeFileAtLocalURL(inputURL);
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+ /**
+ * Creates or looks up a file.
+ *
+ * @param baseURLstr base directory
+ * @param path file/directory to lookup or create
+ * @param options specify whether to create or not
+ * @param directory if true look up directory, if false look up file
+ * @return a Entry object
+ * @throws FileExistsException
+ * @throws IOException
+ * @throws TypeMismatchException
+ * @throws EncodingException
+ * @throws JSONException
+ */
+ private JSONObject getFile(String baseURLstr, String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.getFileForLocalURL(inputURL, path, options, directory);
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+
+ }
+
+ /**
+ * Look up the parent DirectoryEntry containing this Entry.
+ * If this Entry is the root of its filesystem, its parent is itself.
+ */
+ private JSONObject getParent(String baseURLstr) throws JSONException, IOException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.getParentForLocalURL(inputURL);
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+ /**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @return returns a JSONObject represent a W3C File object
+ */
+ private JSONObject getFileMetadata(String baseURLstr) throws FileNotFoundException, JSONException, MalformedURLException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+ return fs.getFileMetadataForLocalURL(inputURL);
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+ /**
+ * Requests a filesystem in which to store application data.
+ *
+ * @param type of file system requested
+ * @param requiredSize required free space in the file system in bytes
+ * @param callbackContext context for returning the result or error
+ * @throws JSONException
+ */
+ private void requestFileSystem(int type, long requiredSize, final CallbackContext callbackContext) throws JSONException {
+ Filesystem rootFs = null;
+ try {
+ rootFs = this.filesystems.get(type);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Pass null through
+ }
+ if (rootFs == null) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR));
+ } else {
+ // If a nonzero required size was specified, check that the retrieved filesystem has enough free space.
+ long availableSize = 0;
+ if (requiredSize > 0) {
+ availableSize = rootFs.getFreeSpaceInBytes();
+ }
+
+ if (availableSize < requiredSize) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR));
+ } else {
+ JSONObject fs = new JSONObject();
+ fs.put("name", rootFs.name);
+ fs.put("root", rootFs.getRootEntry());
+ callbackContext.success(fs);
+ }
+ }
+ }
+
+ /**
+ * Requests a filesystem in which to store application data.
+ *
+ * @return a JSONObject representing the file system
+ */
+ private JSONArray requestAllFileSystems() throws IOException, JSONException {
+ JSONArray ret = new JSONArray();
+ for (Filesystem fs : filesystems) {
+ ret.put(fs.getRootEntry());
+ }
+ return ret;
+ }
+
+ private static String toDirUrl(File f) {
+ return Uri.fromFile(f).toString() + '/';
+ }
+
+ private JSONObject requestAllPaths() throws JSONException {
+ Context context = cordova.getActivity();
+ JSONObject ret = new JSONObject();
+ ret.put("applicationDirectory", "file:///android_asset/");
+ ret.put("applicationStorageDirectory", toDirUrl(context.getFilesDir().getParentFile()));
+ ret.put("dataDirectory", toDirUrl(context.getFilesDir()));
+ ret.put("cacheDirectory", toDirUrl(context.getCacheDir()));
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ try {
+ ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile()));
+ ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null)));
+ ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir()));
+ ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory()));
+ }
+ catch(NullPointerException e) {
+ /* If external storage is unavailable, context.getExternal* returns null */
+ LOG.d(LOG_TAG, "Unable to access these paths, most liklely due to USB storage");
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Returns a JSON object representing the given File. Internal APIs should be modified
+ * to use URLs instead of raw FS paths wherever possible, when interfacing with this plugin.
+ *
+ * @param file the File to convert
+ * @return a JSON representation of the given File
+ * @throws JSONException
+ */
+ public JSONObject getEntryForFile(File file) throws JSONException {
+ JSONObject entry;
+
+ for (Filesystem fs : filesystems) {
+ entry = fs.makeEntryForFile(file);
+ if (entry != null) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a JSON object representing the given File. Deprecated, as this is only used by
+ * FileTransfer, and because it is a static method that should really be an instance method,
+ * since it depends on the actual filesystem roots in use. Internal APIs should be modified
+ * to use URLs instead of raw FS paths wherever possible, when interfacing with this plugin.
+ *
+ * @param file the File to convert
+ * @return a JSON representation of the given File
+ * @throws JSONException
+ */
+ @Deprecated
+ public static JSONObject getEntry(File file) throws JSONException {
+ if (getFilePlugin() != null) {
+ return getFilePlugin().getEntryForFile(file);
+ }
+ return null;
+ }
+
+ /**
+ * Read the contents of a file.
+ * This is done in a background thread; the result is sent to the callback.
+ *
+ * @param start Start position in the file.
+ * @param end End position to stop at (exclusive).
+ * @param callbackContext The context through which to send the result.
+ * @param encoding The encoding to return contents as. Typical value is UTF-8. (see http://www.iana.org/assignments/character-sets)
+ * @param resultType The desired type of data to send to the callback.
+ * @return Contents of file.
+ */
+ public void readFileAs(final String srcURLstr, final int start, final int end, final CallbackContext callbackContext, final String encoding, final int resultType) throws MalformedURLException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+
+ fs.readFileAtURL(inputURL, start, end, new Filesystem.ReadFileCallback() {
+ public void handleData(InputStream inputStream, String contentType) {
+ try {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ final int BUFFER_SIZE = 8192;
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ for (;;) {
+ int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
+
+ if (bytesRead <= 0) {
+ break;
+ }
+ os.write(buffer, 0, bytesRead);
+ }
+
+ PluginResult result;
+ switch (resultType) {
+ case PluginResult.MESSAGE_TYPE_STRING:
+ result = new PluginResult(PluginResult.Status.OK, os.toString(encoding));
+ break;
+ case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
+ result = new PluginResult(PluginResult.Status.OK, os.toByteArray());
+ break;
+ case PluginResult.MESSAGE_TYPE_BINARYSTRING:
+ result = new PluginResult(PluginResult.Status.OK, os.toByteArray(), true);
+ break;
+ default: // Base64.
+ byte[] base64 = Base64.encode(os.toByteArray(), Base64.NO_WRAP);
+ String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII");
+ result = new PluginResult(PluginResult.Status.OK, s);
+ }
+
+ callbackContext.sendPluginResult(result);
+ } catch (IOException e) {
+ LOG.d(LOG_TAG, e.getLocalizedMessage());
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR));
+ }
+ }
+ });
+
+
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ } catch (FileNotFoundException e) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_FOUND_ERR));
+ } catch (IOException e) {
+ LOG.d(LOG_TAG, e.getLocalizedMessage());
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR));
+ }
+ }
+
+
+ /**
+ * Write contents of file.
+ *
+ * @param data The contents of the file.
+ * @param offset The position to begin writing the file.
+ * @param isBinary True if the file contents are base64-encoded binary data
+ */
+ /**/
+ public long write(String srcURLstr, String data, int offset, boolean isBinary) throws FileNotFoundException, IOException, NoModificationAllowedException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+
+ long x = fs.writeToFileAtURL(inputURL, data, offset, isBinary); LOG.d("TEST",srcURLstr + ": "+x); return x;
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+
+ }
+
+ /**
+ * Truncate the file to size
+ */
+ private long truncateFile(String srcURLstr, long size) throws FileNotFoundException, IOException, NoModificationAllowedException {
+ try {
+ LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr);
+ Filesystem fs = this.filesystemForURL(inputURL);
+ if (fs == null) {
+ throw new MalformedURLException("No installed handlers for this URL");
+ }
+
+ return fs.truncateFileAtURL(inputURL, size);
+ } catch (IllegalArgumentException e) {
+ MalformedURLException mue = new MalformedURLException("Unrecognized filesystem URL");
+ mue.initCause(e);
+ throw mue;
+ }
+ }
+
+
+ /*
+ * Handle the response
+ */
+
+ public void onRequestPermissionResult(int requestCode, String[] permissions,
+ int[] grantResults) throws JSONException {
+
+ final PendingRequests.Request req = pendingRequests.getAndRemove(requestCode);
+ if (req != null) {
+ for(int r:grantResults)
+ {
+ if(r == PackageManager.PERMISSION_DENIED)
+ {
+ req.getCallbackContext().sendPluginResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR));
+ return;
+ }
+ }
+ switch(req.getAction())
+ {
+ case ACTION_GET_FILE:
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ String dirname = args.getString(0);
+
+ String path = args.getString(1);
+ JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false);
+ req.getCallbackContext().success(obj);
+ }
+ }, req.getRawArgs(), req.getCallbackContext());
+ break;
+ case ACTION_GET_DIRECTORY:
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ String dirname = args.getString(0);
+
+ String path = args.getString(1);
+ JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true);
+ req.getCallbackContext().success(obj);
+ }
+ }, req.getRawArgs(), req.getCallbackContext());
+ break;
+ case ACTION_WRITE:
+ threadhelper( new FileOp( ){
+ public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException {
+ String fname=args.getString(0);
+ String data=args.getString(1);
+ int offset=args.getInt(2);
+ Boolean isBinary=args.getBoolean(3);
+ long fileSize = write(fname, data, offset, isBinary);
+ req.getCallbackContext().sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
+ }
+ }, req.getRawArgs(), req.getCallbackContext());
+ break;
+ }
+ } else {
+ LOG.d(LOG_TAG, "Received permission callback for unknown request code");
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/Filesystem.java b/StoneIsland/plugins/cordova-plugin-file/src/android/Filesystem.java
new file mode 100644
index 00000000..c69d3bdd
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/Filesystem.java
@@ -0,0 +1,331 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file;
+
+import android.net.Uri;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.apache.cordova.CordovaResourceApi;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public abstract class Filesystem {
+
+ protected final Uri rootUri;
+ protected final CordovaResourceApi resourceApi;
+ public final String name;
+ private JSONObject rootEntry;
+
+ public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) {
+ this.rootUri = rootUri;
+ this.name = name;
+ this.resourceApi = resourceApi;
+ }
+
+ public interface ReadFileCallback {
+ public void handleData(InputStream inputStream, String contentType) throws IOException;
+ }
+
+ public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL) {
+ try {
+ String path = inputURL.path;
+ int end = path.endsWith("/") ? 1 : 0;
+ String[] parts = path.substring(0, path.length() - end).split("/+");
+ String fileName = parts[parts.length - 1];
+
+ JSONObject entry = new JSONObject();
+ entry.put("isFile", !inputURL.isDirectory);
+ entry.put("isDirectory", inputURL.isDirectory);
+ entry.put("name", fileName);
+ entry.put("fullPath", path);
+ // The file system can't be specified, as it would lead to an infinite loop,
+ // but the filesystem name can be.
+ entry.put("filesystemName", inputURL.fsName);
+ // Backwards compatibility
+ entry.put("filesystem", "temporary".equals(inputURL.fsName) ? 0 : 1);
+
+ String nativeUrlStr = nativeURL.toString();
+ if (inputURL.isDirectory && !nativeUrlStr.endsWith("/")) {
+ nativeUrlStr += "/";
+ }
+ entry.put("nativeURL", nativeUrlStr);
+ return entry;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+ public JSONObject makeEntryForURL(LocalFilesystemURL inputURL) {
+ Uri nativeUri = toNativeUri(inputURL);
+ return nativeUri == null ? null : makeEntryForURL(inputURL, nativeUri);
+ }
+
+ public JSONObject makeEntryForNativeUri(Uri nativeUri) {
+ LocalFilesystemURL inputUrl = toLocalUri(nativeUri);
+ return inputUrl == null ? null : makeEntryForURL(inputUrl, nativeUri);
+ }
+
+ public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException {
+ return makeEntryForURL(inputURL);
+ }
+
+ public JSONObject makeEntryForFile(File file) {
+ return makeEntryForNativeUri(Uri.fromFile(file));
+ }
+
+ abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String path,
+ JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException;
+
+ abstract boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException;
+
+ abstract boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException;
+
+ abstract LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException;
+
+ public final JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ LocalFilesystemURL[] children = listChildren(inputURL);
+ JSONArray entries = new JSONArray();
+ if (children != null) {
+ for (LocalFilesystemURL url : children) {
+ entries.put(makeEntryForURL(url));
+ }
+ }
+ return entries;
+ }
+
+ abstract JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException;
+
+ public Uri getRootUri() {
+ return rootUri;
+ }
+
+ public boolean exists(LocalFilesystemURL inputURL) {
+ try {
+ getFileMetadataForLocalURL(inputURL);
+ } catch (FileNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public Uri nativeUriForFullPath(String fullPath) {
+ Uri ret = null;
+ if (fullPath != null) {
+ String encodedPath = Uri.fromFile(new File(fullPath)).getEncodedPath();
+ if (encodedPath.startsWith("/")) {
+ encodedPath = encodedPath.substring(1);
+ }
+ ret = rootUri.buildUpon().appendEncodedPath(encodedPath).build();
+ }
+ return ret;
+ }
+
+ public LocalFilesystemURL localUrlforFullPath(String fullPath) {
+ Uri nativeUri = nativeUriForFullPath(fullPath);
+ if (nativeUri != null) {
+ return toLocalUri(nativeUri);
+ }
+ return null;
+ }
+
+ /**
+ * Removes multiple repeated //s, and collapses processes ../s.
+ */
+ protected static String normalizePath(String rawPath) {
+ // If this is an absolute path, trim the leading "/" and replace it later
+ boolean isAbsolutePath = rawPath.startsWith("/");
+ if (isAbsolutePath) {
+ rawPath = rawPath.replaceFirst("/+", "");
+ }
+ ArrayList<String> components = new ArrayList<String>(Arrays.asList(rawPath.split("/+")));
+ for (int index = 0; index < components.size(); ++index) {
+ if (components.get(index).equals("..")) {
+ components.remove(index);
+ if (index > 0) {
+ components.remove(index-1);
+ --index;
+ }
+ }
+ }
+ StringBuilder normalizedPath = new StringBuilder();
+ for(String component: components) {
+ normalizedPath.append("/");
+ normalizedPath.append(component);
+ }
+ if (isAbsolutePath) {
+ return normalizedPath.toString();
+ } else {
+ return normalizedPath.toString().substring(1);
+ }
+ }
+
+ /**
+ * Gets the free space in bytes available on this filesystem.
+ * Subclasses may override this method to return nonzero free space.
+ */
+ public long getFreeSpaceInBytes() {
+ return 0;
+ }
+
+ public abstract Uri toNativeUri(LocalFilesystemURL inputURL);
+ public abstract LocalFilesystemURL toLocalUri(Uri inputURL);
+
+ public JSONObject getRootEntry() {
+ if (rootEntry == null) {
+ rootEntry = makeEntryForNativeUri(rootUri);
+ }
+ return rootEntry;
+ }
+
+ public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException {
+ Uri parentUri = inputURL.uri;
+ String parentPath = new File(inputURL.uri.getPath()).getParent();
+ if (!"/".equals(parentPath)) {
+ parentUri = inputURL.uri.buildUpon().path(parentPath + '/').build();
+ }
+ return getEntryForLocalURL(LocalFilesystemURL.parse(parentUri));
+ }
+
+ protected LocalFilesystemURL makeDestinationURL(String newName, LocalFilesystemURL srcURL, LocalFilesystemURL destURL, boolean isDirectory) {
+ // I know this looks weird but it is to work around a JSON bug.
+ if ("null".equals(newName) || "".equals(newName)) {
+ newName = srcURL.uri.getLastPathSegment();;
+ }
+
+ String newDest = destURL.uri.toString();
+ if (newDest.endsWith("/")) {
+ newDest = newDest + newName;
+ } else {
+ newDest = newDest + "/" + newName;
+ }
+ if (isDirectory) {
+ newDest += '/';
+ }
+ return LocalFilesystemURL.parse(newDest);
+ }
+
+ /* Read a source URL (possibly from a different filesystem, srcFs,) and copy it to
+ * the destination URL on this filesystem, optionally with a new filename.
+ * If move is true, then this method should either perform an atomic move operation
+ * or remove the source file when finished.
+ */
+ public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
+ Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException {
+ // First, check to see that we can do it
+ if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) {
+ throw new NoModificationAllowedException("Cannot move file at source URL");
+ }
+ final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory);
+
+ Uri srcNativeUri = srcFs.toNativeUri(srcURL);
+
+ CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcNativeUri);
+ OutputStream os = null;
+ try {
+ os = getOutputStreamForURL(destination);
+ } catch (IOException e) {
+ ofrr.inputStream.close();
+ throw e;
+ }
+ // Closes streams.
+ resourceApi.copyResource(ofrr, os);
+
+ if (move) {
+ srcFs.removeFileAtLocalURL(srcURL);
+ }
+ return getEntryForLocalURL(destination);
+ }
+
+ public OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException {
+ return resourceApi.openOutputStream(toNativeUri(inputURL));
+ }
+
+ public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
+ ReadFileCallback readFileCallback) throws IOException {
+ CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(toNativeUri(inputURL));
+ if (end < 0) {
+ end = ofrr.length;
+ }
+ long numBytesToRead = end - start;
+ try {
+ if (start > 0) {
+ ofrr.inputStream.skip(start);
+ }
+ InputStream inputStream = ofrr.inputStream;
+ if (end < ofrr.length) {
+ inputStream = new LimitedInputStream(inputStream, numBytesToRead);
+ }
+ readFileCallback.handleData(inputStream, ofrr.mimeType);
+ } finally {
+ ofrr.inputStream.close();
+ }
+ }
+
+ abstract long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset,
+ boolean isBinary) throws NoModificationAllowedException, IOException;
+
+ abstract long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
+ throws IOException, NoModificationAllowedException;
+
+ // This method should return null if filesystem urls cannot be mapped to paths
+ abstract String filesystemPathForURL(LocalFilesystemURL url);
+
+ abstract LocalFilesystemURL URLforFilesystemPath(String path);
+
+ abstract boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL);
+
+ protected class LimitedInputStream extends FilterInputStream {
+ long numBytesToRead;
+ public LimitedInputStream(InputStream in, long numBytesToRead) {
+ super(in);
+ this.numBytesToRead = numBytesToRead;
+ }
+ @Override
+ public int read() throws IOException {
+ if (numBytesToRead <= 0) {
+ return -1;
+ }
+ numBytesToRead--;
+ return in.read();
+ }
+ @Override
+ public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ if (numBytesToRead <= 0) {
+ return -1;
+ }
+ int bytesToRead = byteCount;
+ if (byteCount > numBytesToRead) {
+ bytesToRead = (int)numBytesToRead; // Cast okay; long is less than int here.
+ }
+ int numBytesRead = in.read(buffer, byteOffset, bytesToRead);
+ numBytesToRead -= numBytesRead;
+ return numBytesRead;
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/InvalidModificationException.java b/StoneIsland/plugins/cordova-plugin-file/src/android/InvalidModificationException.java
new file mode 100644
index 00000000..8f6bec59
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/InvalidModificationException.java
@@ -0,0 +1,30 @@
+/*
+ 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.
+*/
+
+
+package org.apache.cordova.file;
+
+@SuppressWarnings("serial")
+public class InvalidModificationException extends Exception {
+
+ public InvalidModificationException(String message) {
+ super(message);
+ }
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystem.java b/StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystem.java
new file mode 100644
index 00000000..051f9949
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystem.java
@@ -0,0 +1,513 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import org.apache.cordova.CordovaResourceApi;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.os.Build;
+import android.os.Environment;
+import android.util.Base64;
+import android.net.Uri;
+import android.content.Context;
+import android.content.Intent;
+
+import java.nio.charset.Charset;
+
+public class LocalFilesystem extends Filesystem {
+ private final Context context;
+
+ public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot) {
+ super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi);
+ this.context = context;
+ }
+
+ public String filesystemPathForFullPath(String fullPath) {
+ return new File(rootUri.getPath(), fullPath).toString();
+ }
+
+ @Override
+ public String filesystemPathForURL(LocalFilesystemURL url) {
+ return filesystemPathForFullPath(url.path);
+ }
+
+ private String fullPathForFilesystemPath(String absolutePath) {
+ if (absolutePath != null && absolutePath.startsWith(rootUri.getPath())) {
+ return absolutePath.substring(rootUri.getPath().length() - 1);
+ }
+ return null;
+ }
+
+ @Override
+ public Uri toNativeUri(LocalFilesystemURL inputURL) {
+ return nativeUriForFullPath(inputURL.path);
+ }
+
+ @Override
+ public LocalFilesystemURL toLocalUri(Uri inputURL) {
+ if (!"file".equals(inputURL.getScheme())) {
+ return null;
+ }
+ File f = new File(inputURL.getPath());
+ // Removes and duplicate /s (e.g. file:///a//b/c)
+ Uri resolvedUri = Uri.fromFile(f);
+ String rootUriNoTrailingSlash = rootUri.getEncodedPath();
+ rootUriNoTrailingSlash = rootUriNoTrailingSlash.substring(0, rootUriNoTrailingSlash.length() - 1);
+ if (!resolvedUri.getEncodedPath().startsWith(rootUriNoTrailingSlash)) {
+ return null;
+ }
+ String subPath = resolvedUri.getEncodedPath().substring(rootUriNoTrailingSlash.length());
+ // Strip leading slash
+ if (!subPath.isEmpty()) {
+ subPath = subPath.substring(1);
+ }
+ Uri.Builder b = new Uri.Builder()
+ .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
+ .authority("localhost")
+ .path(name);
+ if (!subPath.isEmpty()) {
+ b.appendEncodedPath(subPath);
+ }
+ if (f.isDirectory()) {
+ // Add trailing / for directories.
+ b.appendEncodedPath("");
+ }
+ return LocalFilesystemURL.parse(b.build());
+ }
+
+ @Override
+ public LocalFilesystemURL URLforFilesystemPath(String path) {
+ return localUrlforFullPath(fullPathForFilesystemPath(path));
+ }
+
+ @Override
+ public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
+ String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+ boolean create = false;
+ boolean exclusive = false;
+
+ if (options != null) {
+ create = options.optBoolean("create");
+ if (create) {
+ exclusive = options.optBoolean("exclusive");
+ }
+ }
+
+ // Check for a ":" character in the file to line up with BB and iOS
+ if (path.contains(":")) {
+ throw new EncodingException("This path has an invalid \":\" in it.");
+ }
+
+ LocalFilesystemURL requestedURL;
+
+ // Check whether the supplied path is absolute or relative
+ if (directory && !path.endsWith("/")) {
+ path += "/";
+ }
+ if (path.startsWith("/")) {
+ requestedURL = localUrlforFullPath(normalizePath(path));
+ } else {
+ requestedURL = localUrlforFullPath(normalizePath(inputURL.path + "/" + path));
+ }
+
+ File fp = new File(this.filesystemPathForURL(requestedURL));
+
+ if (create) {
+ if (exclusive && fp.exists()) {
+ throw new FileExistsException("create/exclusive fails");
+ }
+ if (directory) {
+ fp.mkdir();
+ } else {
+ fp.createNewFile();
+ }
+ if (!fp.exists()) {
+ throw new FileExistsException("create fails");
+ }
+ }
+ else {
+ if (!fp.exists()) {
+ throw new FileNotFoundException("path does not exist");
+ }
+ if (directory) {
+ if (fp.isFile()) {
+ throw new TypeMismatchException("path doesn't exist or is file");
+ }
+ } else {
+ if (fp.isDirectory()) {
+ throw new TypeMismatchException("path doesn't exist or is directory");
+ }
+ }
+ }
+
+ // Return the directory
+ return makeEntryForURL(requestedURL);
+ }
+
+ @Override
+ public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException {
+
+ File fp = new File(filesystemPathForURL(inputURL));
+
+ // You can't delete a directory that is not empty
+ if (fp.isDirectory() && fp.list().length > 0) {
+ throw new InvalidModificationException("You can't delete a directory that is not empty.");
+ }
+
+ return fp.delete();
+ }
+
+ @Override
+ public boolean exists(LocalFilesystemURL inputURL) {
+ File fp = new File(filesystemPathForURL(inputURL));
+ return fp.exists();
+ }
+
+ @Override
+ public long getFreeSpaceInBytes() {
+ return DirectoryManager.getFreeSpaceInBytes(rootUri.getPath());
+ }
+
+ @Override
+ public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException {
+ File directory = new File(filesystemPathForURL(inputURL));
+ return removeDirRecursively(directory);
+ }
+
+ protected boolean removeDirRecursively(File directory) throws FileExistsException {
+ if (directory.isDirectory()) {
+ for (File file : directory.listFiles()) {
+ removeDirRecursively(file);
+ }
+ }
+
+ if (!directory.delete()) {
+ throw new FileExistsException("could not delete: " + directory.getName());
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ File fp = new File(filesystemPathForURL(inputURL));
+
+ if (!fp.exists()) {
+ // The directory we are listing doesn't exist so we should fail.
+ throw new FileNotFoundException();
+ }
+
+ File[] files = fp.listFiles();
+ if (files == null) {
+ // inputURL is a directory
+ return null;
+ }
+ LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length];
+ for (int i = 0; i < files.length; i++) {
+ entries[i] = URLforFilesystemPath(files[i].getPath());
+ }
+
+ return entries;
+ }
+
+ @Override
+ public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
+ File file = new File(filesystemPathForURL(inputURL));
+
+ if (!file.exists()) {
+ throw new FileNotFoundException("File at " + inputURL.uri + " does not exist.");
+ }
+
+ JSONObject metadata = new JSONObject();
+ try {
+ // Ensure that directories report a size of 0
+ metadata.put("size", file.isDirectory() ? 0 : file.length());
+ metadata.put("type", resourceApi.getMimeType(Uri.fromFile(file)));
+ metadata.put("name", file.getName());
+ metadata.put("fullPath", inputURL.path);
+ metadata.put("lastModifiedDate", file.lastModified());
+ } catch (JSONException e) {
+ return null;
+ }
+ return metadata;
+ }
+
+ private void copyFile(Filesystem srcFs, LocalFilesystemURL srcURL, File destFile, boolean move) throws IOException, InvalidModificationException, NoModificationAllowedException {
+ if (move) {
+ String realSrcPath = srcFs.filesystemPathForURL(srcURL);
+ if (realSrcPath != null) {
+ File srcFile = new File(realSrcPath);
+ if (srcFile.renameTo(destFile)) {
+ return;
+ }
+ // Trying to rename the file failed. Possibly because we moved across file system on the device.
+ }
+ }
+
+ CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(srcFs.toNativeUri(srcURL));
+ copyResource(offr, new FileOutputStream(destFile));
+
+ if (move) {
+ srcFs.removeFileAtLocalURL(srcURL);
+ }
+ }
+
+ private void copyDirectory(Filesystem srcFs, LocalFilesystemURL srcURL, File dstDir, boolean move) throws IOException, NoModificationAllowedException, InvalidModificationException, FileExistsException {
+ if (move) {
+ String realSrcPath = srcFs.filesystemPathForURL(srcURL);
+ if (realSrcPath != null) {
+ File srcDir = new File(realSrcPath);
+ // If the destination directory already exists and is empty then delete it. This is according to spec.
+ if (dstDir.exists()) {
+ if (dstDir.list().length > 0) {
+ throw new InvalidModificationException("directory is not empty");
+ }
+ dstDir.delete();
+ }
+ // Try to rename the directory
+ if (srcDir.renameTo(dstDir)) {
+ return;
+ }
+ // Trying to rename the file failed. Possibly because we moved across file system on the device.
+ }
+ }
+
+ if (dstDir.exists()) {
+ if (dstDir.list().length > 0) {
+ throw new InvalidModificationException("directory is not empty");
+ }
+ } else {
+ if (!dstDir.mkdir()) {
+ // If we can't create the directory then fail
+ throw new NoModificationAllowedException("Couldn't create the destination directory");
+ }
+ }
+
+ LocalFilesystemURL[] children = srcFs.listChildren(srcURL);
+ for (LocalFilesystemURL childLocalUrl : children) {
+ File target = new File(dstDir, new File(childLocalUrl.path).getName());
+ if (childLocalUrl.isDirectory) {
+ copyDirectory(srcFs, childLocalUrl, target, false);
+ } else {
+ copyFile(srcFs, childLocalUrl, target, false);
+ }
+ }
+
+ if (move) {
+ srcFs.recursiveRemoveFileAtLocalURL(srcURL);
+ }
+ }
+
+ @Override
+ public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
+ Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException {
+
+ // Check to see if the destination directory exists
+ String newParent = this.filesystemPathForURL(destURL);
+ File destinationDir = new File(newParent);
+ if (!destinationDir.exists()) {
+ // The destination does not exist so we should fail.
+ throw new FileNotFoundException("The source does not exist");
+ }
+
+ // Figure out where we should be copying to
+ final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory);
+
+ Uri dstNativeUri = toNativeUri(destinationURL);
+ Uri srcNativeUri = srcFs.toNativeUri(srcURL);
+ // Check to see if source and destination are the same file
+ if (dstNativeUri.equals(srcNativeUri)) {
+ throw new InvalidModificationException("Can't copy onto itself");
+ }
+
+ if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) {
+ throw new InvalidModificationException("Source URL is read-only (cannot move)");
+ }
+
+ File destFile = new File(dstNativeUri.getPath());
+ if (destFile.exists()) {
+ if (!srcURL.isDirectory && destFile.isDirectory()) {
+ throw new InvalidModificationException("Can't copy/move a file to an existing directory");
+ } else if (srcURL.isDirectory && destFile.isFile()) {
+ throw new InvalidModificationException("Can't copy/move a directory to an existing file");
+ }
+ }
+
+ if (srcURL.isDirectory) {
+ // E.g. Copy /sdcard/myDir to /sdcard/myDir/backup
+ if (dstNativeUri.toString().startsWith(srcNativeUri.toString() + '/')) {
+ throw new InvalidModificationException("Can't copy directory into itself");
+ }
+ copyDirectory(srcFs, srcURL, destFile, move);
+ } else {
+ copyFile(srcFs, srcURL, destFile, move);
+ }
+ return makeEntryForURL(destinationURL);
+ }
+
+ @Override
+ public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
+ int offset, boolean isBinary) throws IOException, NoModificationAllowedException {
+
+ boolean append = false;
+ if (offset > 0) {
+ this.truncateFileAtURL(inputURL, offset);
+ append = true;
+ }
+
+ byte[] rawData;
+ if (isBinary) {
+ rawData = Base64.decode(data, Base64.DEFAULT);
+ } else {
+ rawData = data.getBytes(Charset.defaultCharset());
+ }
+ ByteArrayInputStream in = new ByteArrayInputStream(rawData);
+ try
+ {
+ byte buff[] = new byte[rawData.length];
+ String absolutePath = filesystemPathForURL(inputURL);
+ FileOutputStream out = new FileOutputStream(absolutePath, append);
+ try {
+ in.read(buff, 0, buff.length);
+ out.write(buff, 0, rawData.length);
+ out.flush();
+ } finally {
+ // Always close the output
+ out.close();
+ }
+ if (isPublicDirectory(absolutePath)) {
+ broadcastNewFile(Uri.fromFile(new File(absolutePath)));
+ }
+ }
+ catch (NullPointerException e)
+ {
+ // This is a bug in the Android implementation of the Java Stack
+ NoModificationAllowedException realException = new NoModificationAllowedException(inputURL.toString());
+ realException.initCause(e);
+ throw realException;
+ }
+
+ return rawData.length;
+ }
+
+ private boolean isPublicDirectory(String absolutePath) {
+ // TODO: should expose a way to scan app's private files (maybe via a flag).
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // Lollipop has a bug where SD cards are null.
+ for (File f : context.getExternalMediaDirs()) {
+ if(f != null && absolutePath.startsWith(f.getAbsolutePath())) {
+ return true;
+ }
+ }
+ }
+
+ String extPath = Environment.getExternalStorageDirectory().getAbsolutePath();
+ return absolutePath.startsWith(extPath);
+ }
+
+ /**
+ * Send broadcast of new file so files appear over MTP
+ */
+ private void broadcastNewFile(Uri nativeUri) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, nativeUri);
+ context.sendBroadcast(intent);
+ }
+
+ @Override
+ public long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException {
+ File file = new File(filesystemPathForURL(inputURL));
+
+ if (!file.exists()) {
+ throw new FileNotFoundException("File at " + inputURL.uri + " does not exist.");
+ }
+
+ RandomAccessFile raf = new RandomAccessFile(filesystemPathForURL(inputURL), "rw");
+ try {
+ if (raf.length() >= size) {
+ FileChannel channel = raf.getChannel();
+ channel.truncate(size);
+ return size;
+ }
+
+ return raf.length();
+ } finally {
+ raf.close();
+ }
+
+
+ }
+
+ @Override
+ public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
+ String path = filesystemPathForURL(inputURL);
+ File file = new File(path);
+ return file.exists();
+ }
+
+ // This is a copy & paste from CordovaResource API that is required since CordovaResourceApi
+ // has a bug pre-4.0.0.
+ // TODO: Once cordova-android@4.0.0 is released, delete this copy and make the plugin depend on
+ // 4.0.0 with an engine tag.
+ private static void copyResource(CordovaResourceApi.OpenForReadResult input, OutputStream outputStream) throws IOException {
+ try {
+ InputStream inputStream = input.inputStream;
+ if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) {
+ FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel();
+ FileChannel outChannel = ((FileOutputStream)outputStream).getChannel();
+ long offset = 0;
+ long length = input.length;
+ if (input.assetFd != null) {
+ offset = input.assetFd.getStartOffset();
+ }
+ // transferFrom()'s 2nd arg is a relative position. Need to set the absolute
+ // position first.
+ inChannel.position(offset);
+ outChannel.transferFrom(inChannel, 0, length);
+ } else {
+ final int BUFFER_SIZE = 8192;
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ for (;;) {
+ int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
+
+ if (bytesRead <= 0) {
+ break;
+ }
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ }
+ } finally {
+ input.inputStream.close();
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java b/StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java
new file mode 100644
index 00000000..b96b6ee4
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java
@@ -0,0 +1,64 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file;
+
+import android.net.Uri;
+
+public class LocalFilesystemURL {
+
+ public static final String FILESYSTEM_PROTOCOL = "cdvfile";
+
+ public final Uri uri;
+ public final String fsName;
+ public final String path;
+ public final boolean isDirectory;
+
+ private LocalFilesystemURL(Uri uri, String fsName, String fsPath, boolean isDirectory) {
+ this.uri = uri;
+ this.fsName = fsName;
+ this.path = fsPath;
+ this.isDirectory = isDirectory;
+ }
+
+ public static LocalFilesystemURL parse(Uri uri) {
+ if (!FILESYSTEM_PROTOCOL.equals(uri.getScheme())) {
+ return null;
+ }
+ String path = uri.getPath();
+ if (path.length() < 1) {
+ return null;
+ }
+ int firstSlashIdx = path.indexOf('/', 1);
+ if (firstSlashIdx < 0) {
+ return null;
+ }
+ String fsName = path.substring(1, firstSlashIdx);
+ path = path.substring(firstSlashIdx);
+ boolean isDirectory = path.charAt(path.length() - 1) == '/';
+ return new LocalFilesystemURL(uri, fsName, path, isDirectory);
+ }
+
+ public static LocalFilesystemURL parse(String uri) {
+ return parse(Uri.parse(uri));
+ }
+
+ public String toString() {
+ return uri.toString();
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java b/StoneIsland/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java
new file mode 100644
index 00000000..627eafb5
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java
@@ -0,0 +1,29 @@
+/*
+ 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.
+*/
+
+package org.apache.cordova.file;
+
+@SuppressWarnings("serial")
+public class NoModificationAllowedException extends Exception {
+
+ public NoModificationAllowedException(String message) {
+ super(message);
+ }
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/PendingRequests.java b/StoneIsland/plugins/cordova-plugin-file/src/android/PendingRequests.java
new file mode 100644
index 00000000..4c75f423
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/PendingRequests.java
@@ -0,0 +1,94 @@
+/*
+ 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.
+*/
+package org.apache.cordova.file;
+
+import android.util.SparseArray;
+
+import org.apache.cordova.CallbackContext;
+
+/**
+ * Holds pending runtime permission requests
+ */
+class PendingRequests {
+ private int currentReqId = 0;
+ private SparseArray<Request> requests = new SparseArray<Request>();
+
+ /**
+ * Creates a request and adds it to the array of pending requests. Each created request gets a
+ * unique result code for use with requestPermission()
+ * @param rawArgs The raw arguments passed to the plugin
+ * @param action The action this request corresponds to (get file, etc.)
+ * @param callbackContext The CallbackContext for this plugin call
+ * @return The request code that can be used to retrieve the Request object
+ */
+ public synchronized int createRequest(String rawArgs, int action, CallbackContext callbackContext) {
+ Request req = new Request(rawArgs, action, callbackContext);
+ requests.put(req.requestCode, req);
+ return req.requestCode;
+ }
+
+ /**
+ * Gets the request corresponding to this request code and removes it from the pending requests
+ * @param requestCode The request code for the desired request
+ * @return The request corresponding to the given request code or null if such a
+ * request is not found
+ */
+ public synchronized Request getAndRemove(int requestCode) {
+ Request result = requests.get(requestCode);
+ requests.remove(requestCode);
+ return result;
+ }
+
+ /**
+ * Holds the options and CallbackContext for a call made to the plugin.
+ */
+ public class Request {
+
+ // Unique int used to identify this request in any Android permission callback
+ private int requestCode;
+
+ // Action to be performed after permission request result
+ private int action;
+
+ // Raw arguments passed to plugin
+ private String rawArgs;
+
+ // The callback context for this plugin request
+ private CallbackContext callbackContext;
+
+ private Request(String rawArgs, int action, CallbackContext callbackContext) {
+ this.rawArgs = rawArgs;
+ this.action = action;
+ this.callbackContext = callbackContext;
+ this.requestCode = currentReqId ++;
+ }
+
+ public int getAction() {
+ return this.action;
+ }
+
+ public String getRawArgs() {
+ return rawArgs;
+ }
+
+ public CallbackContext getCallbackContext() {
+ return callbackContext;
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/TypeMismatchException.java b/StoneIsland/plugins/cordova-plugin-file/src/android/TypeMismatchException.java
new file mode 100644
index 00000000..1315f9a9
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/TypeMismatchException.java
@@ -0,0 +1,30 @@
+/*
+ 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.
+*/
+
+
+package org.apache.cordova.file;
+
+@SuppressWarnings("serial")
+public class TypeMismatchException extends Exception {
+
+ public TypeMismatchException(String message) {
+ super(message);
+ }
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/android/build-extras.gradle b/StoneIsland/plugins/cordova-plugin-file/src/android/build-extras.gradle
new file mode 100644
index 00000000..a0a7844a
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/android/build-extras.gradle
@@ -0,0 +1,47 @@
+/*
+ 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.
+ */
+ext.postBuildExtras = {
+ def inAssetsDir = file("assets")
+ def outAssetsDir = inAssetsDir
+ def outFile = new File(outAssetsDir, "cdvasset.manifest")
+
+ def newTask = task("cdvCreateAssetManifest") << {
+ def contents = new HashMap()
+ def sizes = new HashMap()
+ contents[""] = inAssetsDir.list()
+ def tree = fileTree(dir: inAssetsDir)
+ tree.visit { fileDetails ->
+ if (fileDetails.isDirectory()) {
+ contents[fileDetails.relativePath.toString()] = fileDetails.file.list()
+ } else {
+ sizes[fileDetails.relativePath.toString()] = fileDetails.file.length()
+ }
+ }
+
+ outAssetsDir.mkdirs()
+ outFile.withObjectOutputStream { oos ->
+ oos.writeObject(contents)
+ oos.writeObject(sizes)
+ }
+ }
+ newTask.inputs.dir inAssetsDir
+ newTask.outputs.file outFile
+ def preBuildTask = tasks["preBuild"]
+ preBuildTask.dependsOn(newTask)
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/browser/FileProxy.js b/StoneIsland/plugins/cordova-plugin-file/src/browser/FileProxy.js
new file mode 100644
index 00000000..17955b68
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/browser/FileProxy.js
@@ -0,0 +1,1059 @@
+/*
+ *
+ * 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.
+ *
+ */
+(function () {
+ /* global require, exports, module */
+ /* global FILESYSTEM_PREFIX */
+ /* global IDBKeyRange */
+ /* global FileReader */
+ /* global atob, btoa, Blob */
+
+ /* Heavily based on https://github.com/ebidel/idb.filesystem.js */
+
+ // For chrome we don't need to implement proxy methods
+ // All functionality can be accessed natively.
+ if (require('./isChrome')()) {
+ var pathsPrefix = {
+ // Read-only directory where the application is installed.
+ applicationDirectory: location.origin + '/', // eslint-disable-line no-undef
+ // Where to put app-specific data files.
+ dataDirectory: 'filesystem:file:///persistent/',
+ // Cached files that should survive app restarts.
+ // Apps should not rely on the OS to delete files in here.
+ cacheDirectory: 'filesystem:file:///temporary/'
+ };
+
+ exports.requestAllPaths = function (successCallback) {
+ successCallback(pathsPrefix);
+ };
+
+ require('cordova/exec/proxy').add('File', module.exports);
+ return;
+ }
+
+ var LocalFileSystem = require('./LocalFileSystem');
+ var FileSystem = require('./FileSystem');
+ var FileEntry = require('./FileEntry');
+ var FileError = require('./FileError');
+ var DirectoryEntry = require('./DirectoryEntry');
+ var File = require('./File');
+
+ (function (exports, global) {
+ var indexedDB = global.indexedDB || global.mozIndexedDB;
+ if (!indexedDB) {
+ throw 'Firefox OS File plugin: indexedDB not supported';
+ }
+
+ var fs_ = null;
+
+ var idb_ = {};
+ idb_.db = null;
+ var FILE_STORE_ = 'entries';
+
+ var DIR_SEPARATOR = '/';
+
+ var pathsPrefix = {
+ // Read-only directory where the application is installed.
+ applicationDirectory: location.origin + '/', // eslint-disable-line no-undef
+ // Where to put app-specific data files.
+ dataDirectory: 'file:///persistent/',
+ // Cached files that should survive app restarts.
+ // Apps should not rely on the OS to delete files in here.
+ cacheDirectory: 'file:///temporary/'
+ };
+
+ var unicodeLastChar = 65535;
+
+ /** * Exported functionality ***/
+
+ exports.requestFileSystem = function (successCallback, errorCallback, args) {
+ var type = args[0];
+ // Size is ignored since IDB filesystem size depends
+ // on browser implementation and can't be set up by user
+ var size = args[1]; // eslint-disable-line no-unused-vars
+
+ if (type !== LocalFileSystem.TEMPORARY && type !== LocalFileSystem.PERSISTENT) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ return;
+ }
+
+ var name = type === LocalFileSystem.TEMPORARY ? 'temporary' : 'persistent';
+ var storageName = (location.protocol + location.host).replace(/:/g, '_'); // eslint-disable-line no-undef
+
+ var root = new DirectoryEntry('', DIR_SEPARATOR);
+ fs_ = new FileSystem(name, root);
+
+ idb_.open(storageName, function () {
+ successCallback(fs_);
+ }, errorCallback);
+ };
+
+ // Overridden by Android, BlackBerry 10 and iOS to populate fsMap
+ require('./fileSystems').getFs = function (name, callback) {
+ callback(new FileSystem(name, fs_.root));
+ };
+
+ // list a directory's contents (files and folders).
+ exports.readEntries = function (successCallback, errorCallback, args) {
+ var fullPath = args[0];
+
+ if (typeof successCallback !== 'function') {
+ throw Error('Expected successCallback argument.');
+ }
+
+ var path = resolveToFullPath_(fullPath);
+
+ exports.getDirectory(function () {
+ idb_.getAllEntries(path.fullPath + DIR_SEPARATOR, path.storagePath, function (entries) {
+ successCallback(entries);
+ }, errorCallback);
+ }, function () {
+ if (errorCallback) {
+ errorCallback(FileError.NOT_FOUND_ERR);
+ }
+ }, [path.storagePath, path.fullPath, {create: false}]);
+ };
+
+ exports.getFile = function (successCallback, errorCallback, args) {
+ var fullPath = args[0];
+ var path = args[1];
+ var options = args[2] || {};
+
+ // Create an absolute path if we were handed a relative one.
+ path = resolveToFullPath_(fullPath, path);
+
+ idb_.get(path.storagePath, function (fileEntry) {
+ if (options.create === true && options.exclusive === true && fileEntry) {
+ // If create and exclusive are both true, and the path already exists,
+ // getFile must fail.
+
+ if (errorCallback) {
+ errorCallback(FileError.PATH_EXISTS_ERR);
+ }
+ } else if (options.create === true && !fileEntry) {
+ // If create is true, the path doesn't exist, and no other error occurs,
+ // getFile must create it as a zero-length file and return a corresponding
+ // FileEntry.
+ var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
+
+ newFileEntry.file_ = new MyFile({
+ size: 0,
+ name: newFileEntry.name,
+ lastModifiedDate: new Date(),
+ storagePath: path.storagePath
+ });
+
+ idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback);
+ } else if (options.create === true && fileEntry) {
+ if (fileEntry.isFile) {
+ // Overwrite file, delete then create new.
+ idb_['delete'](path.storagePath, function () {
+ var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
+
+ newFileEntry.file_ = new MyFile({
+ size: 0,
+ name: newFileEntry.name,
+ lastModifiedDate: new Date(),
+ storagePath: path.storagePath
+ });
+
+ idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback);
+ }, errorCallback);
+ } else {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ }
+ } else if ((!options.create || options.create === false) && !fileEntry) {
+ // If create is not true and the path doesn't exist, getFile must fail.
+ if (errorCallback) {
+ errorCallback(FileError.NOT_FOUND_ERR);
+ }
+ } else if ((!options.create || options.create === false) && fileEntry &&
+ fileEntry.isDirectory) {
+ // If create is not true and the path exists, but is a directory, getFile
+ // must fail.
+ if (errorCallback) {
+ errorCallback(FileError.TYPE_MISMATCH_ERR);
+ }
+ } else {
+ // Otherwise, if no other error occurs, getFile must return a FileEntry
+ // corresponding to path.
+
+ successCallback(fileEntryFromIdbEntry(fileEntry));
+ }
+ }, errorCallback);
+ };
+
+ exports.getFileMetadata = function (successCallback, errorCallback, args) {
+ var fullPath = args[0];
+
+ exports.getFile(function (fileEntry) {
+ successCallback(new File(fileEntry.file_.name, fileEntry.fullPath, '', fileEntry.file_.lastModifiedDate,
+ fileEntry.file_.size));
+ }, errorCallback, [fullPath, null]);
+ };
+
+ exports.getMetadata = function (successCallback, errorCallback, args) {
+ exports.getFile(function (fileEntry) {
+ successCallback(
+ {
+ modificationTime: fileEntry.file_.lastModifiedDate,
+ size: fileEntry.file_.lastModifiedDate
+ });
+ }, errorCallback, args);
+ };
+
+ exports.setMetadata = function (successCallback, errorCallback, args) {
+ var fullPath = args[0];
+ var metadataObject = args[1];
+
+ exports.getFile(function (fileEntry) {
+ fileEntry.file_.lastModifiedDate = metadataObject.modificationTime;
+ idb_.put(fileEntry, fileEntry.file_.storagePath, successCallback, errorCallback);
+ }, errorCallback, [fullPath, null]);
+ };
+
+ exports.write = function (successCallback, errorCallback, args) {
+ var fileName = args[0];
+ var data = args[1];
+ var position = args[2];
+ var isBinary = args[3]; // eslint-disable-line no-unused-vars
+
+ if (!data) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ return;
+ }
+
+ if (typeof data === 'string' || data instanceof String) {
+ data = new Blob([data]); // eslint-disable-line no-undef
+ }
+
+ exports.getFile(function (fileEntry) {
+ var blob_ = fileEntry.file_.blob_;
+
+ if (!blob_) {
+ blob_ = new Blob([data], {type: data.type}); // eslint-disable-line no-undef
+ } else {
+ // Calc the head and tail fragments
+ var head = blob_.slice(0, position);
+ var tail = blob_.slice(position + (data.size || data.byteLength));
+
+ // Calc the padding
+ var padding = position - head.size;
+ if (padding < 0) {
+ padding = 0;
+ }
+
+ // Do the "write". In fact, a full overwrite of the Blob.
+ blob_ = new Blob([head, new Uint8Array(padding), data, tail], // eslint-disable-line no-undef
+ {type: data.type});
+ }
+
+ // Set the blob we're writing on this file entry so we can recall it later.
+ fileEntry.file_.blob_ = blob_;
+ fileEntry.file_.lastModifiedDate = new Date() || null;
+ fileEntry.file_.size = blob_.size;
+ fileEntry.file_.name = blob_.name;
+ fileEntry.file_.type = blob_.type;
+
+ idb_.put(fileEntry, fileEntry.file_.storagePath, function () {
+ successCallback(data.size || data.byteLength);
+ }, errorCallback);
+ }, errorCallback, [fileName, null]);
+ };
+
+ exports.readAsText = function (successCallback, errorCallback, args) {
+ var fileName = args[0];
+ var enc = args[1];
+ var startPos = args[2];
+ var endPos = args[3];
+
+ readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback);
+ };
+
+ exports.readAsDataURL = function (successCallback, errorCallback, args) {
+ var fileName = args[0];
+ var startPos = args[1];
+ var endPos = args[2];
+
+ readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback);
+ };
+
+ exports.readAsBinaryString = function (successCallback, errorCallback, args) {
+ var fileName = args[0];
+ var startPos = args[1];
+ var endPos = args[2];
+
+ readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback);
+ };
+
+ exports.readAsArrayBuffer = function (successCallback, errorCallback, args) {
+ var fileName = args[0];
+ var startPos = args[1];
+ var endPos = args[2];
+
+ readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback);
+ };
+
+ exports.removeRecursively = exports.remove = function (successCallback, errorCallback, args) {
+ if (typeof successCallback !== 'function') {
+ throw Error('Expected successCallback argument.');
+ }
+
+ var fullPath = resolveToFullPath_(args[0]).storagePath;
+ if (fullPath === pathsPrefix.cacheDirectory || fullPath === pathsPrefix.dataDirectory) {
+ errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR);
+ return;
+ }
+
+ function deleteEntry (isDirectory) {
+ // TODO: This doesn't protect against directories that have content in it.
+ // Should throw an error instead if the dirEntry is not empty.
+ idb_['delete'](fullPath, function () {
+ successCallback();
+ }, function () {
+ if (errorCallback) { errorCallback(); }
+ }, isDirectory);
+ }
+
+ // We need to to understand what we are deleting:
+ exports.getDirectory(function (entry) {
+ deleteEntry(entry.isDirectory);
+ }, function () {
+ // DirectoryEntry was already deleted or entry is FileEntry
+ deleteEntry(false);
+ }, [fullPath, null, {create: false}]);
+ };
+
+ exports.getDirectory = function (successCallback, errorCallback, args) {
+ var fullPath = args[0];
+ var path = args[1];
+ var options = args[2];
+
+ // Create an absolute path if we were handed a relative one.
+ path = resolveToFullPath_(fullPath, path);
+
+ idb_.get(path.storagePath, function (folderEntry) {
+ if (!options) {
+ options = {};
+ }
+
+ if (options.create === true && options.exclusive === true && folderEntry) {
+ // If create and exclusive are both true, and the path already exists,
+ // getDirectory must fail.
+ if (errorCallback) {
+ errorCallback(FileError.PATH_EXISTS_ERR);
+ }
+ // There is a strange bug in mobilespec + FF, which results in coming to multiple else-if's
+ // so we are shielding from it with returns.
+ return;
+ }
+
+ if (options.create === true && !folderEntry) {
+ // If create is true, the path doesn't exist, and no other error occurs,
+ // getDirectory must create it as a zero-length file and return a corresponding
+ // MyDirectoryEntry.
+ var dirEntry = new DirectoryEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
+
+ idb_.put(dirEntry, path.storagePath, successCallback, errorCallback);
+ return;
+ }
+
+ if (options.create === true && folderEntry) {
+
+ if (folderEntry.isDirectory) {
+ // IDB won't save methods, so we need re-create the MyDirectoryEntry.
+ successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.filesystem));
+ } else {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ }
+ return;
+ }
+
+ if ((!options.create || options.create === false) && !folderEntry) {
+ // Handle root special. It should always exist.
+ if (path.fullPath === DIR_SEPARATOR) {
+ successCallback(fs_.root);
+ return;
+ }
+
+ // If create is not true and the path doesn't exist, getDirectory must fail.
+ if (errorCallback) {
+ errorCallback(FileError.NOT_FOUND_ERR);
+ }
+
+ return;
+ }
+ if ((!options.create || options.create === false) && folderEntry && folderEntry.isFile) {
+ // If create is not true and the path exists, but is a file, getDirectory
+ // must fail.
+ if (errorCallback) {
+ errorCallback(FileError.TYPE_MISMATCH_ERR);
+ }
+ return;
+ }
+
+ // Otherwise, if no other error occurs, getDirectory must return a
+ // MyDirectoryEntry corresponding to path.
+
+ // IDB won't' save methods, so we need re-create MyDirectoryEntry.
+ successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.filesystem));
+ }, errorCallback);
+ };
+
+ exports.getParent = function (successCallback, errorCallback, args) {
+ if (typeof successCallback !== 'function') {
+ throw Error('Expected successCallback argument.');
+ }
+
+ var fullPath = args[0];
+ // fullPath is like this:
+ // file:///persistent/path/to/file or
+ // file:///persistent/path/to/directory/
+
+ if (fullPath === DIR_SEPARATOR || fullPath === pathsPrefix.cacheDirectory ||
+ fullPath === pathsPrefix.dataDirectory) {
+ successCallback(fs_.root);
+ return;
+ }
+
+ // To delete all slashes at the end
+ while (fullPath[fullPath.length - 1] === '/') {
+ fullPath = fullPath.substr(0, fullPath.length - 1);
+ }
+
+ var pathArr = fullPath.split(DIR_SEPARATOR);
+ pathArr.pop();
+ var parentName = pathArr.pop();
+ var path = pathArr.join(DIR_SEPARATOR) + DIR_SEPARATOR;
+
+ // To get parent of root files
+ var joined = path + parentName + DIR_SEPARATOR;// is like this: file:///persistent/
+ if (joined === pathsPrefix.cacheDirectory || joined === pathsPrefix.dataDirectory) {
+ exports.getDirectory(successCallback, errorCallback, [joined, DIR_SEPARATOR, {create: false}]);
+ return;
+ }
+
+ exports.getDirectory(successCallback, errorCallback, [path, parentName, {create: false}]);
+ };
+
+ exports.copyTo = function (successCallback, errorCallback, args) {
+ var srcPath = args[0];
+ var parentFullPath = args[1];
+ var name = args[2];
+
+ if (name.indexOf('/') !== -1 || srcPath === parentFullPath + name) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+
+ return;
+ }
+
+ // Read src file
+ exports.getFile(function (srcFileEntry) {
+
+ var path = resolveToFullPath_(parentFullPath);
+ // Check directory
+ exports.getDirectory(function () {
+
+ // Create dest file
+ exports.getFile(function (dstFileEntry) {
+
+ exports.write(function () {
+ successCallback(dstFileEntry);
+ }, errorCallback, [dstFileEntry.file_.storagePath, srcFileEntry.file_.blob_, 0]);
+
+ }, errorCallback, [parentFullPath, name, {create: true}]);
+
+ }, function () { if (errorCallback) { errorCallback(FileError.NOT_FOUND_ERR); } },
+ [path.storagePath, null, {create: false}]);
+
+ }, errorCallback, [srcPath, null]);
+ };
+
+ exports.moveTo = function (successCallback, errorCallback, args) {
+ var srcPath = args[0];
+ // parentFullPath and name parameters is ignored because
+ // args is being passed downstream to exports.copyTo method
+ var parentFullPath = args[1]; // eslint-disable-line
+ var name = args[2]; // eslint-disable-line
+
+ exports.copyTo(function (fileEntry) {
+
+ exports.remove(function () {
+ successCallback(fileEntry);
+ }, errorCallback, [srcPath]);
+
+ }, errorCallback, args);
+ };
+
+ exports.resolveLocalFileSystemURI = function (successCallback, errorCallback, args) {
+ var path = args[0];
+
+ // Ignore parameters
+ if (path.indexOf('?') !== -1) {
+ path = String(path).split('?')[0];
+ }
+
+ // support for encodeURI
+ if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape
+ path = decodeURI(path);
+ }
+
+ if (path.trim()[0] === '/') {
+ if (errorCallback) {
+ errorCallback(FileError.ENCODING_ERR);
+ }
+ return;
+ }
+
+ // support for cdvfile
+ if (path.trim().substr(0, 7) === 'cdvfile') {
+ if (path.indexOf('cdvfile://localhost') === -1) {
+ if (errorCallback) {
+ errorCallback(FileError.ENCODING_ERR);
+ }
+ return;
+ }
+
+ var indexPersistent = path.indexOf('persistent');
+ var indexTemporary = path.indexOf('temporary');
+
+ // cdvfile://localhost/persistent/path/to/file
+ if (indexPersistent !== -1) {
+ path = 'file:///persistent' + path.substr(indexPersistent + 10);
+ } else if (indexTemporary !== -1) {
+ path = 'file:///temporary' + path.substr(indexTemporary + 9);
+ } else {
+ if (errorCallback) {
+ errorCallback(FileError.ENCODING_ERR);
+ }
+ return;
+ }
+ }
+
+ // to avoid path form of '///path/to/file'
+ function handlePathSlashes (path) {
+ var cutIndex = 0;
+ for (var i = 0; i < path.length - 1; i++) {
+ if (path[i] === DIR_SEPARATOR && path[i + 1] === DIR_SEPARATOR) {
+ cutIndex = i + 1;
+ } else break;
+ }
+
+ return path.substr(cutIndex);
+ }
+
+ // Handle localhost containing paths (see specs )
+ if (path.indexOf('file://localhost/') === 0) {
+ path = path.replace('file://localhost/', 'file:///');
+ }
+
+ if (path.indexOf(pathsPrefix.dataDirectory) === 0) {
+ path = path.substring(pathsPrefix.dataDirectory.length - 1);
+ path = handlePathSlashes(path);
+
+ exports.requestFileSystem(function () {
+ exports.getFile(successCallback, function () {
+ exports.getDirectory(successCallback, errorCallback, [pathsPrefix.dataDirectory, path,
+ {create: false}]);
+ }, [pathsPrefix.dataDirectory, path, {create: false}]);
+ }, errorCallback, [LocalFileSystem.PERSISTENT]);
+ } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) {
+ path = path.substring(pathsPrefix.cacheDirectory.length - 1);
+ path = handlePathSlashes(path);
+
+ exports.requestFileSystem(function () {
+ exports.getFile(successCallback, function () {
+ exports.getDirectory(successCallback, errorCallback, [pathsPrefix.cacheDirectory, path,
+ {create: false}]);
+ }, [pathsPrefix.cacheDirectory, path, {create: false}]);
+ }, errorCallback, [LocalFileSystem.TEMPORARY]);
+ } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) {
+ path = path.substring(pathsPrefix.applicationDirectory.length);
+ // TODO: need to cut out redundant slashes?
+
+ var xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
+ xhr.open('GET', path, true);
+ xhr.onreadystatechange = function () {
+ if (xhr.status === 200 && xhr.readyState === 4) {
+ exports.requestFileSystem(function (fs) {
+ fs.name = location.hostname; // eslint-disable-line no-undef
+
+ // TODO: need to call exports.getFile(...) to handle errors correct
+ fs.root.getFile(path, {create: true}, writeFile, errorCallback);
+ }, errorCallback, [LocalFileSystem.PERSISTENT]);
+ }
+ };
+
+ xhr.onerror = function () {
+ if (errorCallback) {
+ errorCallback(FileError.NOT_READABLE_ERR);
+ }
+ };
+
+ xhr.send();
+ } else {
+ if (errorCallback) {
+ errorCallback(FileError.NOT_FOUND_ERR);
+ }
+ }
+
+ function writeFile (entry) {
+ entry.createWriter(function (fileWriter) {
+ fileWriter.onwriteend = function (evt) {
+ if (!evt.target.error) {
+ entry.filesystemName = location.hostname; // eslint-disable-line no-undef
+ successCallback(entry);
+ }
+ };
+ fileWriter.onerror = function () {
+ if (errorCallback) {
+ errorCallback(FileError.NOT_READABLE_ERR);
+ }
+ };
+ fileWriter.write(new Blob([xhr.response])); // eslint-disable-line no-undef
+ }, errorCallback); // eslint-disable-line no-undef
+ }
+ };
+
+ exports.requestAllPaths = function (successCallback) {
+ successCallback(pathsPrefix);
+ };
+
+ /** * Helpers ***/
+
+ /**
+ * Interface to wrap the native File interface.
+ *
+ * This interface is necessary for creating zero-length (empty) files,
+ * something the Filesystem API allows you to do. Unfortunately, File's
+ * constructor cannot be called directly, making it impossible to instantiate
+ * an empty File in JS.
+ *
+ * @param {Object} opts Initial values.
+ * @constructor
+ */
+ function MyFile (opts) {
+ var blob_ = new Blob(); // eslint-disable-line no-undef
+
+ this.size = opts.size || 0;
+ this.name = opts.name || '';
+ this.type = opts.type || '';
+ this.lastModifiedDate = opts.lastModifiedDate || null;
+ this.storagePath = opts.storagePath || '';
+
+ // Need some black magic to correct the object's size/name/type based on the
+ // blob that is saved.
+ Object.defineProperty(this, 'blob_', {
+ enumerable: true,
+ get: function () {
+ return blob_;
+ },
+ set: function (val) {
+ blob_ = val;
+ this.size = blob_.size;
+ this.name = blob_.name;
+ this.type = blob_.type;
+ this.lastModifiedDate = blob_.lastModifiedDate;
+ }.bind(this)
+ });
+ }
+
+ MyFile.prototype.constructor = MyFile;
+
+ var MyFileHelper = {
+ toJson: function (myFile, success) {
+ /*
+ Safari private browse mode cannot store Blob object to indexeddb.
+ Then use pure json object instead of Blob object.
+ */
+ var fr = new FileReader();
+ fr.onload = function (ev) {
+ var base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(fr.result)));
+ success({
+ opt: {
+ size: myFile.size,
+ name: myFile.name,
+ type: myFile.type,
+ lastModifiedDate: myFile.lastModifiedDate,
+ storagePath: myFile.storagePath
+ },
+ base64: base64
+ });
+ };
+ fr.readAsArrayBuffer(myFile.blob_);
+ },
+ setBase64: function (myFile, base64) {
+ if (base64) {
+ var arrayBuffer = (new Uint8Array(
+ [].map.call(atob(base64), function (c) { return c.charCodeAt(0); })
+ )).buffer;
+
+ myFile.blob_ = new Blob([arrayBuffer], { type: myFile.type });
+ } else {
+ myFile.blob_ = new Blob();
+ }
+ }
+ };
+
+ // When saving an entry, the fullPath should always lead with a slash and never
+ // end with one (e.g. a directory). Also, resolve '.' and '..' to an absolute
+ // one. This method ensures path is legit!
+ function resolveToFullPath_ (cwdFullPath, path) {
+ path = path || '';
+ var fullPath = path;
+ var prefix = '';
+
+ cwdFullPath = cwdFullPath || DIR_SEPARATOR;
+ if (cwdFullPath.indexOf(FILESYSTEM_PREFIX) === 0) {
+ prefix = cwdFullPath.substring(0, cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
+ cwdFullPath = cwdFullPath.substring(cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
+ }
+
+ var relativePath = path[0] !== DIR_SEPARATOR;
+ if (relativePath) {
+ fullPath = cwdFullPath;
+ if (cwdFullPath !== DIR_SEPARATOR) {
+ fullPath += DIR_SEPARATOR + path;
+ } else {
+ fullPath += path;
+ }
+ }
+
+ // Remove doubled separator substrings
+ var re = new RegExp(DIR_SEPARATOR + DIR_SEPARATOR, 'g');
+ fullPath = fullPath.replace(re, DIR_SEPARATOR);
+
+ // Adjust '..'s by removing parent directories when '..' flows in path.
+ var parts = fullPath.split(DIR_SEPARATOR);
+ for (var i = 0; i < parts.length; ++i) {
+ var part = parts[i];
+ if (part === '..') {
+ parts[i - 1] = '';
+ parts[i] = '';
+ }
+ }
+ fullPath = parts.filter(function (el) {
+ return el;
+ }).join(DIR_SEPARATOR);
+
+ // Add back in leading slash.
+ if (fullPath[0] !== DIR_SEPARATOR) {
+ fullPath = DIR_SEPARATOR + fullPath;
+ }
+
+ // Replace './' by current dir. ('./one/./two' -> one/two)
+ fullPath = fullPath.replace(/\.\//g, DIR_SEPARATOR);
+
+ // Replace '//' with '/'.
+ fullPath = fullPath.replace(/\/\//g, DIR_SEPARATOR);
+
+ // Replace '/.' with '/'.
+ fullPath = fullPath.replace(/\/\./g, DIR_SEPARATOR);
+
+ // Remove '/' if it appears on the end.
+ if (fullPath[fullPath.length - 1] === DIR_SEPARATOR &&
+ fullPath !== DIR_SEPARATOR) {
+ fullPath = fullPath.substring(0, fullPath.length - 1);
+ }
+
+ var storagePath = prefix + fullPath;
+ storagePath = decodeURI(storagePath);
+ fullPath = decodeURI(fullPath);
+
+ return {
+ storagePath: storagePath,
+ fullPath: fullPath,
+ fileName: fullPath.split(DIR_SEPARATOR).pop(),
+ fsName: prefix.split(DIR_SEPARATOR).pop()
+ };
+ }
+
+ function fileEntryFromIdbEntry (fileEntry) {
+ // IDB won't save methods, so we need re-create the FileEntry.
+ var clonedFileEntry = new FileEntry(fileEntry.name, fileEntry.fullPath, fileEntry.filesystem);
+ clonedFileEntry.file_ = fileEntry.file_;
+
+ return clonedFileEntry;
+ }
+
+ function readAs (what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) {
+ exports.getFile(function (fileEntry) {
+ var fileReader = new FileReader(); // eslint-disable-line no-undef
+ var blob = fileEntry.file_.blob_.slice(startPos, endPos);
+
+ fileReader.onload = function (e) {
+ successCallback(e.target.result);
+ };
+
+ fileReader.onerror = errorCallback;
+
+ switch (what) {
+ case 'text':
+ fileReader.readAsText(blob, encoding);
+ break;
+ case 'dataURL':
+ fileReader.readAsDataURL(blob);
+ break;
+ case 'arrayBuffer':
+ fileReader.readAsArrayBuffer(blob);
+ break;
+ case 'binaryString':
+ fileReader.readAsBinaryString(blob);
+ break;
+ }
+
+ }, errorCallback, [fullPath, null]);
+ }
+
+ /** * Core logic to handle IDB operations ***/
+
+ idb_.open = function (dbName, successCallback, errorCallback) {
+ var self = this;
+
+ // TODO: FF 12.0a1 isn't liking a db name with : in it.
+ var request = indexedDB.open(dbName.replace(':', '_')/*, 1 /*version */);
+
+ request.onerror = errorCallback || onError;
+
+ request.onupgradeneeded = function (e) {
+ // First open was called or higher db version was used.
+
+ // console.log('onupgradeneeded: oldVersion:' + e.oldVersion,
+ // 'newVersion:' + e.newVersion);
+
+ self.db = e.target.result;
+ self.db.onerror = onError;
+
+ if (!self.db.objectStoreNames.contains(FILE_STORE_)) {
+ self.db.createObjectStore(FILE_STORE_/*, {keyPath: 'id', autoIncrement: true} */);
+ }
+ };
+
+ request.onsuccess = function (e) {
+ self.db = e.target.result;
+ self.db.onerror = onError;
+ successCallback(e);
+ };
+
+ request.onblocked = errorCallback || onError;
+ };
+
+ idb_.close = function () {
+ this.db.close();
+ this.db = null;
+ };
+
+ idb_.get = function (fullPath, successCallback, errorCallback) {
+ if (!this.db) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ return;
+ }
+
+ var tx = this.db.transaction([FILE_STORE_], 'readonly');
+
+ var request = tx.objectStore(FILE_STORE_).get(fullPath);
+
+ tx.onabort = errorCallback || onError;
+ tx.oncomplete = function () {
+ var entry = request.result;
+ if (entry && entry.file_json) {
+ /*
+ Safari private browse mode cannot store Blob object to indexeddb.
+ Then use pure json object instead of Blob object.
+ */
+ entry.file_ = new MyFile(entry.file_json.opt);
+ MyFileHelper.setBase64(entry.file_, entry.file_json.base64);
+ delete entry.file_json;
+ }
+ successCallback(entry);
+ };
+ };
+
+ idb_.getAllEntries = function (fullPath, storagePath, successCallback, errorCallback) {
+ if (!this.db) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ return;
+ }
+
+ var results = [];
+
+ if (storagePath[storagePath.length - 1] === DIR_SEPARATOR) {
+ storagePath = storagePath.substring(0, storagePath.length - 1);
+ }
+
+ var range = IDBKeyRange.bound(storagePath + DIR_SEPARATOR + ' ',
+ storagePath + DIR_SEPARATOR + String.fromCharCode(unicodeLastChar));
+
+ var tx = this.db.transaction([FILE_STORE_], 'readonly');
+ tx.onabort = errorCallback || onError;
+ tx.oncomplete = function () {
+ results = results.filter(function (val) {
+ var pathWithoutSlash = val.fullPath;
+
+ if (val.fullPath[val.fullPath.length - 1] === DIR_SEPARATOR) {
+ pathWithoutSlash = pathWithoutSlash.substr(0, pathWithoutSlash.length - 1);
+ }
+
+ var valPartsLen = pathWithoutSlash.split(DIR_SEPARATOR).length;
+ var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length;
+
+ /* Input fullPath parameter equals '//' for root folder */
+ /* Entries in root folder has valPartsLen equals 2 (see below) */
+ if (fullPath[fullPath.length - 1] === DIR_SEPARATOR && fullPath.trim().length === 2) {
+ fullPathPartsLen = 1;
+ } else if (fullPath[fullPath.length - 1] === DIR_SEPARATOR) {
+ fullPathPartsLen = fullPath.substr(0, fullPath.length - 1).split(DIR_SEPARATOR).length;
+ } else {
+ fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length;
+ }
+
+ if (valPartsLen === fullPathPartsLen + 1) {
+ // If this a subfolder and entry is a direct child, include it in
+ // the results. Otherwise, it's not an entry of this folder.
+ return val;
+ } else return false;
+ });
+
+ successCallback(results);
+ };
+
+ var request = tx.objectStore(FILE_STORE_).openCursor(range);
+
+ request.onsuccess = function (e) {
+ var cursor = e.target.result;
+ if (cursor) {
+ var val = cursor.value;
+
+ results.push(val.isFile ? fileEntryFromIdbEntry(val) : new DirectoryEntry(val.name, val.fullPath, val.filesystem));
+ cursor['continue']();
+ }
+ };
+ };
+
+ idb_['delete'] = function (fullPath, successCallback, errorCallback, isDirectory) {
+ if (!idb_.db) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ return;
+ }
+
+ var tx = this.db.transaction([FILE_STORE_], 'readwrite');
+ tx.oncomplete = successCallback;
+ tx.onabort = errorCallback || onError;
+ tx.oncomplete = function () {
+ if (isDirectory) {
+ // We delete nested files and folders after deleting parent folder
+ // We use ranges: https://developer.mozilla.org/en-US/docs/Web/API/IDBKeyRange
+ fullPath = fullPath + DIR_SEPARATOR;
+
+ // Range contains all entries in the form fullPath<symbol> where
+ // symbol in the range from ' ' to symbol which has code `unicodeLastChar`
+ var range = IDBKeyRange.bound(fullPath + ' ', fullPath + String.fromCharCode(unicodeLastChar));
+
+ var newTx = this.db.transaction([FILE_STORE_], 'readwrite');
+ newTx.oncomplete = successCallback;
+ newTx.onabort = errorCallback || onError;
+ newTx.objectStore(FILE_STORE_)['delete'](range);
+ } else {
+ successCallback();
+ }
+ };
+ tx.objectStore(FILE_STORE_)['delete'](fullPath);
+ };
+
+ idb_.put = function (entry, storagePath, successCallback, errorCallback, retry) {
+ if (!this.db) {
+ if (errorCallback) {
+ errorCallback(FileError.INVALID_MODIFICATION_ERR);
+ }
+ return;
+ }
+
+ var tx = this.db.transaction([FILE_STORE_], 'readwrite');
+ tx.onabort = errorCallback || onError;
+ tx.oncomplete = function () {
+ // TODO: Error is thrown if we pass the request event back instead.
+ successCallback(entry);
+ };
+
+ try {
+ tx.objectStore(FILE_STORE_).put(entry, storagePath);
+ } catch (e) {
+ if (e.name === 'DataCloneError') {
+ tx.oncomplete = null;
+ /*
+ Safari private browse mode cannot store Blob object to indexeddb.
+ Then use pure json object instead of Blob object.
+ */
+
+ var successCallback2 = function (entry) {
+ entry.file_ = new MyFile(entry.file_json.opt);
+ delete entry.file_json;
+ successCallback(entry);
+ };
+
+ if (!retry) {
+ if (entry.file_ && entry.file_ instanceof MyFile && entry.file_.blob_) {
+ MyFileHelper.toJson(entry.file_, function (json) {
+ entry.file_json = json;
+ delete entry.file_;
+ idb_.put(entry, storagePath, successCallback2, errorCallback, true);
+ });
+ return;
+ }
+ }
+ }
+ throw e;
+ }
+ };
+
+ // Global error handler. Errors bubble from request, to transaction, to db.
+ function onError (e) {
+ switch (e.target.errorCode) {
+ case 12:
+ console.log('Error - Attempt to open db with a lower version than the ' +
+ 'current one.');
+ break;
+ default:
+ console.log('errorCode: ' + e.target.errorCode);
+ }
+
+ console.log(e, e.code, e.message);
+ }
+
+ })(module.exports, window);
+
+ require('cordova/exec/proxy').add('File', module.exports);
+})();
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h
new file mode 100644
index 00000000..e09e2250
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h
@@ -0,0 +1,30 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+
+extern NSString* const kCDVAssetsLibraryPrefix;
+extern NSString* const kCDVAssetsLibraryScheme;
+
+@interface CDVAssetLibraryFilesystem : NSObject<CDVFileSystem> {
+}
+
+- (id) initWithName:(NSString *)name;
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m
new file mode 100644
index 00000000..8486b7be
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m
@@ -0,0 +1,253 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+#import "CDVAssetLibraryFilesystem.h"
+#import <Cordova/CDV.h>
+#import <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+
+NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
+NSString* const kCDVAssetsLibraryScheme = @"assets-library";
+
+@implementation CDVAssetLibraryFilesystem
+@synthesize name=_name, urlTransformer;
+
+
+/*
+ The CDVAssetLibraryFilesystem works with resources which are identified
+ by iOS as
+ asset-library://<path>
+ and represents them internally as URLs of the form
+ cdvfile://localhost/assets-library/<path>
+ */
+
+- (NSURL *)assetLibraryURLForLocalURL:(CDVFilesystemURL *)url
+{
+ if ([url.url.scheme isEqualToString:kCDVFilesystemURLPrefix]) {
+ NSString *path = [[url.url absoluteString] substringFromIndex:[@"cdvfile://localhost/assets-library" length]];
+ return [NSURL URLWithString:[NSString stringWithFormat:@"assets-library:/%@", path]];
+ }
+ return url.url;
+}
+
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
+{
+ NSDictionary* entry = [self makeEntryForLocalURL:url];
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry];
+}
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url {
+ return [self makeEntryForPath:url.fullPath isDirectory:NO];
+}
+
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir
+{
+ NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+ NSString* lastPart = [fullPath lastPathComponent];
+ if (isDir && ![fullPath hasSuffix:@"/"]) {
+ fullPath = [fullPath stringByAppendingString:@"/"];
+ }
+ [dirEntry setObject:[NSNumber numberWithBool:!isDir] forKey:@"isFile"];
+ [dirEntry setObject:[NSNumber numberWithBool:isDir] forKey:@"isDirectory"];
+ [dirEntry setObject:fullPath forKey:@"fullPath"];
+ [dirEntry setObject:lastPart forKey:@"name"];
+ [dirEntry setObject:self.name forKey: @"filesystemName"];
+
+ NSURL* nativeURL = [NSURL URLWithString:[NSString stringWithFormat:@"assets-library:/%@",fullPath]];
+ if (self.urlTransformer) {
+ nativeURL = self.urlTransformer(nativeURL);
+ }
+ dirEntry[@"nativeURL"] = [nativeURL absoluteString];
+
+ return dirEntry;
+}
+
+/* helper function to get the mimeType from the file extension
+ * IN:
+ * NSString* fullPath - filename (may include path)
+ * OUT:
+ * NSString* the mime type as type/subtype. nil if not able to determine
+ */
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+ NSString* mimeType = nil;
+
+ if (fullPath) {
+ CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+ if (typeId) {
+ mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+ if (!mimeType) {
+ // special case for m4a
+ if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
+ mimeType = @"audio/mp4";
+ } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+ mimeType = @"audio/wav";
+ } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
+ mimeType = @"text/css";
+ }
+ }
+ CFRelease(typeId);
+ }
+ }
+ return mimeType;
+}
+
+- (id)initWithName:(NSString *)name
+{
+ if (self) {
+ self.name = name;
+ }
+ return self;
+}
+
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+ // return unsupported result for assets-library URLs
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+ // we don't (yet?) support getting the parent of an asset
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+ // setMetadata doesn't make sense for asset library files
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+ // return error for assets-library URLs
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+ // return error for assets-library URLs
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+ // return unsupported result for assets-library URLs
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+ // assets-library files can't be truncated
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+ // text can't be written into assets-library files
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+ // Copying to an assets library file is not doable, since we can't write it.
+ CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+ callback(result);
+}
+
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)url
+{
+ NSString *path = nil;
+ if ([[url.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+ path = [url.url path];
+ } else {
+ path = url.fullPath;
+ }
+ if ([path hasSuffix:@"/"]) {
+ path = [path substringToIndex:([path length]-1)];
+ }
+ return path;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+ if (asset) {
+ // We have the asset! Get the data and send it off.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ NSUInteger size = (end > start) ? (end - start) : [assetRepresentation size];
+ Byte* buffer = (Byte*)malloc(size);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:start length:size error:nil];
+ NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
+
+ callback(data, MIMEType, NO_ERROR);
+ } else {
+ callback(nil, nil, NOT_FOUND_ERR);
+ }
+ };
+
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ NSLog(@"Error: %@", error);
+ callback(nil, nil, SECURITY_ERR);
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[self assetLibraryURLForLocalURL:localURL] resultBlock:resultBlock failureBlock:failureBlock];
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+ // In this case, we need to use an asynchronous method to retrieve the file.
+ // Because of this, we can't just assign to `result` and send it at the end of the method.
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+ if (asset) {
+ // We have the asset! Populate the dictionary and send it off.
+ NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
+ [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+ NSString* filename = [assetRepresentation filename];
+ [fileInfo setObject:filename forKey:@"name"];
+ [fileInfo setObject:[CDVAssetLibraryFilesystem getMimeTypeFromPath:filename] forKey:@"type"];
+ NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
+ NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
+ [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+
+ callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]);
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[self assetLibraryURLForLocalURL:localURL] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
+}
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.h b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.h
new file mode 100644
index 00000000..987c66b5
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.h
@@ -0,0 +1,157 @@
+/*
+ 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.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Cordova/CDVPlugin.h>
+
+extern NSString* const kCDVAssetsLibraryPrefix;
+extern NSString* const kCDVFilesystemURLPrefix;
+
+enum CDVFileError {
+ NO_ERROR = 0,
+ NOT_FOUND_ERR = 1,
+ SECURITY_ERR = 2,
+ ABORT_ERR = 3,
+ NOT_READABLE_ERR = 4,
+ ENCODING_ERR = 5,
+ NO_MODIFICATION_ALLOWED_ERR = 6,
+ INVALID_STATE_ERR = 7,
+ SYNTAX_ERR = 8,
+ INVALID_MODIFICATION_ERR = 9,
+ QUOTA_EXCEEDED_ERR = 10,
+ TYPE_MISMATCH_ERR = 11,
+ PATH_EXISTS_ERR = 12
+};
+typedef int CDVFileError;
+
+@interface CDVFilesystemURL : NSObject {
+ NSURL *_url;
+ NSString *_fileSystemName;
+ NSString *_fullPath;
+}
+
+- (id) initWithString:(NSString*)strURL;
+- (id) initWithURL:(NSURL*)URL;
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL;
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL;
+
+- (NSString *)absoluteURL;
+
+@property (atomic) NSURL *url;
+@property (atomic) NSString *fileSystemName;
+@property (atomic) NSString *fullPath;
+
+@end
+
+@interface CDVFilesystemURLProtocol : NSURLProtocol
+@end
+
+@protocol CDVFileSystem
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options;
+- (CDVPluginResult *)getParentForURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options;
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos;
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend;
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback;
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback;
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback;
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url;
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir;
+
+@property (nonatomic,strong) NSString *name;
+@property (nonatomic, copy) NSURL*(^urlTransformer)(NSURL*);
+
+@optional
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURI;
+- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path;
+
+@end
+
+@interface CDVFile : CDVPlugin {
+ NSString* rootDocsPath;
+ NSString* appDocsPath;
+ NSString* appLibraryPath;
+ NSString* appTempPath;
+
+ NSMutableArray* fileSystems_;
+ BOOL userHasAllowed;
+}
+
+- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath;
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir;
+- (NSDictionary *)makeEntryForURL:(NSURL *)URL;
+- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath;
+
+- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL;
+
+/* Native Registration API */
+- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs;
+- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName;
+
+/* Exec API */
+- (void)requestFileSystem:(CDVInvokedUrlCommand*)command;
+- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command;
+- (void)getDirectory:(CDVInvokedUrlCommand*)command;
+- (void)getFile:(CDVInvokedUrlCommand*)command;
+- (void)getParent:(CDVInvokedUrlCommand*)command;
+- (void)removeRecursively:(CDVInvokedUrlCommand*)command;
+- (void)remove:(CDVInvokedUrlCommand*)command;
+- (void)copyTo:(CDVInvokedUrlCommand*)command;
+- (void)moveTo:(CDVInvokedUrlCommand*)command;
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command;
+- (void)readEntries:(CDVInvokedUrlCommand*)command;
+- (void)readAsText:(CDVInvokedUrlCommand*)command;
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command;
+- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command;
+- (void)write:(CDVInvokedUrlCommand*)command;
+- (void)testFileExists:(CDVInvokedUrlCommand*)command;
+- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command;
+- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command;
+- (void)truncate:(CDVInvokedUrlCommand*)command;
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy;
+
+/* Compatibilty with older File API */
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+- (NSDictionary *)getDirectoryEntry:(NSString *)target isDirectory:(BOOL)bDirRequest;
+
+/* Conversion between filesystem paths and URLs */
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)URL;
+
+/* Internal methods for testing */
+- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command;
+
+@property (nonatomic, strong) NSString* rootDocsPath;
+@property (nonatomic, strong) NSString* appDocsPath;
+@property (nonatomic, strong) NSString* appLibraryPath;
+@property (nonatomic, strong) NSString* appTempPath;
+@property (nonatomic, strong) NSString* persistentPath;
+@property (nonatomic, strong) NSString* temporaryPath;
+@property (nonatomic, strong) NSMutableArray* fileSystems;
+
+@property BOOL userHasAllowed;
+
+@end
+
+#define kW3FileTemporary @"temporary"
+#define kW3FilePersistent @"persistent"
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.m b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.m
new file mode 100644
index 00000000..59e7d640
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVFile.m
@@ -0,0 +1,1119 @@
+/*
+ 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.
+ */
+
+#import <Cordova/CDV.h>
+#import "CDVFile.h"
+#import "CDVLocalFilesystem.h"
+#import "CDVAssetLibraryFilesystem.h"
+#import <objc/message.h>
+
+static NSString* toBase64(NSData* data) {
+ SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
+ SEL s2 = NSSelectorFromString(@"base64EncodedString");
+ SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
+
+ if ([data respondsToSelector:s1]) {
+ NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
+ return func(data, s1);
+ } else if ([data respondsToSelector:s2]) {
+ NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
+ return func(data, s2);
+ } else if ([data respondsToSelector:s3]) {
+ NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
+ return func(data, s3, 0);
+ } else {
+ return nil;
+ }
+}
+
+CDVFile *filePlugin = nil;
+
+extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
+
+#ifndef __IPHONE_5_1
+ NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
+#endif
+
+NSString* const kCDVFilesystemURLPrefix = @"cdvfile";
+
+@implementation CDVFilesystemURL
+@synthesize url=_url;
+@synthesize fileSystemName=_fileSystemName;
+@synthesize fullPath=_fullPath;
+
+- (id) initWithString:(NSString *)strURL
+{
+ if ( self = [super init] ) {
+ NSURL *decodedURL = [NSURL URLWithString:strURL];
+ return [self initWithURL:decodedURL];
+ }
+ return nil;
+}
+
+-(id) initWithURL:(NSURL *)URL
+{
+ if ( self = [super init] ) {
+ self.url = URL;
+ self.fileSystemName = [self filesystemNameForLocalURI:URL];
+ self.fullPath = [self fullPathForLocalURI:URL];
+ }
+ return self;
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString FileSystem Name for this URI, or nil if it is not recognized.
+ */
+- (NSString *)filesystemNameForLocalURI:(NSURL *)uri
+{
+ if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
+ NSArray *pathComponents = [uri pathComponents];
+ if (pathComponents != nil && pathComponents.count > 1) {
+ return [pathComponents objectAtIndex:1];
+ }
+ } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+ return @"assets-library";
+ }
+ return nil;
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString fullPath component suitable for an Entry object.
+ * The incoming URI should be properly escaped. The returned fullPath is unescaped.
+ */
+- (NSString *)fullPathForLocalURI:(NSURL *)uri
+{
+ if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
+ NSString *path = [uri path];
+ if ([uri query]) {
+ path = [NSString stringWithFormat:@"%@?%@", path, [uri query]];
+ }
+ NSRange slashRange = [path rangeOfString:@"/" options:0 range:NSMakeRange(1, path.length-1)];
+ if (slashRange.location == NSNotFound) {
+ return @"";
+ }
+ return [path substringFromIndex:slashRange.location];
+ } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+ return [[uri absoluteString] substringFromIndex:[kCDVAssetsLibraryScheme length]+2];
+ }
+ return nil;
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
+{
+ return [[CDVFilesystemURL alloc] initWithString:strURL];
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
+{
+ return [[CDVFilesystemURL alloc] initWithURL:URL];
+}
+
+- (NSString *)absoluteURL
+{
+ return [NSString stringWithFormat:@"cdvfile://localhost/%@%@", self.fileSystemName, self.fullPath];
+}
+
+@end
+
+@implementation CDVFilesystemURLProtocol
+
++ (BOOL)canInitWithRequest:(NSURLRequest*)request
+{
+ NSURL* url = [request URL];
+ return [[url scheme] isEqualToString:kCDVFilesystemURLPrefix];
+}
+
++ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
+{
+ return request;
+}
+
++ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
+{
+ return [[[requestA URL] resourceSpecifier] isEqualToString:[[requestB URL] resourceSpecifier]];
+}
+
+- (void)startLoading
+{
+ CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithURL:[[self request] URL]];
+ NSObject<CDVFileSystem> *fs = [filePlugin filesystemForURL:url];
+ __weak CDVFilesystemURLProtocol* weakSelf = self;
+
+ [fs readFileAtURL:url start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
+ NSMutableDictionary* responseHeaders = [[NSMutableDictionary alloc] init];
+ responseHeaders[@"Cache-Control"] = @"no-cache";
+
+ if (!error) {
+ responseHeaders[@"Content-Type"] = mimetype;
+ NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
+ [[weakSelf client] URLProtocol:weakSelf didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [[weakSelf client] URLProtocol:weakSelf didLoadData:data];
+ [[weakSelf client] URLProtocolDidFinishLoading:weakSelf];
+ } else {
+ NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
+ [[weakSelf client] URLProtocol:weakSelf didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [[weakSelf client] URLProtocolDidFinishLoading:weakSelf];
+ }
+ }];
+}
+
+- (void)stopLoading
+{}
+
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
+ willCacheResponse:(NSCachedURLResponse*)cachedResponse {
+ return nil;
+}
+
+@end
+
+
+@implementation CDVFile
+
+@synthesize rootDocsPath, appDocsPath, appLibraryPath, appTempPath, userHasAllowed, fileSystems=fileSystems_;
+
+- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs {
+ __weak CDVFile* weakSelf = self;
+ SEL sel = NSSelectorFromString(@"urlTransformer");
+ // for backwards compatibility - we check if this property is there
+ // we create a wrapper block because the urlTransformer property
+ // on the commandDelegate might be set dynamically at a future time
+ // (and not dependent on plugin loading order)
+ if ([self.commandDelegate respondsToSelector:sel]) {
+ fs.urlTransformer = ^NSURL*(NSURL* urlToTransform) {
+ // grab the block from the commandDelegate
+ NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(weakSelf.commandDelegate, sel);
+ // if block is not null, we call it
+ if (urlTransformer) {
+ return urlTransformer(urlToTransform);
+ } else { // else we return the same url
+ return urlToTransform;
+ }
+ };
+ }
+ [fileSystems_ addObject:fs];
+}
+
+- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName
+{
+ if (self.fileSystems != nil) {
+ for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
+ if ([fs.name isEqualToString:fsName]) {
+ return fs;
+ }
+ }
+ }
+ return nil;
+}
+
+- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL {
+ if (localURL.fileSystemName == nil) return nil;
+ @try {
+ return [self fileSystemByName:localURL.fileSystemName];
+ }
+ @catch (NSException *e) {
+ return nil;
+ }
+}
+
+- (NSArray *)getExtraFileSystemsPreference:(UIViewController *)vc
+{
+ NSString *filesystemsStr = nil;
+ if([self.viewController isKindOfClass:[CDVViewController class]]) {
+ CDVViewController *vc = (CDVViewController *)self.viewController;
+ NSDictionary *settings = [vc settings];
+ filesystemsStr = [settings[@"iosextrafilesystems"] lowercaseString];
+ }
+ if (!filesystemsStr) {
+ filesystemsStr = @"library,library-nosync,documents,documents-nosync,cache,bundle,root";
+ }
+ return [filesystemsStr componentsSeparatedByString:@","];
+}
+
+- (void)makeNonSyncable:(NSString*)path {
+ [[NSFileManager defaultManager] createDirectoryAtPath:path
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nil];
+ NSURL* url = [NSURL fileURLWithPath:path];
+ [url setResourceValue: [NSNumber numberWithBool: YES]
+ forKey: NSURLIsExcludedFromBackupKey error:nil];
+
+}
+
+- (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDictionary *)availableFileSystems
+{
+ NSMutableSet *installedFilesystems = [[NSMutableSet alloc] initWithCapacity:7];
+
+ /* Build non-syncable directories as necessary */
+ for (NSString *nonSyncFS in @[@"library-nosync", @"documents-nosync"]) {
+ if ([filesystems containsObject:nonSyncFS]) {
+ [self makeNonSyncable:availableFileSystems[nonSyncFS]];
+ }
+ }
+
+ /* Register filesystems in order */
+ for (NSString *fsName in filesystems) {
+ if (![installedFilesystems containsObject:fsName]) {
+ NSString *fsRoot = availableFileSystems[fsName];
+ if (fsRoot) {
+ [filePlugin registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:fsName root:fsRoot]];
+ [installedFilesystems addObject:fsName];
+ } else {
+ NSLog(@"Unrecognized extra filesystem identifier: %@", fsName);
+ }
+ }
+ }
+}
+
+- (NSDictionary *)getAvailableFileSystems
+{
+ NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ return @{
+ @"library": libPath,
+ @"library-nosync": [libPath stringByAppendingPathComponent:@"NoCloud"],
+ @"documents": docPath,
+ @"documents-nosync": [docPath stringByAppendingPathComponent:@"NoCloud"],
+ @"cache": [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0],
+ @"bundle": [[NSBundle mainBundle] bundlePath],
+ @"root": @"/"
+ };
+}
+
+- (void)pluginInitialize
+{
+ filePlugin = self;
+ [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
+
+ fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3];
+
+ // Get the Library directory path
+ NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+ self.appLibraryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"files"];
+
+ // Get the Temporary directory path
+ self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath]; // remove trailing slash from NSTemporaryDirectory()
+
+ // Get the Documents directory path
+ paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ self.rootDocsPath = [paths objectAtIndex:0];
+ self.appDocsPath = [self.rootDocsPath stringByAppendingPathComponent:@"files"];
+
+
+ NSString *location = nil;
+ if([self.viewController isKindOfClass:[CDVViewController class]]) {
+ CDVViewController *vc = (CDVViewController *)self.viewController;
+ NSMutableDictionary *settings = vc.settings;
+ location = [[settings objectForKey:@"iospersistentfilelocation"] lowercaseString];
+ }
+ if (location == nil) {
+ // Compatibilty by default (if the config preference is not set, or
+ // if we're not embedded in a CDVViewController somehow.)
+ location = @"compatibility";
+ }
+
+ NSError *error;
+ if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appTempPath
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error]) {
+ [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]];
+ } else {
+ NSLog(@"Unable to create temporary directory: %@", error);
+ }
+ if ([location isEqualToString:@"library"]) {
+ if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appLibraryPath
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error]) {
+ [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appLibraryPath]];
+ } else {
+ NSLog(@"Unable to create library directory: %@", error);
+ }
+ } else if ([location isEqualToString:@"compatibility"]) {
+ /*
+ * Fall-back to compatibility mode -- this is the logic implemented in
+ * earlier versions of this plugin, and should be maintained here so
+ * that apps which were originally deployed with older versions of the
+ * plugin can continue to provide access to files stored under those
+ * versions.
+ */
+ [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.rootDocsPath]];
+ } else {
+ NSAssert(false,
+ @"File plugin configuration error: Please set iosPersistentFileLocation in config.xml to one of \"library\" (for new applications) or \"compatibility\" (for compatibility with previous versions)");
+ }
+ [self registerFilesystem:[[CDVAssetLibraryFilesystem alloc] initWithName:@"assets-library"]];
+
+ [self registerExtraFileSystems:[self getExtraFileSystemsPreference:self.viewController]
+ fromAvailableSet:[self getAvailableFileSystems]];
+
+}
+
+- (CDVFilesystemURL *)fileSystemURLforArg:(NSString *)urlArg
+{
+ CDVFilesystemURL* ret = nil;
+ if ([urlArg hasPrefix:@"file://"]) {
+ /* This looks like a file url. Get the path, and see if any handlers recognize it. */
+ NSURL *fileURL = [NSURL URLWithString:urlArg];
+ NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath];
+ NSString *path = [resolvedFileURL path];
+ ret = [self fileSystemURLforLocalPath:path];
+ } else {
+ ret = [CDVFilesystemURL fileSystemURLWithString:urlArg];
+ }
+ return ret;
+}
+
+- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath
+{
+ CDVFilesystemURL *localURL = nil;
+ NSUInteger shortestFullPath = 0;
+
+ // Try all installed filesystems, in order. Return the most match url.
+ for (id object in self.fileSystems) {
+ if ([object respondsToSelector:@selector(URLforFilesystemPath:)]) {
+ CDVFilesystemURL *url = [object URLforFilesystemPath:localPath];
+ if (url){
+ // A shorter fullPath would imply that the filesystem is a better match for the local path
+ if (!localURL || ([[url fullPath] length] < shortestFullPath)) {
+ localURL = url;
+ shortestFullPath = [[url fullPath] length];
+ }
+ }
+ }
+ }
+ return localURL;
+}
+
+- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath
+{
+ NSFileManager* fMgr = [[NSFileManager alloc] init];
+
+ NSError* __autoreleasing pError = nil;
+
+ NSDictionary* pDict = [fMgr attributesOfFileSystemForPath:appPath error:&pError];
+ NSNumber* pNumAvail = (NSNumber*)[pDict objectForKey:NSFileSystemFreeSize];
+
+ return pNumAvail;
+}
+
+/* Request the File System info
+ *
+ * IN:
+ * arguments[0] - type (number as string)
+ * TEMPORARY = 0, PERSISTENT = 1;
+ * arguments[1] - size
+ *
+ * OUT:
+ * Dictionary representing FileSystem object
+ * name - the human readable directory name
+ * root = DirectoryEntry object
+ * bool isDirectory
+ * bool isFile
+ * string name
+ * string fullPath
+ * fileSystem = FileSystem object - !! ignored because creates circular reference !!
+ */
+
+- (void)requestFileSystem:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* strType = [command argumentAtIndex:0];
+ unsigned long long size = [[command argumentAtIndex:1] longLongValue];
+
+ int type = [strType intValue];
+ CDVPluginResult* result = nil;
+
+ if (type >= self.fileSystems.count) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+ NSLog(@"No filesystem of type requested");
+ } else {
+ NSString* fullPath = @"/";
+ // check for avail space for size request
+ NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
+ // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
+ if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR];
+ } else {
+ NSObject<CDVFileSystem> *rootFs = [self.fileSystems objectAtIndex:type];
+ if (rootFs == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+ NSLog(@"No filesystem of type requested");
+ } else {
+ NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2];
+ [fileSystem setObject:rootFs.name forKey:@"name"];
+ NSDictionary* dirEntry = [self makeEntryForPath:fullPath fileSystemName:rootFs.name isDirectory:YES];
+ [fileSystem setObject:dirEntry forKey:@"root"];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+ }
+ }
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+
+- (void)requestAllFileSystems:(CDVInvokedUrlCommand*)command
+{
+ NSMutableArray* ret = [[NSMutableArray alloc] init];
+ for (NSObject<CDVFileSystem>* root in fileSystems_) {
+ [ret addObject:[self makeEntryForPath:@"/" fileSystemName:root.name isDirectory:YES]];
+ }
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:ret];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)requestAllPaths:(CDVInvokedUrlCommand*)command
+{
+ NSString* libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0];
+ NSString* libPathSync = [libPath stringByAppendingPathComponent:@"Cloud"];
+ NSString* libPathNoSync = [libPath stringByAppendingPathComponent:@"NoCloud"];
+ NSString* docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+ NSString* storagePath = [libPath stringByDeletingLastPathComponent];
+ NSString* cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
+
+ // Create the directories if necessary.
+ [[NSFileManager defaultManager] createDirectoryAtPath:libPathSync withIntermediateDirectories:YES attributes:nil error:nil];
+ [[NSFileManager defaultManager] createDirectoryAtPath:libPathNoSync withIntermediateDirectories:YES attributes:nil error:nil];
+ // Mark NoSync as non-iCloud.
+ [[NSURL fileURLWithPath:libPathNoSync] setResourceValue: [NSNumber numberWithBool: YES]
+ forKey: NSURLIsExcludedFromBackupKey error:nil];
+
+ NSDictionary* ret = @{
+ @"applicationDirectory": [[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]] absoluteString],
+ @"applicationStorageDirectory": [[NSURL fileURLWithPath:storagePath] absoluteString],
+ @"dataDirectory": [[NSURL fileURLWithPath:libPathNoSync] absoluteString],
+ @"syncedDataDirectory": [[NSURL fileURLWithPath:libPathSync] absoluteString],
+ @"documentsDirectory": [[NSURL fileURLWithPath:docPath] absoluteString],
+ @"cacheDirectory": [[NSURL fileURLWithPath:cachePath] absoluteString],
+ @"tempDirectory": [[NSURL fileURLWithPath:NSTemporaryDirectory()] absoluteString]
+ };
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:ret];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* Creates and returns a dictionary representing an Entry Object
+ *
+ * IN:
+ * NSString* fullPath of the entry
+ * int fsType - FileSystem type
+ * BOOL isDirectory - YES if this is a directory, NO if is a file
+ * OUT:
+ * NSDictionary* Entry object
+ * bool as NSNumber isDirectory
+ * bool as NSNumber isFile
+ * NSString* name - last part of path
+ * NSString* fullPath
+ * NSString* filesystemName - FileSystem name -- actual filesystem will be created on the JS side if necessary, to avoid
+ * creating circular reference (FileSystem contains DirectoryEntry which contains FileSystem.....!!)
+ */
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir
+{
+ NSObject<CDVFileSystem> *fs = [self fileSystemByName:fsName];
+ return [fs makeEntryForPath:fullPath isDirectory:isDir];
+}
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL
+{
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURL];
+ return [fs makeEntryForLocalURL:localURL];
+}
+
+- (NSDictionary *)makeEntryForURL:(NSURL *)URL
+{
+ CDVFilesystemURL* fsURL = [self fileSystemURLforArg:[URL absoluteString]];
+ return [self makeEntryForLocalURL:fsURL];
+}
+
+/*
+ * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
+ * IN
+ * NSString* localURI: Should be an escaped local filesystem URI
+ * OUT
+ * Entry object
+ * bool isDirectory
+ * bool isFile
+ * string name
+ * string fullPath
+ * fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!!
+ */
+- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* localURIstr = [command argumentAtIndex:0];
+ CDVPluginResult* result;
+
+ localURIstr = [self encodePath:localURIstr]; //encode path before resolving
+ CDVFilesystemURL* inputURI = [self fileSystemURLforArg:localURIstr];
+
+ if (inputURI == nil || inputURI.fileSystemName == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+ } else {
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:inputURI];
+ if (fs == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+ } else {
+ result = [fs entryForLocalURI:inputURI];
+ }
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+//encode path with percent escapes
+-(NSString *)encodePath:(NSString *)path
+{
+ NSString *decodedPath = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //decode incase it's already encoded to avoid encoding twice
+ return [decodedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+}
+
+
+/* Part of DirectoryEntry interface, creates or returns the specified directory
+ * IN:
+ * NSString* localURI - local filesystem URI for this directory
+ * NSString* path - directory to be created/returned; may be full path or relative path
+ * NSDictionary* - Flags object
+ * boolean as NSNumber create -
+ * if create is true and directory does not exist, create dir and return directory entry
+ * if create is true and exclusive is true and directory does exist, return error
+ * if create is false and directory does not exist, return error
+ * if create is false and the path represents a file, return error
+ * boolean as NSNumber exclusive - used in conjunction with create
+ * if exclusive is true and create is true - specifies failure if directory already exists
+ *
+ *
+ */
+- (void)getDirectory:(CDVInvokedUrlCommand*)command
+{
+ NSMutableArray* arguments = [NSMutableArray arrayWithArray:command.arguments];
+ NSMutableDictionary* options = nil;
+
+ if ([arguments count] >= 3) {
+ options = [command argumentAtIndex:2 withDefault:nil];
+ }
+ // add getDir to options and call getFile()
+ if (options != nil) {
+ options = [NSMutableDictionary dictionaryWithDictionary:options];
+ } else {
+ options = [NSMutableDictionary dictionaryWithCapacity:1];
+ }
+ [options setObject:[NSNumber numberWithInt:1] forKey:@"getDir"];
+ if ([arguments count] >= 3) {
+ [arguments replaceObjectAtIndex:2 withObject:options];
+ } else {
+ [arguments addObject:options];
+ }
+ CDVInvokedUrlCommand* subCommand =
+ [[CDVInvokedUrlCommand alloc] initWithArguments:arguments
+ callbackId:command.callbackId
+ className:command.className
+ methodName:command.methodName];
+
+ [self getFile:subCommand];
+}
+
+/* Part of DirectoryEntry interface, creates or returns the specified file
+ * IN:
+ * NSString* baseURI - local filesytem URI for the base directory to search
+ * NSString* requestedPath - file to be created/returned; may be absolute path or relative path
+ * NSDictionary* options - Flags object
+ * boolean as NSNumber create -
+ * if create is true and file does not exist, create file and return File entry
+ * if create is true and exclusive is true and file does exist, return error
+ * if create is false and file does not exist, return error
+ * if create is false and the path represents a directory, return error
+ * boolean as NSNumber exclusive - used in conjunction with create
+ * if exclusive is true and create is true - specifies failure if file already exists
+ */
+- (void)getFile:(CDVInvokedUrlCommand*)command
+{
+ NSString* baseURIstr = [command argumentAtIndex:0];
+ CDVFilesystemURL* baseURI = [self fileSystemURLforArg:baseURIstr];
+ NSString* requestedPath = [command argumentAtIndex:1];
+ NSDictionary* options = [command argumentAtIndex:2 withDefault:nil];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:baseURI];
+ CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options];
+
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * Look up the parent Entry containing this Entry.
+ * If this Entry is the root of its filesystem, its parent is itself.
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI
+ * NSMutableDictionary* options
+ * empty
+ */
+- (void)getParent:(CDVInvokedUrlCommand*)command
+{
+ // arguments are URL encoded
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult* result = [fs getParentForURL:localURI];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * set MetaData of entry
+ * Currently we only support "com.apple.MobileBackup" (boolean)
+ */
+- (void)setMetadata:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSDictionary* options = [command argumentAtIndex:1 withDefault:nil];
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult* result = [fs setMetadataForURL:localURI withObject:options];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* removes the directory or file entry
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI
+ *
+ * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir
+ * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file
+ * returns NOT_FOUND_ERR if file or dir is not found
+*/
+- (void)remove:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ CDVPluginResult* result = nil;
+
+ if ([localURI.fullPath isEqualToString:@""]) {
+ // error if try to remove top level (documents or tmp) dir
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+ } else {
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ result = [fs removeFileAtURL:localURI];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* recursively removes the directory
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI
+ *
+ * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir
+ * returns NOT_FOUND_ERR if file or dir is not found
+ */
+- (void)removeRecursively:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ CDVPluginResult* result = nil;
+
+ if ([localURI.fullPath isEqualToString:@""]) {
+ // error if try to remove top level (documents or tmp) dir
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+ } else {
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ result = [fs recursiveRemoveFileAtURL:localURI];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)copyTo:(CDVInvokedUrlCommand*)command
+{
+ [self doCopyMove:command isCopy:YES];
+}
+
+- (void)moveTo:(CDVInvokedUrlCommand*)command
+{
+ [self doCopyMove:command isCopy:NO];
+}
+
+/* Copy/move a file or directory to a new location
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* URL of entry to copy
+ * 1 - NSString* URL of the directory into which to copy/move the entry
+ * 2 - Optionally, the new name of the entry, defaults to the current name
+ * BOOL - bCopy YES if copy, NO if move
+ */
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy
+{
+ NSArray* arguments = command.arguments;
+
+ // arguments
+ NSString* srcURLstr = [command argumentAtIndex:0];
+ NSString* destURLstr = [command argumentAtIndex:1];
+
+ CDVPluginResult *result;
+
+ if (!srcURLstr || !destURLstr) {
+ // either no source or no destination provided
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ CDVFilesystemURL* srcURL = [self fileSystemURLforArg:srcURLstr];
+ CDVFilesystemURL* destURL = [self fileSystemURLforArg:destURLstr];
+
+ NSObject<CDVFileSystem> *srcFs = [self filesystemForURL:srcURL];
+ NSObject<CDVFileSystem> *destFs = [self filesystemForURL:destURL];
+
+ // optional argument; use last component from srcFullPath if new name not provided
+ NSString* newName = ([arguments count] > 2) ? [command argumentAtIndex:2] : [srcURL.url lastPathComponent];
+ if ([newName rangeOfString:@":"].location != NSNotFound) {
+ // invalid chars in new name
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ __weak CDVFile* weakSelf = self;
+ [self.commandDelegate runInBackground:^ {
+ [destFs copyFileToURL:destURL withName:newName fromFileSystem:srcFs atURL:srcURL copy:bCopy callback:^(CDVPluginResult* result) {
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+
+}
+
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ __weak CDVFile* weakSelf = self;
+ [fs getFileMetadataForURL:localURI callback:^(CDVPluginResult* result) {
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+}
+
+- (void)readEntries:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult *result = [fs readEntriesAtURL:localURI];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* read and return file data
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* fullPath
+ * 1 - NSString* encoding
+ * 2 - NSString* start
+ * 3 - NSString* end
+ */
+- (void)readAsText:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSString* encoding = [command argumentAtIndex:1];
+ NSInteger start = [[command argumentAtIndex:2] integerValue];
+ NSInteger end = [[command argumentAtIndex:3] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ if (fs == nil) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ // TODO: implement
+ if ([@"UTF-8" caseInsensitiveCompare : encoding] != NSOrderedSame) {
+ NSLog(@"Only UTF-8 encodings are currently supported by readAsText");
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ __weak CDVFile* weakSelf = self;
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ NSString* str = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSUTF8StringEncoding freeWhenDone:NO];
+ // Check that UTF8 conversion did not fail.
+ if (str != nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:str];
+ result.associatedObject = data;
+ } else {
+ errorCode = ENCODING_ERR;
+ }
+ }
+ if (result == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+/* Read content of text file and return as base64 encoded data url.
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* fullPath
+ * 1 - NSString* start
+ * 2 - NSString* end
+ *
+ * Determines the mime type from the file extension, returns ENCODING_ERR if mimetype can not be determined.
+ */
+
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSInteger start = [[command argumentAtIndex:1] integerValue];
+ NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ __weak CDVFile* weakSelf = self;
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ NSString* b64Str = toBase64(data);
+ NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, b64Str];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:output];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+/* Read content of text file and return as an arraybuffer
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* fullPath
+ * 1 - NSString* start
+ * 2 - NSString* end
+ */
+
+- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSInteger start = [[command argumentAtIndex:1] integerValue];
+ NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ __weak CDVFile* weakSelf = self;
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:data];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+- (void)readAsBinaryString:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSInteger start = [[command argumentAtIndex:1] integerValue];
+ NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ __weak CDVFile* weakSelf = self;
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ NSString* payload = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSASCIIStringEncoding freeWhenDone:NO];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
+ result.associatedObject = data;
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+
+- (void)truncate:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ unsigned long long pos = (unsigned long long)[[command argumentAtIndex:1] longLongValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult *result = [fs truncateFileAtURL:localURI atPosition:pos];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* write
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI of file to write to
+ * 1 - NSString* or NSData* data to write
+ * 2 - NSNumber* position to begin writing
+ */
+- (void)write:(CDVInvokedUrlCommand*)command
+{
+ __weak CDVFile* weakSelf = self;
+
+ [self.commandDelegate runInBackground:^ {
+ NSString* callbackId = command.callbackId;
+
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ id argData = [command argumentAtIndex:1];
+ unsigned long long pos = (unsigned long long)[[command argumentAtIndex:2] longLongValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+
+ [fs truncateFileAtURL:localURI atPosition:pos];
+ CDVPluginResult *result;
+ if ([argData isKindOfClass:[NSString class]]) {
+ NSData *encData = [argData dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
+ result = [fs writeToFileAtURL:localURI withData:encData append:YES];
+ } else if ([argData isKindOfClass:[NSData class]]) {
+ result = [fs writeToFileAtURL:localURI withData:argData append:YES];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid parameter type"];
+ }
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }];
+}
+
+#pragma mark Methods for converting between URLs and paths
+
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURL
+{
+ for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
+ if ([fs.name isEqualToString:localURL.fileSystemName]) {
+ if ([fs respondsToSelector:@selector(filesystemPathForURL:)]) {
+ return [fs filesystemPathForURL:localURL];
+ }
+ }
+ }
+ return nil;
+}
+
+#pragma mark Undocumented Filesystem API
+
+- (void)testFileExists:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* argPath = [command argumentAtIndex:0];
+
+ // Get the file manager
+ NSFileManager* fMgr = [NSFileManager defaultManager];
+ NSString* appFile = argPath; // [ self getFullPath: argPath];
+
+ BOOL bExists = [fMgr fileExistsAtPath:appFile];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(bExists ? 1 : 0)];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* argPath = [command argumentAtIndex:0];
+
+ // Get the file manager
+ NSFileManager* fMgr = [[NSFileManager alloc] init];
+ NSString* appFile = argPath; // [self getFullPath: argPath];
+ BOOL bIsDir = NO;
+ BOOL bExists = [fMgr fileExistsAtPath:appFile isDirectory:&bIsDir];
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:((bExists && bIsDir) ? 1 : 0)];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+// Returns number of bytes available via callback
+- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command
+{
+ // no arguments
+
+ NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
+
+ NSString* strFreeSpace = [NSString stringWithFormat:@"%qu", [pNumAvail unsignedLongLongValue]];
+ // NSLog(@"Free space is %@", strFreeSpace );
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:strFreeSpace];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+#pragma mark Compatibility with older File API
+
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+ return [CDVLocalFilesystem getMimeTypeFromPath:fullPath];
+}
+
+- (NSDictionary *)getDirectoryEntry:(NSString *)localPath isDirectory:(BOOL)bDirRequest
+{
+ CDVFilesystemURL *localURL = [self fileSystemURLforLocalPath:localPath];
+ return [self makeEntryForPath:localURL.fullPath fileSystemName:localURL.fileSystemName isDirectory:bDirRequest];
+}
+
+#pragma mark Internal methods for testing
+// Internal methods for testing: Get the on-disk location of a local filesystem url.
+// [Currently used for testing file-transfer]
+
+- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURL = [self fileSystemURLforArg:command.arguments[0]];
+
+ NSString* fsPath = [self filesystemPathForURL:localURL];
+ CDVPluginResult* result;
+ if (fsPath) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:fsPath];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot resolve URL to a file"];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h
new file mode 100644
index 00000000..a0186c85
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h
@@ -0,0 +1,32 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+
+@interface CDVLocalFilesystem : NSObject<CDVFileSystem> {
+ NSString *_name;
+ NSString *_fsRoot;
+}
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot;
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+
+@property (nonatomic,strong) NSString *fsRoot;
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m
new file mode 100644
index 00000000..c340ce0c
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m
@@ -0,0 +1,750 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+#import "CDVLocalFilesystem.h"
+#import <Cordova/CDV.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+#import <sys/xattr.h>
+
+@implementation CDVLocalFilesystem
+@synthesize name=_name, fsRoot=_fsRoot, urlTransformer;
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot
+{
+ if (self) {
+ self.name = name;
+ self.fsRoot = fsRoot;
+ }
+ return self;
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
+ * URI represents a non-existent path, or is unrecognized or otherwise malformed.
+ */
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
+{
+ CDVPluginResult* result = nil;
+ NSDictionary* entry = [self makeEntryForLocalURL:url];
+ if (entry) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry];
+ } else {
+ // return NOT_FOUND_ERR
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+ return result;
+}
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url {
+ NSString *path = [self filesystemPathForURL:url];
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL isDir = NO;
+ // see if exists and is file or dir
+ BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
+ if (bExists) {
+ return [self makeEntryForPath:url.fullPath isDirectory:isDir];
+ } else {
+ return nil;
+ }
+}
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir
+{
+ NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+ NSString* lastPart = [[self stripQueryParametersFromPath:fullPath] lastPathComponent];
+ if (isDir && ![fullPath hasSuffix:@"/"]) {
+ fullPath = [fullPath stringByAppendingString:@"/"];
+ }
+ [dirEntry setObject:[NSNumber numberWithBool:!isDir] forKey:@"isFile"];
+ [dirEntry setObject:[NSNumber numberWithBool:isDir] forKey:@"isDirectory"];
+ [dirEntry setObject:fullPath forKey:@"fullPath"];
+ [dirEntry setObject:lastPart forKey:@"name"];
+ [dirEntry setObject:self.name forKey: @"filesystemName"];
+
+ NSURL* nativeURL = [NSURL fileURLWithPath:[self filesystemPathForFullPath:fullPath]];
+ if (self.urlTransformer) {
+ nativeURL = self.urlTransformer(nativeURL);
+ }
+
+ dirEntry[@"nativeURL"] = [nativeURL absoluteString];
+
+ return dirEntry;
+}
+
+- (NSString *)stripQueryParametersFromPath:(NSString *)fullPath
+{
+ NSRange questionMark = [fullPath rangeOfString:@"?"];
+ if (questionMark.location != NSNotFound) {
+ return [fullPath substringWithRange:NSMakeRange(0,questionMark.location)];
+ }
+ return fullPath;
+}
+
+- (NSString *)filesystemPathForFullPath:(NSString *)fullPath
+{
+ NSString *path = nil;
+ NSString *strippedFullPath = [self stripQueryParametersFromPath:fullPath];
+ path = [NSString stringWithFormat:@"%@%@", self.fsRoot, strippedFullPath];
+ if ([path length] > 1 && [path hasSuffix:@"/"]) {
+ path = [path substringToIndex:([path length]-1)];
+ }
+ return path;
+}
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ * The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ * or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)url
+{
+ return [self filesystemPathForFullPath:url.fullPath];
+}
+
+- (CDVFilesystemURL *)URLforFullPath:(NSString *)fullPath
+{
+ if (fullPath) {
+ NSString* escapedPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ if ([fullPath hasPrefix:@"/"]) {
+ return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@%@", kCDVFilesystemURLPrefix, self.name, escapedPath]];
+ }
+ return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@/%@", kCDVFilesystemURLPrefix, self.name, escapedPath]];
+ }
+ return nil;
+}
+
+- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path
+{
+ return [self URLforFullPath:[self fullPathForFileSystemPath:path]];
+
+}
+
+- (NSString *)normalizePath:(NSString *)rawPath
+{
+ // If this is an absolute path, the first path component will be '/'. Skip it if that's the case
+ BOOL isAbsolutePath = [rawPath hasPrefix:@"/"];
+ if (isAbsolutePath) {
+ rawPath = [rawPath substringFromIndex:1];
+ }
+ NSMutableArray *components = [NSMutableArray arrayWithArray:[rawPath pathComponents]];
+ for (int index = 0; index < [components count]; ++index) {
+ if ([[components objectAtIndex:index] isEqualToString:@".."]) {
+ [components removeObjectAtIndex:index];
+ if (index > 0) {
+ [components removeObjectAtIndex:index-1];
+ --index;
+ }
+ }
+ }
+
+ if (isAbsolutePath) {
+ return [NSString stringWithFormat:@"/%@", [components componentsJoinedByString:@"/"]];
+ } else {
+ return [components componentsJoinedByString:@"/"];
+ }
+
+
+}
+
+- (BOOL)valueForKeyIsNumber:(NSDictionary*)dict key:(NSString*)key
+{
+ BOOL bNumber = NO;
+ NSObject* value = dict[key];
+ if (value) {
+ bNumber = [value isKindOfClass:[NSNumber class]];
+ }
+ return bNumber;
+}
+
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+ CDVPluginResult* result = nil;
+ BOOL bDirRequest = NO;
+ BOOL create = NO;
+ BOOL exclusive = NO;
+ int errorCode = 0; // !!! risky - no error code currently defined for 0
+
+ if ([self valueForKeyIsNumber:options key:@"create"]) {
+ create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
+ }
+ if ([self valueForKeyIsNumber:options key:@"exclusive"]) {
+ exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
+ }
+ if ([self valueForKeyIsNumber:options key:@"getDir"]) {
+ // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
+ bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
+ }
+ // see if the requested path has invalid characters - should we be checking for more than just ":"?
+ if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
+ errorCode = ENCODING_ERR;
+ } else {
+ // Build new fullPath for the requested resource.
+ // We concatenate the two paths together, and then scan the resulting string to remove
+ // parent ("..") references. Any parent references at the beginning of the string are
+ // silently removed.
+ NSString *combinedPath = [baseURI.fullPath stringByAppendingPathComponent:requestedPath];
+ combinedPath = [self normalizePath:combinedPath];
+ CDVFilesystemURL* requestedURL = [self URLforFullPath:combinedPath];
+
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir;
+ BOOL bExists = [fileMgr fileExistsAtPath:[self filesystemPathForURL:requestedURL] isDirectory:&bIsDir];
+ if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
+ // path exists and is not of requested type - return TYPE_MISMATCH_ERR
+ errorCode = TYPE_MISMATCH_ERR;
+ } else if (!bExists && (create == NO)) {
+ // path does not exist and create is false - return NOT_FOUND_ERR
+ errorCode = NOT_FOUND_ERR;
+ } else if (bExists && (create == YES) && (exclusive == YES)) {
+ // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
+ errorCode = PATH_EXISTS_ERR;
+ } else {
+ // if bExists and create == YES - just return data
+ // if bExists and create == NO - just return data
+ // if !bExists and create == YES - create and return data
+ BOOL bSuccess = YES;
+ NSError __autoreleasing* pError = nil;
+ if (!bExists && (create == YES)) {
+ if (bDirRequest) {
+ // create the dir
+ bSuccess = [fileMgr createDirectoryAtPath:[self filesystemPathForURL:requestedURL] withIntermediateDirectories:NO attributes:nil error:&pError];
+ } else {
+ // create the empty file
+ bSuccess = [fileMgr createFileAtPath:[self filesystemPathForURL:requestedURL] contents:nil attributes:nil];
+ }
+ }
+ if (!bSuccess) {
+ errorCode = ABORT_ERR;
+ if (pError) {
+ NSLog(@"error creating directory: %@", [pError localizedDescription]);
+ }
+ } else {
+ // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
+ // file existed or was created
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath isDirectory:bDirRequest]];
+ }
+ } // are all possible conditions met?
+ }
+
+ if (errorCode > 0) {
+ // create error callback
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+ return result;
+
+}
+
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+ CDVPluginResult* result = nil;
+ CDVFilesystemURL *newURI = nil;
+ if ([localURI.fullPath isEqualToString:@""]) {
+ // return self
+ newURI = localURI;
+ } else {
+ newURI = [CDVFilesystemURL fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
+ }
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir;
+ BOOL bExists = [fileMgr fileExistsAtPath:[self filesystemPathForURL:newURI] isDirectory:&bIsDir];
+ if (bExists) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath isDirectory:bIsDir]];
+ } else {
+ // invalid path or file does not exist
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+ return result;
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+ BOOL ok = NO;
+
+ NSString* filePath = [self filesystemPathForURL:localURI];
+ // we only care about this iCloud key for now.
+ // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
+ NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
+ id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
+
+ if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
+ if (IsAtLeastiOSVersion(@"5.1")) {
+ NSURL* url = [NSURL fileURLWithPath:filePath];
+ NSError* __autoreleasing error = nil;
+
+ ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
+ } else { // below 5.1 (deprecated - only really supported in 5.01)
+ u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
+ if (value == 0) { // remove the attribute (allow backup, the default)
+ ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
+ } else { // set the attribute (skip backup)
+ ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+ }
+ }
+ }
+
+ if (ok) {
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+ }
+}
+
+/* remove the file or directory (recursively)
+ * IN:
+ * NSString* fullPath - the full path to the file or directory to be removed
+ * NSString* callbackId
+ * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
+ */
+
+- (CDVPluginResult*)doRemove:(NSString*)fullPath
+{
+ CDVPluginResult* result = nil;
+ BOOL bSuccess = NO;
+ NSError* __autoreleasing pError = nil;
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+ @try {
+ bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
+ if (bSuccess) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ // see if we can give a useful error
+ CDVFileError errorCode = ABORT_ERR;
+ NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
+ if ([pError code] == NSFileNoSuchFileError) {
+ errorCode = NOT_FOUND_ERR;
+ } else if ([pError code] == NSFileWriteNoPermissionError) {
+ errorCode = NO_MODIFICATION_ALLOWED_ERR;
+ }
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+ } @catch(NSException* e) { // NSInvalidArgumentException if path is . or ..
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
+ }
+
+ return result;
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+ NSString *fileSystemPath = [self filesystemPathForURL:localURI];
+
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir = NO;
+ BOOL bExists = [fileMgr fileExistsAtPath:fileSystemPath isDirectory:&bIsDir];
+ if (!bExists) {
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+ if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fileSystemPath error:nil] count] != 0)) {
+ // dir is not empty
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+ }
+ return [self doRemove:fileSystemPath];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+ NSString *fileSystemPath = [self filesystemPathForURL:localURI];
+ return [self doRemove:fileSystemPath];
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ * The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ * or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
+{
+ if ([fsPath hasPrefix:self.fsRoot]) {
+ return [fsPath substringFromIndex:[self.fsRoot length]];
+ }
+ return nil;
+}
+
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ NSError* __autoreleasing error = nil;
+ NSString *fileSystemPath = [self filesystemPathForURL:localURI];
+
+ NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fileSystemPath error:&error];
+
+ if (contents) {
+ NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1];
+ if ([contents count] > 0) {
+ // create an Entry (as JSON) for each file/dir
+ for (NSString* name in contents) {
+ // see if is dir or file
+ NSString* entryPath = [fileSystemPath stringByAppendingPathComponent:name];
+ BOOL bIsDir = NO;
+ [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
+ NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] isDirectory:bIsDir];
+ [entries addObject:entryDict];
+ }
+ }
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries];
+ } else {
+ // assume not found but could check error for more specific error conditions
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+}
+
+- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos
+{
+ unsigned long long newPos = 0UL;
+
+ NSFileHandle* file = [NSFileHandle fileHandleForWritingAtPath:filePath];
+
+ if (file) {
+ [file truncateFileAtOffset:(unsigned long long)pos];
+ newPos = [file offsetInFile];
+ [file synchronizeFile];
+ [file closeFile];
+ }
+ return newPos;
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+ unsigned long long newPos = [self truncateFile:[self filesystemPathForURL:localURI] atPosition:pos];
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)newPos];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+ NSString *filePath = [self filesystemPathForURL:localURL];
+
+ CDVPluginResult* result = nil;
+ CDVFileError errCode = INVALID_MODIFICATION_ERR;
+ int bytesWritten = 0;
+
+ if (filePath) {
+ NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend];
+ if (fileStream) {
+ NSUInteger len = [encData length];
+ if (len == 0) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:(double)len];
+ } else {
+ [fileStream open];
+
+ bytesWritten = (int)[fileStream write:[encData bytes] maxLength:len];
+
+ [fileStream close];
+ if (bytesWritten > 0) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten];
+ // } else {
+ // can probably get more detailed error info via [fileStream streamError]
+ // errCode already set to INVALID_MODIFICATION_ERR;
+ // bytesWritten = 0; // may be set to -1 on error
+ }
+ }
+ } // else fileStream not created return INVALID_MODIFICATION_ERR
+ } else {
+ // invalid filePath
+ errCode = NOT_FOUND_ERR;
+ }
+ if (!result) {
+ // was an error
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+ }
+ return result;
+}
+
+/**
+ * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
+ * or attempted to copy a directory into a directory that it contains directly or indirectly.
+ *
+ * IN:
+ * NSString* srcDir
+ * NSString* destinationDir
+ * OUT:
+ * YES copy/ move is allows
+ * NO move is onto itself
+ */
+- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
+{
+ // This weird test is to determine if we are copying or moving a directory into itself.
+ // Copy /Documents/myDir to /Documents/myDir-backup is okay but
+ // Copy /Documents/myDir to /Documents/myDir/backup not okay
+ BOOL copyOK = YES;
+ NSRange range = [dest rangeOfString:src];
+
+ if (range.location != NSNotFound) {
+ NSRange testRange = {range.length - 1, ([dest length] - range.length)};
+ NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
+ if (resultRange.location != NSNotFound) {
+ copyOK = NO;
+ }
+ }
+ return copyOK;
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+ NSFileManager *fileMgr = [[NSFileManager alloc] init];
+ NSString *destRootPath = [self filesystemPathForURL:destURL];
+ BOOL bDestIsDir = NO;
+ BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
+
+ NSString *newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
+ NSString *newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
+
+ BOOL bNewIsDir = NO;
+ BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
+
+ CDVPluginResult *result = nil;
+ int errCode = 0;
+
+ if (!bDestExists) {
+ // the destination root does not exist
+ errCode = NOT_FOUND_ERR;
+ }
+
+ else if ([srcFs isKindOfClass:[CDVLocalFilesystem class]]) {
+ /* Same FS, we can shortcut with NSFileManager operations */
+ NSString *srcFullPath = [srcFs filesystemPathForURL:srcURL];
+
+ BOOL bSrcIsDir = NO;
+ BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
+
+ if (!bSrcExists) {
+ // the source does not exist
+ errCode = NOT_FOUND_ERR;
+ } else if ([newFileSystemPath isEqualToString:srcFullPath]) {
+ // source and destination can not be the same
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
+ // can't copy/move dir to file
+ errCode = INVALID_MODIFICATION_ERR;
+ } else { // no errors yet
+ NSError* __autoreleasing error = nil;
+ BOOL bSuccess = NO;
+ if (bCopy) {
+ if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+ // can't copy dir into self
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bNewExists) {
+ // the full destination should NOT already exist if a copy
+ errCode = PATH_EXISTS_ERR;
+ } else {
+ bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+ }
+ } else { // move
+ // iOS requires that destination must not exist before calling moveTo
+ // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents
+ //
+ if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
+ // can't move a file to directory
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+ // can't move a dir into itself
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bNewExists) {
+ if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
+ // can't move dir to a dir that is not empty
+ errCode = INVALID_MODIFICATION_ERR;
+ newFileSystemPath = nil; // so we won't try to move
+ } else {
+ // remove destination so can perform the moveItemAtPath
+ bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
+ if (!bSuccess) {
+ errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
+ newFileSystemPath = nil;
+ }
+ }
+ } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
+ // can't move a directory inside itself or to any child at any depth;
+ errCode = INVALID_MODIFICATION_ERR;
+ newFileSystemPath = nil;
+ }
+
+ if (newFileSystemPath != nil) {
+ bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+ }
+ }
+ if (bSuccess) {
+ // should verify it is there and of the correct type???
+ NSDictionary* newEntry = [self makeEntryForPath:newFullPath isDirectory:bSrcIsDir];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+ } else {
+ if (error) {
+ if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
+ errCode = NOT_READABLE_ERR;
+ } else if ([error code] == NSFileWriteOutOfSpaceError) {
+ errCode = QUOTA_EXCEEDED_ERR;
+ } else if ([error code] == NSFileWriteNoPermissionError) {
+ errCode = NO_MODIFICATION_ALLOWED_ERR;
+ }
+ }
+ }
+ }
+ } else {
+ // Need to copy the hard way
+ [srcFs readFileAtURL:srcURL start:0 end:-1 callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ BOOL bSuccess = [data writeToFile:newFileSystemPath atomically:YES];
+ if (bSuccess) {
+ // should verify it is there and of the correct type???
+ NSDictionary* newEntry = [self makeEntryForPath:newFullPath isDirectory:NO];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ABORT_ERR];
+ }
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+ callback(result);
+ }];
+ return; // Async IO; return without callback.
+ }
+ if (result == nil) {
+ if (!errCode) {
+ errCode = INVALID_MODIFICATION_ERR; // Catch-all default
+ }
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
+ }
+ callback(result);
+}
+
+/* helper function to get the mimeType from the file extension
+ * IN:
+ * NSString* fullPath - filename (may include path)
+ * OUT:
+ * NSString* the mime type as type/subtype. nil if not able to determine
+ */
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+ NSString* mimeType = nil;
+
+ if (fullPath) {
+ CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+ if (typeId) {
+ mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+ if (!mimeType) {
+ // special case for m4a
+ if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
+ mimeType = @"audio/mp4";
+ } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+ mimeType = @"audio/wav";
+ } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
+ mimeType = @"text/css";
+ }
+ }
+ CFRelease(typeId);
+ }
+ }
+ return mimeType;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+ NSString *path = [self filesystemPathForURL:localURL];
+
+ NSString* mimeType = [CDVLocalFilesystem getMimeTypeFromPath:path];
+ if (mimeType == nil) {
+ mimeType = @"*/*";
+ }
+ NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:path];
+ if (start > 0) {
+ [file seekToFileOffset:start];
+ }
+
+ NSData* readData;
+ if (end < 0) {
+ readData = [file readDataToEndOfFile];
+ } else {
+ readData = [file readDataOfLength:(end - start)];
+ }
+ [file closeFile];
+
+ callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR);
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+ NSString *path = [self filesystemPathForURL:localURL];
+ CDVPluginResult *result;
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+ NSError* __autoreleasing error = nil;
+ NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error];
+
+ if (fileAttrs) {
+
+ // create dictionary of file info
+ NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+
+ [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+ [fileInfo setObject:[self mimeTypeForFileAtPath: path] forKey:@"type"];
+ [fileInfo setObject:[path lastPathComponent] forKey:@"name"];
+
+ // Ensure that directories (and other non-regular files) report size of 0
+ unsigned long long size = ([fileAttrs fileType] == NSFileTypeRegular ? [fileAttrs fileSize] : 0);
+ [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:size] forKey:@"size"];
+
+ NSDate* modDate = [fileAttrs fileModificationDate];
+ if (modDate) {
+ [fileInfo setObject:[NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000] forKey:@"lastModifiedDate"];
+ }
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+
+ } else {
+ // didn't get fileAttribs
+ CDVFileError errorCode = ABORT_ERR;
+ NSLog(@"error getting metadata: %@", [error localizedDescription]);
+ if ([error code] == NSFileNoSuchFileError || [error code] == NSFileReadNoSuchFileError) {
+ errorCode = NOT_FOUND_ERR;
+ }
+ // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
+ }
+
+ callback(result);
+}
+
+// fix errors that base on Alexsander Akers from http://stackoverflow.com/a/5998683/2613194
+- (NSString*) mimeTypeForFileAtPath: (NSString *) path {
+ if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
+ return nil;
+ }
+
+ CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL);
+ CFStringRef mimeType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
+ CFRelease(UTI);
+
+ if (!mimeType) {
+ return @"application/octet-stream";
+ }
+ return (__bridge NSString *)mimeType;
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.h b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.h
new file mode 100644
index 00000000..13a9360e
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.h
@@ -0,0 +1,189 @@
+/*
+ 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.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Cordova/CDVPlugin.h>
+
+NSString* const kCDVFilesystemURLPrefix;
+
+/**
+* The default filesystems if non are specified.
+*/
+#define CDV_FILESYSTEMS_DEFAULT @"documents,cache,bundle,root"
+
+/**
+* Preference name of the extra filesystems to be "mounted". the following are supported:
+* 'bundle' - mounts the application directory
+* 'documents' - mounts the users Documents directory (~/Documents)
+* 'root' - mounts the root file system
+* 'cache' - mounts the caches directory (~/Library/Caches/<bundle-id/)
+*/
+#define CDV_PREF_EXTRA_FILESYSTEM @"osxextrafilesystems"
+
+enum CDVFileError {
+ NO_ERROR = 0,
+ NOT_FOUND_ERR = 1,
+ SECURITY_ERR = 2,
+ ABORT_ERR = 3,
+ NOT_READABLE_ERR = 4,
+ ENCODING_ERR = 5,
+ NO_MODIFICATION_ALLOWED_ERR = 6,
+ INVALID_STATE_ERR = 7,
+ SYNTAX_ERR = 8,
+ INVALID_MODIFICATION_ERR = 9,
+ QUOTA_EXCEEDED_ERR = 10,
+ TYPE_MISMATCH_ERR = 11,
+ PATH_EXISTS_ERR = 12
+};
+typedef int CDVFileError;
+
+@interface CDVFilesystemURL : NSObject {
+ NSURL *_url;
+ NSString *_fileSystemName;
+ NSString *_fullPath;
+}
+
+- (id) initWithString:(NSString*)strURL;
+- (id) initWithURL:(NSURL*)URL;
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL;
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL;
+
+- (NSString *)absoluteURL;
+
+@property (atomic) NSURL *url;
+@property (atomic) NSString *fileSystemName;
+@property (atomic) NSString *fullPath;
+
+@end
+
+@interface CDVFilesystemURLProtocol : NSURLProtocol
+@end
+
+@protocol CDVFileSystem
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options;
+- (CDVPluginResult *)getParentForURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options;
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos;
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend;
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback;
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback;
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback;
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url;
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir;
+
+@property (nonatomic,strong) NSString *name;
+@property (nonatomic, copy) NSURL*(^urlTransformer)(NSURL*);
+
+@optional
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURI;
+- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path;
+
+@end
+
+@interface CDVFile : CDVPlugin {
+ NSString* rootDocsPath;
+ NSString* appDocsPath;
+ NSString* appLibraryPath;
+ NSString* appTempPath;
+
+ NSMutableArray* fileSystems_;
+ BOOL userHasAllowed;
+}
+
+- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath;
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir;
+- (NSDictionary *)makeEntryForURL:(NSURL *)URL;
+- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath;
+
+- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL;
+
+/* Native Registration API */
+- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs;
+- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName;
+
+/* Exec API */
+- (void)requestFileSystem:(CDVInvokedUrlCommand*)command;
+- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command;
+- (void)getDirectory:(CDVInvokedUrlCommand*)command;
+- (void)getFile:(CDVInvokedUrlCommand*)command;
+- (void)getParent:(CDVInvokedUrlCommand*)command;
+- (void)removeRecursively:(CDVInvokedUrlCommand*)command;
+- (void)remove:(CDVInvokedUrlCommand*)command;
+- (void)copyTo:(CDVInvokedUrlCommand*)command;
+- (void)moveTo:(CDVInvokedUrlCommand*)command;
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command;
+- (void)readEntries:(CDVInvokedUrlCommand*)command;
+- (void)readAsText:(CDVInvokedUrlCommand*)command;
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command;
+- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command;
+- (void)write:(CDVInvokedUrlCommand*)command;
+- (void)testFileExists:(CDVInvokedUrlCommand*)command;
+- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command;
+- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command;
+- (void)truncate:(CDVInvokedUrlCommand*)command;
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy;
+
+/* Compatibilty with older File API */
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+- (NSDictionary *)getDirectoryEntry:(NSString *)target isDirectory:(BOOL)bDirRequest;
+
+/* Conversion between filesystem paths and URLs */
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)URL;
+
+/* Internal methods for testing */
+- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command;
+
+/**
+ * local path of the 'documents' file system (~/Documents)
+ */
+@property (nonatomic, strong) NSString* appDocsPath;
+
+/**
+* local path of the 'applicationStorageDirectory' file system (~/Library/Application Support/<bundle-id>)
+*/
+@property (nonatomic, strong) NSString* appSupportPath;
+
+/**
+* local path of the 'persistent' file system (~/Library/Application Support/<bundle-id>/files)
+*/
+@property (nonatomic, strong) NSString* appDataPath;
+
+/**
+* local path of the 'documents' file system (~/Documents)
+*/
+@property (nonatomic, strong) NSString* appTempPath;
+
+/**
+* local path of the 'cache' file system (~/Library/Caches/<bundle-id>)
+*/
+@property (nonatomic, strong) NSString* appCachePath;
+
+/**
+ * registered file systems
+ */
+@property (nonatomic, strong) NSMutableArray* fileSystems;
+
+@property BOOL userHasAllowed;
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.m b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.m
new file mode 100644
index 00000000..75a60019
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVFile.m
@@ -0,0 +1,1056 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+#import "CDVLocalFilesystem.h"
+#import <objc/message.h>
+
+static NSString* toBase64(NSData* data) {
+ SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
+ SEL s2 = NSSelectorFromString(@"base64EncodedString");
+ SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
+
+ if ([data respondsToSelector:s1]) {
+ NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
+ return func(data, s1);
+ } else if ([data respondsToSelector:s2]) {
+ NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
+ return func(data, s2);
+ } else if ([data respondsToSelector:s3]) {
+ NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
+ return func(data, s3, 0);
+ } else {
+ return nil;
+ }
+}
+
+CDVFile *filePlugin = nil;
+
+extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
+
+#ifndef __IPHONE_5_1
+ NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
+#endif
+
+NSString* const kCDVFilesystemURLPrefix = @"cdvfile";
+
+@implementation CDVFilesystemURL
+@synthesize url=_url;
+@synthesize fileSystemName=_fileSystemName;
+@synthesize fullPath=_fullPath;
+
+- (id) initWithString:(NSString *)strURL
+{
+ if ( self = [super init] ) {
+ NSURL *decodedURL = [NSURL URLWithString:strURL];
+ return [self initWithURL:decodedURL];
+ }
+ return nil;
+}
+
+-(id) initWithURL:(NSURL *)URL
+{
+ if ( self = [super init] ) {
+ _url = URL;
+ _fileSystemName = [self filesystemNameForLocalURI:URL];
+ _fullPath = [self fullPathForLocalURI:URL];
+ }
+ return self;
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString FileSystem Name for this URI, or nil if it is not recognized.
+ */
+- (NSString *)filesystemNameForLocalURI:(NSURL *)uri
+{
+ if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
+ NSArray *pathComponents = [uri pathComponents];
+ if (pathComponents != nil && pathComponents.count > 1) {
+ return [pathComponents objectAtIndex:1];
+ }
+ }
+ return nil;
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString fullPath component suitable for an Entry object.
+ * The incoming URI should be properly escaped. The returned fullPath is unescaped.
+ */
+- (NSString *)fullPathForLocalURI:(NSURL *)uri
+{
+ if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
+ NSString *path = [uri path];
+ if ([uri query]) {
+ path = [NSString stringWithFormat:@"%@?%@", path, [uri query]];
+ }
+ NSRange slashRange = [path rangeOfString:@"/" options:0 range:NSMakeRange(1, path.length-1)];
+ if (slashRange.location == NSNotFound) {
+ return @"";
+ }
+ return [path substringFromIndex:slashRange.location];
+ }
+ return nil;
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
+{
+ return [[CDVFilesystemURL alloc] initWithString:strURL];
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
+{
+ return [[CDVFilesystemURL alloc] initWithURL:URL];
+}
+
+- (NSString *)absoluteURL
+{
+ return [NSString stringWithFormat:@"cdvfile://localhost/%@%@", self.fileSystemName, self.fullPath];
+}
+
+@end
+
+@implementation CDVFilesystemURLProtocol
+
++ (BOOL)canInitWithRequest:(NSURLRequest*)request
+{
+ NSURL* url = [request URL];
+ return [[url scheme] isEqualToString:kCDVFilesystemURLPrefix];
+}
+
++ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
+{
+ return request;
+}
+
++ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
+{
+ return [[[requestA URL] resourceSpecifier] isEqualToString:[[requestB URL] resourceSpecifier]];
+}
+
+- (void)startLoading
+{
+ CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithURL:[[self request] URL]];
+ NSObject<CDVFileSystem> *fs = [filePlugin filesystemForURL:url];
+ [fs readFileAtURL:url start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
+ NSMutableDictionary* responseHeaders = [[NSMutableDictionary alloc] init];
+ responseHeaders[@"Cache-Control"] = @"no-cache";
+
+ if (!error) {
+ responseHeaders[@"Content-Type"] = mimetype;
+ NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
+ [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [[self client] URLProtocol:self didLoadData:data];
+ [[self client] URLProtocolDidFinishLoading:self];
+ } else {
+ NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
+ [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [[self client] URLProtocolDidFinishLoading:self];
+ }
+ }];
+}
+
+- (void)stopLoading
+{}
+
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
+ willCacheResponse:(NSCachedURLResponse*)cachedResponse {
+ return nil;
+}
+
+@end
+
+
+@implementation CDVFile
+
+@synthesize appDocsPath, appDataPath, appSupportPath, appTempPath, appCachePath, userHasAllowed, fileSystems=fileSystems_;
+
+- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs {
+ __weak CDVFile* weakSelf = self;
+ SEL sel = NSSelectorFromString(@"urlTransformer");
+ // for backwards compatibility - we check if this property is there
+ // we create a wrapper block because the urlTransformer property
+ // on the commandDelegate might be set dynamically at a future time
+ // (and not dependent on plugin loading order)
+ if ([self.commandDelegate respondsToSelector:sel]) {
+ fs.urlTransformer = ^NSURL*(NSURL* urlToTransform) {
+ // grab the block from the commandDelegate
+ NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(weakSelf.commandDelegate, sel);
+ // if block is not null, we call it
+ if (urlTransformer) {
+ return urlTransformer(urlToTransform);
+ } else { // else we return the same url
+ return urlToTransform;
+ }
+ };
+ }
+ [fileSystems_ addObject:fs];
+}
+
+- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName
+{
+ if (self.fileSystems != nil) {
+ for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
+ if ([fs.name isEqualToString:fsName]) {
+ return fs;
+ }
+ }
+ }
+ return nil;
+
+}
+
+- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL {
+ if (localURL.fileSystemName == nil) return nil;
+ @try {
+ return [self fileSystemByName:localURL.fileSystemName];
+ }
+ @catch (NSException *e) {
+ return nil;
+ }
+}
+
+- (NSArray *)getExtraFileSystemsPreference:(NSWindowController *)vc
+{
+ NSString *filesystemsStr = nil;
+ if([self.viewController isKindOfClass:[CDVViewController class]]) {
+ CDVViewController *vc = self.viewController;
+ NSDictionary *settings = [vc settings];
+ filesystemsStr = [settings[CDV_PREF_EXTRA_FILESYSTEM] lowercaseString];
+ }
+ if (!filesystemsStr) {
+ filesystemsStr = CDV_FILESYSTEMS_DEFAULT;
+ }
+ return [filesystemsStr componentsSeparatedByString:@","];
+}
+
+- (void)makeNonSyncable:(NSString*)path {
+ [[NSFileManager defaultManager] createDirectoryAtPath:path
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nil];
+ NSURL* url = [NSURL fileURLWithPath:path];
+ [url setResourceValue: [NSNumber numberWithBool: YES]
+ forKey: NSURLIsExcludedFromBackupKey error:nil];
+
+}
+
+- (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDictionary *)availableFileSystems
+{
+ NSMutableSet *installedFilesystems = [[NSMutableSet alloc] initWithCapacity:7];
+
+ /* Register filesystems in order */
+ for (NSString *fsName in filesystems) {
+ if (![installedFilesystems containsObject:fsName]) {
+ NSString *fsRoot = availableFileSystems[fsName];
+ if (fsRoot) {
+ [filePlugin registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:fsName root:fsRoot]];
+ [installedFilesystems addObject:fsName];
+ } else {
+ NSLog(@"Unrecognized extra filesystem identifier: %@", fsName);
+ }
+ }
+ }
+}
+
+- (NSDictionary *)getAvailableFileSystems
+{
+ return @{
+ @"documents": self.appDocsPath,
+ @"cache": self.appCachePath,
+ @"bundle": [[NSBundle mainBundle] bundlePath],
+ @"root": @"/"
+ };
+}
+
+- (void)pluginInitialize
+{ filePlugin = self;
+ [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
+
+ fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3];
+
+ // Get the Temporary directory path
+ self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath]; // remove trailing slash from NSTemporaryDirectory()
+ [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]];
+
+ // ~/Library/Application Support/<bundle-id>
+ self.appSupportPath = [self getSupportDirectoryFor:NSApplicationSupportDirectory pathComponents:nil];
+
+ // ~/Library/Application Support/<bundle-id>/files
+ self.appDataPath = [self getSupportDirectoryFor:NSApplicationSupportDirectory pathComponents:@[@"files"]];
+ [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appDataPath]];
+
+ // ~/Documents/
+ self.appDocsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+
+ // ~/Library/Caches/<bundle-id>/files
+ self.appCachePath = [self getSupportDirectoryFor:NSCachesDirectory pathComponents:nil];
+
+ [self registerExtraFileSystems:[self getExtraFileSystemsPreference:self.viewController]
+ fromAvailableSet:[self getAvailableFileSystems]];
+}
+
+- (NSString*) getSupportDirectoryFor: (NSSearchPathDirectory) directory pathComponents: (NSArray*) components {
+ NSError* error;
+ NSFileManager* fm = [NSFileManager defaultManager];
+ NSURL* supportDir = [fm URLForDirectory:directory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
+ if (supportDir == nil) {
+ NSLog(@"unable to get support directory: %@", error);
+ return nil;
+ }
+
+ NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier];
+ NSURL *dirPath = [supportDir URLByAppendingPathComponent:bundleID];
+ for (NSString* pathComponent in components) {
+ dirPath = [dirPath URLByAppendingPathComponent:pathComponent];
+ }
+
+ if (![fm fileExistsAtPath:dirPath.path]) {
+ if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES attributes:nil error:&error]) {
+ NSLog(@"unable to create support directory: %@", error);
+ return nil;
+ }
+ }
+ return dirPath.path;
+}
+
+
+- (CDVFilesystemURL *)fileSystemURLforArg:(NSString *)urlArg
+{
+ CDVFilesystemURL* ret = nil;
+ if ([urlArg hasPrefix:@"file://"]) {
+ /* This looks like a file url. Get the path, and see if any handlers recognize it. */
+ NSURL *fileURL = [NSURL URLWithString:urlArg];
+ NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath];
+ NSString *path = [resolvedFileURL path];
+ ret = [self fileSystemURLforLocalPath:path];
+ } else {
+ ret = [CDVFilesystemURL fileSystemURLWithString:urlArg];
+ }
+ return ret;
+}
+
+- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath
+{
+ CDVFilesystemURL *localURL = nil;
+ NSUInteger shortestFullPath = 0;
+
+ // Try all installed filesystems, in order. Return the most match url.
+ for (id object in self.fileSystems) {
+ if ([object respondsToSelector:@selector(URLforFilesystemPath:)]) {
+ CDVFilesystemURL *url = [object URLforFilesystemPath:localPath];
+ if (url){
+ // A shorter fullPath would imply that the filesystem is a better match for the local path
+ if (!localURL || ([[url fullPath] length] < shortestFullPath)) {
+ localURL = url;
+ shortestFullPath = [[url fullPath] length];
+ }
+ }
+ }
+ }
+ return localURL;
+}
+
+- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath
+{
+ NSFileManager* fMgr = [[NSFileManager alloc] init];
+
+ NSError* __autoreleasing pError = nil;
+
+ NSDictionary* pDict = [fMgr attributesOfFileSystemForPath:appPath error:&pError];
+ NSNumber* pNumAvail = (NSNumber*)[pDict objectForKey:NSFileSystemFreeSize];
+
+ return pNumAvail;
+}
+
+/* Request the File System info
+ *
+ * IN:
+ * arguments[0] - type (number as string)
+ * TEMPORARY = 0, PERSISTENT = 1;
+ * arguments[1] - size
+ *
+ * OUT:
+ * Dictionary representing FileSystem object
+ * name - the human readable directory name
+ * root = DirectoryEntry object
+ * bool isDirectory
+ * bool isFile
+ * string name
+ * string fullPath
+ * fileSystem = FileSystem object - !! ignored because creates circular reference !!
+ */
+
+- (void)requestFileSystem:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* strType = [command argumentAtIndex:0];
+ unsigned long long size = [[command argumentAtIndex:1] longLongValue];
+
+ int type = [strType intValue];
+ CDVPluginResult* result = nil;
+
+ if (type > self.fileSystems.count) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+ NSLog(@"No filesystem of type requested");
+ } else {
+ NSString* fullPath = @"/";
+ // check for avail space for size request
+ NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appSupportPath];
+ // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
+ if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR];
+ } else {
+ NSObject<CDVFileSystem> *rootFs = [self.fileSystems objectAtIndex:type];
+ if (rootFs == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+ NSLog(@"No filesystem of type requested");
+ } else {
+ NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2];
+ [fileSystem setObject:rootFs.name forKey:@"name"];
+ NSDictionary* dirEntry = [self makeEntryForPath:fullPath fileSystemName:rootFs.name isDirectory:YES];
+ [fileSystem setObject:dirEntry forKey:@"root"];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+ }
+ }
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+
+- (void)requestAllFileSystems:(CDVInvokedUrlCommand*)command
+{
+ NSMutableArray* ret = [[NSMutableArray alloc] init];
+ for (NSObject<CDVFileSystem>* root in fileSystems_) {
+ [ret addObject:[self makeEntryForPath:@"/" fileSystemName:root.name isDirectory:YES]];
+ }
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:ret];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)requestAllPaths:(CDVInvokedUrlCommand*)command
+{
+ NSDictionary* ret = @{
+ @"applicationDirectory": [[NSURL fileURLWithPath:[[NSBundle mainBundle] resourcePath]] absoluteString],
+ @"applicationStorageDirectory": [[NSURL fileURLWithPath:self.appSupportPath] absoluteString],
+ @"dataDirectory": [[NSURL fileURLWithPath:self.appDataPath] absoluteString],
+ @"documentsDirectory": [[NSURL fileURLWithPath:self.appDocsPath] absoluteString],
+ @"cacheDirectory": [[NSURL fileURLWithPath:self.appCachePath] absoluteString],
+ @"tempDirectory":[[NSURL fileURLWithPath:self.appTempPath] absoluteString],
+ @"rootDirectory": @"file:///",
+ };
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:ret];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* Creates and returns a dictionary representing an Entry Object
+ *
+ * IN:
+ * NSString* fullPath of the entry
+ * int fsType - FileSystem type
+ * BOOL isDirectory - YES if this is a directory, NO if is a file
+ * OUT:
+ * NSDictionary* Entry object
+ * bool as NSNumber isDirectory
+ * bool as NSNumber isFile
+ * NSString* name - last part of path
+ * NSString* fullPath
+ * NSString* filesystemName - FileSystem name -- actual filesystem will be created on the JS side if necessary, to avoid
+ * creating circular reference (FileSystem contains DirectoryEntry which contains FileSystem.....!!)
+ */
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir
+{
+ NSObject<CDVFileSystem> *fs = [self fileSystemByName:fsName];
+ return [fs makeEntryForPath:fullPath isDirectory:isDir];
+}
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL
+{
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURL];
+ return [fs makeEntryForLocalURL:localURL];
+}
+
+- (NSDictionary *)makeEntryForURL:(NSURL *)URL
+{
+ CDVFilesystemURL* fsURL = [self fileSystemURLforArg:[URL absoluteString]];
+ return [self makeEntryForLocalURL:fsURL];
+}
+
+/*
+ * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
+ * IN
+ * NSString* localURI: Should be an escaped local filesystem URI
+ * OUT
+ * Entry object
+ * bool isDirectory
+ * bool isFile
+ * string name
+ * string fullPath
+ * fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!!
+ */
+- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* localURIstr = [command argumentAtIndex:0];
+ CDVPluginResult* result;
+
+ localURIstr = [self encodePath:localURIstr]; //encode path before resolving
+ CDVFilesystemURL* inputURI = [self fileSystemURLforArg:localURIstr];
+
+ if (inputURI == nil || inputURI.fileSystemName == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+ } else {
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:inputURI];
+ if (fs == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+ } else {
+ result = [fs entryForLocalURI:inputURI];
+ }
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+//encode path with percent escapes
+-(NSString *)encodePath:(NSString *)path
+{
+ NSString *decodedPath = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //decode incase it's already encoded to avoid encoding twice
+ return [decodedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+}
+
+
+/* Part of DirectoryEntry interface, creates or returns the specified directory
+ * IN:
+ * NSString* localURI - local filesystem URI for this directory
+ * NSString* path - directory to be created/returned; may be full path or relative path
+ * NSDictionary* - Flags object
+ * boolean as NSNumber create -
+ * if create is true and directory does not exist, create dir and return directory entry
+ * if create is true and exclusive is true and directory does exist, return error
+ * if create is false and directory does not exist, return error
+ * if create is false and the path represents a file, return error
+ * boolean as NSNumber exclusive - used in conjunction with create
+ * if exclusive is true and create is true - specifies failure if directory already exists
+ *
+ *
+ */
+- (void)getDirectory:(CDVInvokedUrlCommand*)command
+{
+ NSMutableArray* arguments = [NSMutableArray arrayWithArray:command.arguments];
+ NSMutableDictionary* options = nil;
+
+ if ([arguments count] >= 3) {
+ options = [command argumentAtIndex:2 withDefault:nil];
+ }
+ // add getDir to options and call getFile()
+ if (options != nil) {
+ options = [NSMutableDictionary dictionaryWithDictionary:options];
+ } else {
+ options = [NSMutableDictionary dictionaryWithCapacity:1];
+ }
+ [options setObject:[NSNumber numberWithInt:1] forKey:@"getDir"];
+ if ([arguments count] >= 3) {
+ [arguments replaceObjectAtIndex:2 withObject:options];
+ } else {
+ [arguments addObject:options];
+ }
+ CDVInvokedUrlCommand* subCommand =
+ [[CDVInvokedUrlCommand alloc] initWithArguments:arguments
+ callbackId:command.callbackId
+ className:command.className
+ methodName:command.methodName];
+
+ [self getFile:subCommand];
+}
+
+/* Part of DirectoryEntry interface, creates or returns the specified file
+ * IN:
+ * NSString* baseURI - local filesytem URI for the base directory to search
+ * NSString* requestedPath - file to be created/returned; may be absolute path or relative path
+ * NSDictionary* options - Flags object
+ * boolean as NSNumber create -
+ * if create is true and file does not exist, create file and return File entry
+ * if create is true and exclusive is true and file does exist, return error
+ * if create is false and file does not exist, return error
+ * if create is false and the path represents a directory, return error
+ * boolean as NSNumber exclusive - used in conjunction with create
+ * if exclusive is true and create is true - specifies failure if file already exists
+ */
+- (void)getFile:(CDVInvokedUrlCommand*)command
+{
+ NSString* baseURIstr = [command argumentAtIndex:0];
+ CDVFilesystemURL* baseURI = [self fileSystemURLforArg:baseURIstr];
+ NSString* requestedPath = [command argumentAtIndex:1];
+ NSDictionary* options = [command argumentAtIndex:2 withDefault:nil];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:baseURI];
+ CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options];
+
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * Look up the parent Entry containing this Entry.
+ * If this Entry is the root of its filesystem, its parent is itself.
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI
+ * NSMutableDictionary* options
+ * empty
+ */
+- (void)getParent:(CDVInvokedUrlCommand*)command
+{
+ // arguments are URL encoded
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult* result = [fs getParentForURL:localURI];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * set MetaData of entry
+ * Currently we only support "com.apple.MobileBackup" (boolean)
+ */
+- (void)setMetadata:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSDictionary* options = [command argumentAtIndex:1 withDefault:nil];
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult* result = [fs setMetadataForURL:localURI withObject:options];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* removes the directory or file entry
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI
+ *
+ * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir
+ * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file
+ * returns NOT_FOUND_ERR if file or dir is not found
+*/
+- (void)remove:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ CDVPluginResult* result = nil;
+
+ if ([localURI.fullPath isEqualToString:@""]) {
+ // error if try to remove top level (documents or tmp) dir
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+ } else {
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ result = [fs removeFileAtURL:localURI];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* recursively removes the directory
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI
+ *
+ * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir
+ * returns NOT_FOUND_ERR if file or dir is not found
+ */
+- (void)removeRecursively:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ CDVPluginResult* result = nil;
+
+ if ([localURI.fullPath isEqualToString:@""]) {
+ // error if try to remove top level (documents or tmp) dir
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+ } else {
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ result = [fs recursiveRemoveFileAtURL:localURI];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)copyTo:(CDVInvokedUrlCommand*)command
+{
+ [self doCopyMove:command isCopy:YES];
+}
+
+- (void)moveTo:(CDVInvokedUrlCommand*)command
+{
+ [self doCopyMove:command isCopy:NO];
+}
+
+/* Copy/move a file or directory to a new location
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* URL of entry to copy
+ * 1 - NSString* URL of the directory into which to copy/move the entry
+ * 2 - Optionally, the new name of the entry, defaults to the current name
+ * BOOL - bCopy YES if copy, NO if move
+ */
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy
+{
+ NSArray* arguments = command.arguments;
+
+ // arguments
+ NSString* srcURLstr = [command argumentAtIndex:0];
+ NSString* destURLstr = [command argumentAtIndex:1];
+
+ CDVPluginResult *result;
+
+ if (!srcURLstr || !destURLstr) {
+ // either no source or no destination provided
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ CDVFilesystemURL* srcURL = [self fileSystemURLforArg:srcURLstr];
+ CDVFilesystemURL* destURL = [self fileSystemURLforArg:destURLstr];
+
+ NSObject<CDVFileSystem> *srcFs = [self filesystemForURL:srcURL];
+ NSObject<CDVFileSystem> *destFs = [self filesystemForURL:destURL];
+
+ // optional argument; use last component from srcFullPath if new name not provided
+ NSString* newName = ([arguments count] > 2) ? [command argumentAtIndex:2] : [srcURL.url lastPathComponent];
+ if ([newName rangeOfString:@":"].location != NSNotFound) {
+ // invalid chars in new name
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ [destFs copyFileToURL:destURL withName:newName fromFileSystem:srcFs atURL:srcURL copy:bCopy callback:^(CDVPluginResult* result) {
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+
+}
+
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ [fs getFileMetadataForURL:localURI callback:^(CDVPluginResult* result) {
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+}
+
+- (void)readEntries:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult *result = [fs readEntriesAtURL:localURI];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* read and return file data
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* fullPath
+ * 1 - NSString* encoding
+ * 2 - NSString* start
+ * 3 - NSString* end
+ */
+- (void)readAsText:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSString* encoding = [command argumentAtIndex:1];
+ NSInteger start = [[command argumentAtIndex:2] integerValue];
+ NSInteger end = [[command argumentAtIndex:3] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ if (fs == nil) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ // TODO: implement
+ if ([@"UTF-8" caseInsensitiveCompare : encoding] != NSOrderedSame) {
+ NSLog(@"Only UTF-8 encodings are currently supported by readAsText");
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ NSString* str = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSUTF8StringEncoding freeWhenDone:NO];
+ // Check that UTF8 conversion did not fail.
+ if (str != nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:str];
+ result.associatedObject = data;
+ } else {
+ errorCode = ENCODING_ERR;
+ }
+ }
+ if (result == nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+/* Read content of text file and return as base64 encoded data url.
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* fullPath
+ * 1 - NSString* start
+ * 2 - NSString* end
+ *
+ * Determines the mime type from the file extension, returns ENCODING_ERR if mimetype can not be determined.
+ */
+
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSInteger start = [[command argumentAtIndex:1] integerValue];
+ NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ NSString* b64Str = toBase64(data);
+ NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, b64Str];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:output];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+/* Read content of text file and return as an arraybuffer
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* fullPath
+ * 1 - NSString* start
+ * 2 - NSString* end
+ */
+
+- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSInteger start = [[command argumentAtIndex:1] integerValue];
+ NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:data];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+- (void)readAsBinaryString:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ NSInteger start = [[command argumentAtIndex:1] integerValue];
+ NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+ [self.commandDelegate runInBackground:^ {
+ [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ NSString* payload = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSASCIIStringEncoding freeWhenDone:NO];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
+ result.associatedObject = data;
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }];
+ }];
+}
+
+
+- (void)truncate:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ unsigned long long pos = (unsigned long long)[[command argumentAtIndex:1] longLongValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+ CDVPluginResult *result = [fs truncateFileAtURL:localURI atPosition:pos];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* write
+ * IN:
+ * NSArray* arguments
+ * 0 - NSString* localURI of file to write to
+ * 1 - NSString* or NSData* data to write
+ * 2 - NSNumber* position to begin writing
+ */
+- (void)write:(CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^ {
+ NSString* callbackId = command.callbackId;
+
+ // arguments
+ CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+ id argData = [command argumentAtIndex:1];
+ unsigned long long pos = (unsigned long long)[[command argumentAtIndex:2] longLongValue];
+
+ NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+
+ [fs truncateFileAtURL:localURI atPosition:pos];
+ CDVPluginResult *result;
+ if ([argData isKindOfClass:[NSString class]]) {
+ NSData *encData = [argData dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
+ result = [fs writeToFileAtURL:localURI withData:encData append:YES];
+ } else if ([argData isKindOfClass:[NSData class]]) {
+ result = [fs writeToFileAtURL:localURI withData:argData append:YES];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid parameter type"];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }];
+}
+
+#pragma mark Methods for converting between URLs and paths
+
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURL
+{
+ for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
+ if ([fs.name isEqualToString:localURL.fileSystemName]) {
+ if ([fs respondsToSelector:@selector(filesystemPathForURL:)]) {
+ return [fs filesystemPathForURL:localURL];
+ }
+ }
+ }
+ return nil;
+}
+
+#pragma mark Undocumented Filesystem API
+
+- (void)testFileExists:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* argPath = [command argumentAtIndex:0];
+
+ // Get the file manager
+ NSFileManager* fMgr = [NSFileManager defaultManager];
+ NSString* appFile = argPath; // [ self getFullPath: argPath];
+
+ BOOL bExists = [fMgr fileExistsAtPath:appFile];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(bExists ? 1 : 0)];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command
+{
+ // arguments
+ NSString* argPath = [command argumentAtIndex:0];
+
+ // Get the file manager
+ NSFileManager* fMgr = [[NSFileManager alloc] init];
+ NSString* appFile = argPath; // [self getFullPath: argPath];
+ BOOL bIsDir = NO;
+ BOOL bExists = [fMgr fileExistsAtPath:appFile isDirectory:&bIsDir];
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:((bExists && bIsDir) ? 1 : 0)];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+// Returns number of bytes available via callback
+- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command
+{
+ // no arguments
+
+ NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appDocsPath];
+
+ NSString* strFreeSpace = [NSString stringWithFormat:@"%qu", [pNumAvail unsignedLongLongValue]];
+ // NSLog(@"Free space is %@", strFreeSpace );
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:strFreeSpace];
+
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+#pragma mark Compatibility with older File API
+
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+ return [CDVLocalFilesystem getMimeTypeFromPath:fullPath];
+}
+
+- (NSDictionary *)getDirectoryEntry:(NSString *)localPath isDirectory:(BOOL)bDirRequest
+{
+ CDVFilesystemURL *localURL = [self fileSystemURLforLocalPath:localPath];
+ return [self makeEntryForPath:localURL.fullPath fileSystemName:localURL.fileSystemName isDirectory:bDirRequest];
+}
+
+#pragma mark Internal methods for testing
+// Internal methods for testing: Get the on-disk location of a local filesystem url.
+// [Currently used for testing file-transfer]
+
+- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command
+{
+ CDVFilesystemURL* localURL = [self fileSystemURLforArg:command.arguments[0]];
+
+ NSString* fsPath = [self filesystemPathForURL:localURL];
+ CDVPluginResult* result;
+ if (fsPath) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:fsPath];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot resolve URL to a file"];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h
new file mode 100644
index 00000000..a0186c85
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h
@@ -0,0 +1,32 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+
+@interface CDVLocalFilesystem : NSObject<CDVFileSystem> {
+ NSString *_name;
+ NSString *_fsRoot;
+}
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot;
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+
+@property (nonatomic,strong) NSString *fsRoot;
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m
new file mode 100644
index 00000000..016e3999
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m
@@ -0,0 +1,733 @@
+/*
+ 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.
+ */
+
+#import "CDVFile.h"
+#import "CDVLocalFilesystem.h"
+#import <sys/xattr.h>
+
+@implementation CDVLocalFilesystem
+@synthesize name=_name, fsRoot=_fsRoot, urlTransformer;
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot
+{
+ if (self) {
+ _name = name;
+ _fsRoot = fsRoot;
+ }
+ return self;
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
+ * URI represents a non-existent path, or is unrecognized or otherwise malformed.
+ */
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
+{
+ CDVPluginResult* result = nil;
+ NSDictionary* entry = [self makeEntryForLocalURL:url];
+ if (entry) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry];
+ } else {
+ // return NOT_FOUND_ERR
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+ return result;
+}
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url {
+ NSString *path = [self filesystemPathForURL:url];
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL isDir = NO;
+ // see if exists and is file or dir
+ BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
+ if (bExists) {
+ return [self makeEntryForPath:url.fullPath isDirectory:isDir];
+ } else {
+ return nil;
+ }
+}
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir
+{
+ NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+ NSString* lastPart = [[self stripQueryParametersFromPath:fullPath] lastPathComponent];
+ if (isDir && ![fullPath hasSuffix:@"/"]) {
+ fullPath = [fullPath stringByAppendingString:@"/"];
+ }
+ [dirEntry setObject:[NSNumber numberWithBool:!isDir] forKey:@"isFile"];
+ [dirEntry setObject:[NSNumber numberWithBool:isDir] forKey:@"isDirectory"];
+ [dirEntry setObject:fullPath forKey:@"fullPath"];
+ [dirEntry setObject:lastPart forKey:@"name"];
+ [dirEntry setObject:self.name forKey: @"filesystemName"];
+
+ NSURL* nativeURL = [NSURL fileURLWithPath:[self filesystemPathForFullPath:fullPath]];
+ if (self.urlTransformer) {
+ nativeURL = self.urlTransformer(nativeURL);
+ }
+
+ dirEntry[@"nativeURL"] = [nativeURL absoluteString];
+
+ return dirEntry;
+}
+
+- (NSString *)stripQueryParametersFromPath:(NSString *)fullPath
+{
+ NSRange questionMark = [fullPath rangeOfString:@"?"];
+ if (questionMark.location != NSNotFound) {
+ return [fullPath substringWithRange:NSMakeRange(0,questionMark.location)];
+ }
+ return fullPath;
+}
+
+- (NSString *)filesystemPathForFullPath:(NSString *)fullPath
+{
+ NSString *path = nil;
+ NSString *strippedFullPath = [self stripQueryParametersFromPath:fullPath];
+ path = [NSString stringWithFormat:@"%@%@", self.fsRoot, strippedFullPath];
+ if ([path length] > 1 && [path hasSuffix:@"/"]) {
+ path = [path substringToIndex:([path length]-1)];
+ }
+ return path;
+}
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ * The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ * or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)url
+{
+ return [self filesystemPathForFullPath:url.fullPath];
+}
+
+- (CDVFilesystemURL *)URLforFullPath:(NSString *)fullPath
+{
+ if (fullPath) {
+ NSString* escapedPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ if ([fullPath hasPrefix:@"/"]) {
+ return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@%@", kCDVFilesystemURLPrefix, self.name, escapedPath]];
+ }
+ return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@/%@", kCDVFilesystemURLPrefix, self.name, escapedPath]];
+ }
+ return nil;
+}
+
+- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path
+{
+ return [self URLforFullPath:[self fullPathForFileSystemPath:path]];
+
+}
+
+- (NSString *)normalizePath:(NSString *)rawPath
+{
+ // If this is an absolute path, the first path component will be '/'. Skip it if that's the case
+ BOOL isAbsolutePath = [rawPath hasPrefix:@"/"];
+ if (isAbsolutePath) {
+ rawPath = [rawPath substringFromIndex:1];
+ }
+ NSMutableArray *components = [NSMutableArray arrayWithArray:[rawPath pathComponents]];
+ for (int index = 0; index < [components count]; ++index) {
+ if ([[components objectAtIndex:index] isEqualToString:@".."]) {
+ [components removeObjectAtIndex:index];
+ if (index > 0) {
+ [components removeObjectAtIndex:index-1];
+ --index;
+ }
+ }
+ }
+
+ if (isAbsolutePath) {
+ return [NSString stringWithFormat:@"/%@", [components componentsJoinedByString:@"/"]];
+ } else {
+ return [components componentsJoinedByString:@"/"];
+ }
+
+
+}
+
+- (BOOL)valueForKeyIsNumber:(NSDictionary*)dict key:(NSString*)key
+{
+ BOOL bNumber = NO;
+ NSObject* value = dict[key];
+ if (value) {
+ bNumber = [value isKindOfClass:[NSNumber class]];
+ }
+ return bNumber;
+}
+
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+ CDVPluginResult* result = nil;
+ BOOL bDirRequest = NO;
+ BOOL create = NO;
+ BOOL exclusive = NO;
+ int errorCode = 0; // !!! risky - no error code currently defined for 0
+
+ if ([self valueForKeyIsNumber:options key:@"create"]) {
+ create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
+ }
+ if ([self valueForKeyIsNumber:options key:@"exclusive"]) {
+ exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
+ }
+ if ([self valueForKeyIsNumber:options key:@"getDir"]) {
+ // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
+ bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
+ }
+ // see if the requested path has invalid characters - should we be checking for more than just ":"?
+ if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
+ errorCode = ENCODING_ERR;
+ } else {
+ // Build new fullPath for the requested resource.
+ // We concatenate the two paths together, and then scan the resulting string to remove
+ // parent ("..") references. Any parent references at the beginning of the string are
+ // silently removed.
+ NSString *combinedPath = [baseURI.fullPath stringByAppendingPathComponent:requestedPath];
+ combinedPath = [self normalizePath:combinedPath];
+ CDVFilesystemURL* requestedURL = [self URLforFullPath:combinedPath];
+
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir;
+ BOOL bExists = [fileMgr fileExistsAtPath:[self filesystemPathForURL:requestedURL] isDirectory:&bIsDir];
+ if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
+ // path exists and is not of requested type - return TYPE_MISMATCH_ERR
+ errorCode = TYPE_MISMATCH_ERR;
+ } else if (!bExists && (create == NO)) {
+ // path does not exist and create is false - return NOT_FOUND_ERR
+ errorCode = NOT_FOUND_ERR;
+ } else if (bExists && (create == YES) && (exclusive == YES)) {
+ // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
+ errorCode = PATH_EXISTS_ERR;
+ } else {
+ // if bExists and create == YES - just return data
+ // if bExists and create == NO - just return data
+ // if !bExists and create == YES - create and return data
+ BOOL bSuccess = YES;
+ NSError __autoreleasing* pError = nil;
+ if (!bExists && (create == YES)) {
+ if (bDirRequest) {
+ // create the dir
+ bSuccess = [fileMgr createDirectoryAtPath:[self filesystemPathForURL:requestedURL] withIntermediateDirectories:NO attributes:nil error:&pError];
+ } else {
+ // create the empty file
+ bSuccess = [fileMgr createFileAtPath:[self filesystemPathForURL:requestedURL] contents:nil attributes:nil];
+ }
+ }
+ if (!bSuccess) {
+ errorCode = ABORT_ERR;
+ if (pError) {
+ NSLog(@"error creating directory: %@", [pError localizedDescription]);
+ }
+ } else {
+ // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
+ // file existed or was created
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath isDirectory:bDirRequest]];
+ }
+ } // are all possible conditions met?
+ }
+
+ if (errorCode > 0) {
+ // create error callback
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+ return result;
+
+}
+
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+ CDVPluginResult* result = nil;
+ CDVFilesystemURL *newURI = nil;
+ if ([localURI.fullPath isEqualToString:@""]) {
+ // return self
+ newURI = localURI;
+ } else {
+ newURI = [CDVFilesystemURL fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
+ }
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir;
+ BOOL bExists = [fileMgr fileExistsAtPath:[self filesystemPathForURL:newURI] isDirectory:&bIsDir];
+ if (bExists) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath isDirectory:bIsDir]];
+ } else {
+ // invalid path or file does not exist
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+ return result;
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+ BOOL ok = NO;
+
+ NSString* filePath = [self filesystemPathForURL:localURI];
+ // we only care about this iCloud key for now.
+ // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
+ NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
+ id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
+
+ if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
+// todo: fix me
+// if (IsAtLeastiOSVersion(@"5.1")) {
+// NSURL* url = [NSURL fileURLWithPath:filePath];
+// NSError* __autoreleasing error = nil;
+//
+// ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
+// } else { // below 5.1 (deprecated - only really supported in 5.01)
+// u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
+// if (value == 0) { // remove the attribute (allow backup, the default)
+// ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
+// } else { // set the attribute (skip backup)
+// ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+// }
+// }
+ }
+
+ if (ok) {
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+ }
+}
+
+/* remove the file or directory (recursively)
+ * IN:
+ * NSString* fullPath - the full path to the file or directory to be removed
+ * NSString* callbackId
+ * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
+ */
+
+- (CDVPluginResult*)doRemove:(NSString*)fullPath
+{
+ CDVPluginResult* result = nil;
+ BOOL bSuccess = NO;
+ NSError* __autoreleasing pError = nil;
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+ @try {
+ bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
+ if (bSuccess) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ // see if we can give a useful error
+ CDVFileError errorCode = ABORT_ERR;
+ NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
+ if ([pError code] == NSFileNoSuchFileError) {
+ errorCode = NOT_FOUND_ERR;
+ } else if ([pError code] == NSFileWriteNoPermissionError) {
+ errorCode = NO_MODIFICATION_ALLOWED_ERR;
+ }
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+ } @catch(NSException* e) { // NSInvalidArgumentException if path is . or ..
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
+ }
+
+ return result;
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+ NSString *fileSystemPath = [self filesystemPathForURL:localURI];
+
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir = NO;
+ BOOL bExists = [fileMgr fileExistsAtPath:fileSystemPath isDirectory:&bIsDir];
+ if (!bExists) {
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+ if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fileSystemPath error:nil] count] != 0)) {
+ // dir is not empty
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+ }
+ return [self doRemove:fileSystemPath];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+ NSString *fileSystemPath = [self filesystemPathForURL:localURI];
+ return [self doRemove:fileSystemPath];
+}
+
+/*
+ * IN
+ * NSString localURI
+ * OUT
+ * NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ * The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ * or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
+{
+ if ([fsPath hasPrefix:self.fsRoot]) {
+ return [fsPath substringFromIndex:[self.fsRoot length]];
+ }
+ return nil;
+}
+
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ NSError* __autoreleasing error = nil;
+ NSString *fileSystemPath = [self filesystemPathForURL:localURI];
+
+ NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fileSystemPath error:&error];
+
+ if (contents) {
+ NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1];
+ if ([contents count] > 0) {
+ // create an Entry (as JSON) for each file/dir
+ for (NSString* name in contents) {
+ // see if is dir or file
+ NSString* entryPath = [fileSystemPath stringByAppendingPathComponent:name];
+ BOOL bIsDir = NO;
+ [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
+ NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] isDirectory:bIsDir];
+ [entries addObject:entryDict];
+ }
+ }
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries];
+ } else {
+ // assume not found but could check error for more specific error conditions
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ }
+}
+
+- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos
+{
+ unsigned long long newPos = 0UL;
+
+ NSFileHandle* file = [NSFileHandle fileHandleForWritingAtPath:filePath];
+
+ if (file) {
+ [file truncateFileAtOffset:(unsigned long long)pos];
+ newPos = [file offsetInFile];
+ [file synchronizeFile];
+ [file closeFile];
+ }
+ return newPos;
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+ unsigned long long newPos = [self truncateFile:[self filesystemPathForURL:localURI] atPosition:pos];
+ return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)newPos];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+ NSString *filePath = [self filesystemPathForURL:localURL];
+
+ CDVPluginResult* result = nil;
+ CDVFileError errCode = INVALID_MODIFICATION_ERR;
+ int bytesWritten = 0;
+
+ if (filePath) {
+ NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend];
+ if (fileStream) {
+ NSUInteger len = [encData length];
+ if (len == 0) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:(double)len];
+ } else {
+ [fileStream open];
+
+ bytesWritten = (int)[fileStream write:[encData bytes] maxLength:len];
+
+ [fileStream close];
+ if (bytesWritten > 0) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten];
+ // } else {
+ // can probably get more detailed error info via [fileStream streamError]
+ // errCode already set to INVALID_MODIFICATION_ERR;
+ // bytesWritten = 0; // may be set to -1 on error
+ }
+ }
+ } // else fileStream not created return INVALID_MODIFICATION_ERR
+ } else {
+ // invalid filePath
+ errCode = NOT_FOUND_ERR;
+ }
+ if (!result) {
+ // was an error
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+ }
+ return result;
+}
+
+/**
+ * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
+ * or attempted to copy a directory into a directory that it contains directly or indirectly.
+ *
+ * IN:
+ * NSString* srcDir
+ * NSString* destinationDir
+ * OUT:
+ * YES copy/ move is allows
+ * NO move is onto itself
+ */
+- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
+{
+ // This weird test is to determine if we are copying or moving a directory into itself.
+ // Copy /Documents/myDir to /Documents/myDir-backup is okay but
+ // Copy /Documents/myDir to /Documents/myDir/backup not okay
+ BOOL copyOK = YES;
+ NSRange range = [dest rangeOfString:src];
+
+ if (range.location != NSNotFound) {
+ NSRange testRange = {range.length - 1, ([dest length] - range.length)};
+ NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
+ if (resultRange.location != NSNotFound) {
+ copyOK = NO;
+ }
+ }
+ return copyOK;
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+ NSFileManager *fileMgr = [[NSFileManager alloc] init];
+ NSString *destRootPath = [self filesystemPathForURL:destURL];
+ BOOL bDestIsDir = NO;
+ BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
+
+ NSString *newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
+ NSString *newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
+
+ BOOL bNewIsDir = NO;
+ BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
+
+ CDVPluginResult *result = nil;
+ int errCode = 0;
+
+ if (!bDestExists) {
+ // the destination root does not exist
+ errCode = NOT_FOUND_ERR;
+ }
+
+ else if ([srcFs isKindOfClass:[CDVLocalFilesystem class]]) {
+ /* Same FS, we can shortcut with NSFileManager operations */
+ NSString *srcFullPath = [srcFs filesystemPathForURL:srcURL];
+
+ BOOL bSrcIsDir = NO;
+ BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
+
+ if (!bSrcExists) {
+ // the source does not exist
+ errCode = NOT_FOUND_ERR;
+ } else if ([newFileSystemPath isEqualToString:srcFullPath]) {
+ // source and destination can not be the same
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
+ // can't copy/move dir to file
+ errCode = INVALID_MODIFICATION_ERR;
+ } else { // no errors yet
+ NSError* __autoreleasing error = nil;
+ BOOL bSuccess = NO;
+ if (bCopy) {
+ if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+ // can't copy dir into self
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bNewExists) {
+ // the full destination should NOT already exist if a copy
+ errCode = PATH_EXISTS_ERR;
+ } else {
+ bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+ }
+ } else { // move
+ // iOS requires that destination must not exist before calling moveTo
+ // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents
+ //
+ if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
+ // can't move a file to directory
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+ // can't move a dir into itself
+ errCode = INVALID_MODIFICATION_ERR;
+ } else if (bNewExists) {
+ if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
+ // can't move dir to a dir that is not empty
+ errCode = INVALID_MODIFICATION_ERR;
+ newFileSystemPath = nil; // so we won't try to move
+ } else {
+ // remove destination so can perform the moveItemAtPath
+ bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
+ if (!bSuccess) {
+ errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
+ newFileSystemPath = nil;
+ }
+ }
+ } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
+ // can't move a directory inside itself or to any child at any depth;
+ errCode = INVALID_MODIFICATION_ERR;
+ newFileSystemPath = nil;
+ }
+
+ if (newFileSystemPath != nil) {
+ bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+ }
+ }
+ if (bSuccess) {
+ // should verify it is there and of the correct type???
+ NSDictionary* newEntry = [self makeEntryForPath:newFullPath isDirectory:bSrcIsDir];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+ } else {
+ if (error) {
+ if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
+ errCode = NOT_READABLE_ERR;
+ } else if ([error code] == NSFileWriteOutOfSpaceError) {
+ errCode = QUOTA_EXCEEDED_ERR;
+ } else if ([error code] == NSFileWriteNoPermissionError) {
+ errCode = NO_MODIFICATION_ALLOWED_ERR;
+ }
+ }
+ }
+ }
+ } else {
+ // Need to copy the hard way
+ [srcFs readFileAtURL:srcURL start:0 end:-1 callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+ CDVPluginResult* result = nil;
+ if (data != nil) {
+ BOOL bSuccess = [data writeToFile:newFileSystemPath atomically:YES];
+ if (bSuccess) {
+ // should verify it is there and of the correct type???
+ NSDictionary* newEntry = [self makeEntryForPath:newFullPath isDirectory:NO];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ABORT_ERR];
+ }
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+ }
+ callback(result);
+ }];
+ return; // Async IO; return without callback.
+ }
+ if (result == nil) {
+ if (!errCode) {
+ errCode = INVALID_MODIFICATION_ERR; // Catch-all default
+ }
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
+ }
+ callback(result);
+}
+
+/* helper function to get the mimeType from the file extension
+ * IN:
+ * NSString* fullPath - filename (may include path)
+ * OUT:
+ * NSString* the mime type as type/subtype. nil if not able to determine
+ */
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+ NSString* mimeType = nil;
+
+ if (fullPath) {
+ CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+ if (typeId) {
+ mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+ if (!mimeType) {
+ // special case for m4a
+ if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
+ mimeType = @"audio/mp4";
+ } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+ mimeType = @"audio/wav";
+ } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
+ mimeType = @"text/css";
+ }
+ }
+ CFRelease(typeId);
+ }
+ }
+ return mimeType;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+ NSString *path = [self filesystemPathForURL:localURL];
+
+ NSString* mimeType = [CDVLocalFilesystem getMimeTypeFromPath:path];
+ if (mimeType == nil) {
+ mimeType = @"*/*";
+ }
+ NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:path];
+ if (start > 0) {
+ [file seekToFileOffset:start];
+ }
+
+ NSData* readData;
+ if (end < 0) {
+ readData = [file readDataToEndOfFile];
+ } else {
+ readData = [file readDataOfLength:(end - start)];
+ }
+ [file closeFile];
+
+ callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR);
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+ NSString *path = [self filesystemPathForURL:localURL];
+ CDVPluginResult *result;
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+ NSError* __autoreleasing error = nil;
+ NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error];
+
+ if (fileAttrs) {
+
+ // create dictionary of file info
+ NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+
+ [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+ [fileInfo setObject:@"" forKey:@"type"]; // can't easily get the mimetype unless create URL, send request and read response so skipping
+ [fileInfo setObject:[path lastPathComponent] forKey:@"name"];
+
+ // Ensure that directories (and other non-regular files) report size of 0
+ unsigned long long size = ([fileAttrs fileType] == NSFileTypeRegular ? [fileAttrs fileSize] : 0);
+ [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:size] forKey:@"size"];
+
+ NSDate* modDate = [fileAttrs fileModificationDate];
+ if (modDate) {
+ [fileInfo setObject:[NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000] forKey:@"lastModifiedDate"];
+ }
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+
+ } else {
+ // didn't get fileAttribs
+ CDVFileError errorCode = ABORT_ERR;
+ NSLog(@"error getting metadata: %@", [error localizedDescription]);
+ if ([error code] == NSFileNoSuchFileError || [error code] == NSFileReadNoSuchFileError) {
+ errorCode = NOT_FOUND_ERR;
+ }
+ // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
+ }
+
+ callback(result);
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-file/src/windows/FileProxy.js b/StoneIsland/plugins/cordova-plugin-file/src/windows/FileProxy.js
new file mode 100644
index 00000000..d829d775
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/src/windows/FileProxy.js
@@ -0,0 +1,1190 @@
+/*
+ *
+ * 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 Windows, WinJS, MSApp */
+
+var File = require('./File');
+var FileError = require('./FileError');
+var Flags = require('./Flags');
+var FileSystem = require('./FileSystem');
+var LocalFileSystem = require('./LocalFileSystem');
+var utils = require('cordova/utils');
+
+function Entry (isFile, isDirectory, name, fullPath, filesystemName, nativeURL) {
+ this.isFile = !!isFile;
+ this.isDirectory = !!isDirectory;
+ this.name = name || '';
+ this.fullPath = fullPath || '';
+ this.filesystemName = filesystemName || null;
+ this.nativeURL = nativeURL || null;
+}
+
+var FileEntry = function (name, fullPath, filesystemName, nativeURL) {
+ FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, filesystemName, nativeURL]);
+};
+
+utils.extend(FileEntry, Entry);
+
+var DirectoryEntry = function (name, fullPath, filesystemName, nativeURL) {
+ DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath, filesystemName, nativeURL);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+var getFolderFromPathAsync = Windows.Storage.StorageFolder.getFolderFromPathAsync;
+var getFileFromPathAsync = Windows.Storage.StorageFile.getFileFromPathAsync;
+
+function writeBytesAsync (storageFile, data, position) {
+ return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite)
+ .then(function (output) {
+ output.seek(position);
+ var dataWriter = new Windows.Storage.Streams.DataWriter(output);
+ dataWriter.writeBytes(data);
+ return dataWriter.storeAsync().then(function (size) {
+ output.size = position + size;
+ return dataWriter.flushAsync().then(function () {
+ output.close();
+ return size;
+ });
+ });
+ });
+}
+
+function writeTextAsync (storageFile, data, position) {
+ return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite)
+ .then(function (output) {
+ output.seek(position);
+ var dataWriter = new Windows.Storage.Streams.DataWriter(output);
+ dataWriter.writeString(data);
+ return dataWriter.storeAsync().then(function (size) {
+ output.size = position + size;
+ return dataWriter.flushAsync().then(function () {
+ output.close();
+ return size;
+ });
+ });
+ });
+}
+
+function writeBlobAsync (storageFile, data, position) {
+ return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite)
+ .then(function (output) {
+ output.seek(position);
+ var dataSize = data.size;
+ var input = (data.detachStream || data.msDetachStream).call(data);
+
+ // Copy the stream from the blob to the File stream
+ return Windows.Storage.Streams.RandomAccessStream.copyAsync(input, output)
+ .then(function () {
+ output.size = position + dataSize;
+ return output.flushAsync().then(function () {
+ input.close();
+ output.close();
+
+ return dataSize;
+ });
+ });
+ });
+}
+
+function writeArrayBufferAsync (storageFile, data, position) {
+ return writeBlobAsync(storageFile, new Blob([data]), position); // eslint-disable-line no-undef
+}
+
+function cordovaPathToNative (path) {
+ // turn / into \\
+ var cleanPath = path.replace(/\//g, '\\');
+ // turn \\ into \
+ cleanPath = cleanPath.replace(/\\+/g, '\\');
+ return cleanPath;
+}
+
+function nativePathToCordova (path) {
+ var cleanPath = path.replace(/\\/g, '/');
+ return cleanPath;
+}
+
+var driveRE = new RegExp('^[/]*([A-Z]:)');
+var invalidNameRE = /[\\?*|"<>:]/;
+function validName (name) {
+ return !invalidNameRE.test(name.replace(driveRE, ''));
+}
+
+function sanitize (path) {
+ var slashesRE = new RegExp('/{2,}', 'g');
+ var components = path.replace(slashesRE, '/').split(/\/+/);
+ // Remove double dots, use old school array iteration instead of RegExp
+ // since it is impossible to debug them
+ for (var index = 0; index < components.length; ++index) {
+ if (components[index] === '..') {
+ components.splice(index, 1);
+ if (index > 0) {
+ // if we're not in the start of array then remove preceeding path component,
+ // In case if relative path points above the root directory, just ignore double dots
+ // See file.spec.111 should not traverse above above the root directory for test case
+ components.splice(index - 1, 1);
+ --index;
+ }
+ }
+ }
+ return components.join('/');
+}
+
+var WinFS = function (name, root) {
+ this.winpath = root.winpath;
+ if (this.winpath && !/\/$/.test(this.winpath)) {
+ this.winpath += '/';
+ }
+ this.makeNativeURL = function (path) {
+ // CB-11848: This RE supposed to match all leading slashes in sanitized path.
+ // Removing leading slash to avoid duplicating because this.root.nativeURL already has trailing slash
+ var regLeadingSlashes = /^\/*/;
+ var sanitizedPath = sanitize(path.replace(':', '%3A')).replace(regLeadingSlashes, '');
+ return FileSystem.encodeURIPath(this.root.nativeURL + sanitizedPath);
+ };
+ root.fullPath = '/';
+ if (!root.nativeURL) { root.nativeURL = 'file://' + sanitize(this.winpath + root.fullPath).replace(':', '%3A'); }
+ WinFS.__super__.constructor.call(this, name, root);
+};
+
+utils.extend(WinFS, FileSystem);
+
+WinFS.prototype.__format__ = function (fullPath) {
+ var path = sanitize('/' + this.name + (fullPath[0] === '/' ? '' : '/') + FileSystem.encodeURIPath(fullPath));
+ return 'cdvfile://localhost' + path;
+};
+
+var windowsPaths = {
+ dataDirectory: 'ms-appdata:///local/',
+ cacheDirectory: 'ms-appdata:///temp/',
+ tempDirectory: 'ms-appdata:///temp/',
+ syncedDataDirectory: 'ms-appdata:///roaming/',
+ applicationDirectory: 'ms-appx:///',
+ applicationStorageDirectory: 'ms-appx:///'
+};
+
+var AllFileSystems;
+
+function getAllFS () {
+ if (!AllFileSystems) {
+ AllFileSystems = {
+ 'persistent':
+ Object.freeze(new WinFS('persistent', {
+ name: 'persistent',
+ nativeURL: 'ms-appdata:///local',
+ winpath: nativePathToCordova(Windows.Storage.ApplicationData.current.localFolder.path)
+ })),
+ 'temporary':
+ Object.freeze(new WinFS('temporary', {
+ name: 'temporary',
+ nativeURL: 'ms-appdata:///temp',
+ winpath: nativePathToCordova(Windows.Storage.ApplicationData.current.temporaryFolder.path)
+ })),
+ 'application':
+ Object.freeze(new WinFS('application', {
+ name: 'application',
+ nativeURL: 'ms-appx:///',
+ winpath: nativePathToCordova(Windows.ApplicationModel.Package.current.installedLocation.path)
+ })),
+ 'root':
+ Object.freeze(new WinFS('root', {
+ name: 'root',
+ // nativeURL: 'file:///'
+ winpath: ''
+ }))
+ };
+ }
+ return AllFileSystems;
+}
+
+function getFS (name) {
+ return getAllFS()[name];
+}
+
+FileSystem.prototype.__format__ = function (fullPath) {
+ return getFS(this.name).__format__(fullPath);
+};
+
+require('./fileSystems').getFs = function (name, callback) {
+ setTimeout(function () { callback(getFS(name)); });
+};
+
+function getFilesystemFromPath (path) {
+ var res;
+ var allfs = getAllFS();
+ Object.keys(allfs).some(function (fsn) {
+ var fs = allfs[fsn];
+ if (path.indexOf(fs.winpath) === 0) { res = fs; }
+ return res;
+ });
+ return res;
+}
+
+var msapplhRE = new RegExp('^ms-appdata://localhost/');
+function pathFromURL (url) {
+ url = url.replace(msapplhRE, 'ms-appdata:///');
+ var path = decodeURIComponent(url);
+ // support for file name with parameters
+ if (/\?/g.test(path)) {
+ path = String(path).split('?')[0];
+ }
+ if (path.indexOf('file:/') === 0) {
+ if (path.indexOf('file://') !== 0) {
+ url = 'file:///' + url.substr(6);
+ }
+ }
+
+ ['file://', 'ms-appdata:///', 'ms-appx://', 'cdvfile://localhost/'].every(function (p) {
+ if (path.indexOf(p) !== 0) { return true; }
+ var thirdSlash = path.indexOf('/', p.length);
+ if (thirdSlash < 0) {
+ path = '';
+ } else {
+ path = sanitize(path.substr(thirdSlash));
+ }
+ });
+
+ return path.replace(driveRE, '$1');
+}
+
+function getFilesystemFromURL (url) {
+ url = url.replace(msapplhRE, 'ms-appdata:///');
+ var res;
+ if (url.indexOf('file:/') === 0) { res = getFilesystemFromPath(pathFromURL(url)); } else {
+ var allfs = getAllFS();
+ Object.keys(allfs).every(function (fsn) {
+ var fs = allfs[fsn];
+ if (url.indexOf(fs.root.nativeURL) === 0 ||
+ url.indexOf('cdvfile://localhost/' + fs.name + '/') === 0) {
+ res = fs;
+ return false;
+ }
+ return true;
+ });
+ }
+ return res;
+}
+
+function getFsPathForWinPath (fs, wpath) {
+ var path = nativePathToCordova(wpath);
+ if (path.indexOf(fs.winpath) !== 0) { return null; }
+ return path.replace(fs.winpath, '/');
+}
+
+var WinError = {
+ invalidArgument: -2147024809,
+ fileNotFound: -2147024894,
+ accessDenied: -2147024891
+};
+
+function openPath (path, ops) {
+ ops = ops || {};
+ return new WinJS.Promise(function (complete, failed) {
+ getFileFromPathAsync(path).done(
+ function (file) {
+ complete({file: file});
+ },
+ function (err) {
+ if (err.number !== WinError.fileNotFound && err.number !== WinError.invalidArgument) { failed(FileError.NOT_READABLE_ERR); }
+ getFolderFromPathAsync(path)
+ .done(
+ function (dir) {
+ if (!ops.getContent) { complete({folder: dir}); } else {
+ WinJS.Promise.join({
+ files: dir.getFilesAsync(),
+ folders: dir.getFoldersAsync()
+ }).done(
+ function (a) {
+ complete({
+ folder: dir,
+ files: a.files,
+ folders: a.folders
+ });
+ },
+ function (err) { // eslint-disable-line handle-callback-err
+ failed(FileError.NOT_READABLE_ERR);
+ }
+ );
+ }
+ },
+ function (err) {
+ if (err.number === WinError.fileNotFound || err.number === WinError.invalidArgument) { complete({}); } else { failed(FileError.NOT_READABLE_ERR); }
+ }
+ );
+ }
+ );
+ });
+}
+
+function copyFolder (src, dst, name) {
+ name = name || src.name;
+ return new WinJS.Promise(function (complete, failed) {
+ WinJS.Promise.join({
+ fld: dst.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists),
+ files: src.getFilesAsync(),
+ folders: src.getFoldersAsync()
+ }).done(
+ function (the) {
+ if (!(the.files.length || the.folders.length)) {
+ complete();
+ return;
+ }
+ var todo = the.files.length;
+ var copyfolders = function () {
+ if (!(todo--)) {
+ complete();
+ return;
+ }
+ copyFolder(the.folders[todo], dst)
+ .done(function () { copyfolders(); }, failed);
+ };
+ var copyfiles = function () {
+ if (!(todo--)) {
+ todo = the.folders.length;
+ copyfolders();
+ return;
+ }
+ the.files[todo].copyAsync(the.fld)
+ .done(function () { copyfiles(); }, failed);
+ };
+ copyfiles();
+ },
+ failed
+ );
+ });
+}
+
+function moveFolder (src, dst, name) {
+ name = name || src.name;
+ return new WinJS.Promise(function (complete, failed) {
+ WinJS.Promise.join({
+ fld: dst.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists),
+ files: src.getFilesAsync(),
+ folders: src.getFoldersAsync()
+ }).done(
+ function (the) {
+ if (!(the.files.length || the.folders.length)) {
+ complete();
+ return;
+ }
+ var todo = the.files.length;
+ var movefolders = function () {
+ if (!(todo--)) {
+ src.deleteAsync().done(complete, failed);
+ return;
+ }
+ moveFolder(the.folders[todo], the.fld)
+ .done(movefolders, failed);
+ };
+ var movefiles = function () {
+ if (!(todo--)) {
+ todo = the.folders.length;
+ movefolders();
+ return;
+ }
+ the.files[todo].moveAsync(the.fld)
+ .done(function () { movefiles(); }, failed);
+ };
+ movefiles();
+ },
+ failed
+ );
+ });
+}
+
+function transport (success, fail, args, ops) { // ["fullPath","parent", "newName"]
+ var src = args[0];
+ var parent = args[1];
+ var name = args[2];
+
+ var srcFS = getFilesystemFromURL(src);
+ var dstFS = getFilesystemFromURL(parent);
+ var srcPath = pathFromURL(src);
+ var dstPath = pathFromURL(parent);
+ if (!(srcFS && dstFS && validName(name))) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+
+ var srcWinPath = cordovaPathToNative(sanitize(srcFS.winpath + srcPath));
+ var dstWinPath = cordovaPathToNative(sanitize(dstFS.winpath + dstPath));
+ var tgtFsPath = sanitize(dstPath + '/' + name);
+ var tgtWinPath = cordovaPathToNative(sanitize(dstFS.winpath + dstPath + '/' + name));
+ if (srcWinPath === dstWinPath || srcWinPath === tgtWinPath) {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ return;
+ }
+
+ WinJS.Promise.join({
+ src: openPath(srcWinPath),
+ dst: openPath(dstWinPath),
+ tgt: openPath(tgtWinPath, {getContent: true})
+ })
+ .done(
+ function (the) {
+ if ((!the.dst.folder) || !(the.src.folder || the.src.file)) {
+ fail(FileError.NOT_FOUND_ERR);
+ return;
+ }
+ if ((the.src.folder && the.tgt.file) ||
+ (the.src.file && the.tgt.folder) ||
+ (the.tgt.folder && (the.tgt.files.length || the.tgt.folders.length))) {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ return;
+ }
+ if (the.src.file) {
+ ops.fileOp(the.src.file, the.dst.folder, name, Windows.Storage.NameCollisionOption.replaceExisting)
+ .done(
+ function (storageFile) {
+ success(new FileEntry(
+ name,
+ tgtFsPath,
+ dstFS.name,
+ dstFS.makeNativeURL(tgtFsPath)
+ ));
+ },
+ function (err) { // eslint-disable-line handle-callback-err
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+ } else {
+ ops.folderOp(the.src.folder, the.dst.folder, name).done(
+ function () {
+ success(new DirectoryEntry(
+ name,
+ tgtFsPath,
+ dstFS.name,
+ dstFS.makeNativeURL(tgtFsPath)
+ ));
+ },
+ function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+ }
+ },
+ function (err) { // eslint-disable-line handle-callback-err
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+}
+
+module.exports = {
+ requestAllFileSystems: function () {
+ return getAllFS();
+ },
+ requestAllPaths: function (success) {
+ success(windowsPaths);
+ },
+ getFileMetadata: function (success, fail, args) {
+ module.exports.getMetadata(success, fail, args);
+ },
+
+ getMetadata: function (success, fail, args) {
+ var fs = getFilesystemFromURL(args[0]);
+ var path = pathFromURL(args[0]);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var fullPath = cordovaPathToNative(fs.winpath + path);
+
+ var getMetadataForFile = function (storageFile) {
+ storageFile.getBasicPropertiesAsync().then(
+ function (basicProperties) {
+ success(new File(storageFile.name, storageFile.path, storageFile.fileType, basicProperties.dateModified, basicProperties.size));
+ }, function () {
+ fail(FileError.NOT_READABLE_ERR);
+ }
+ );
+ };
+
+ var getMetadataForFolder = function (storageFolder) {
+ storageFolder.getBasicPropertiesAsync().then(
+ function (basicProperties) {
+ var metadata = {
+ size: basicProperties.size,
+ lastModifiedDate: basicProperties.dateModified
+ };
+ success(metadata);
+ },
+ function () {
+ fail(FileError.NOT_READABLE_ERR);
+ }
+ );
+ };
+
+ getFileFromPathAsync(fullPath).then(getMetadataForFile,
+ function () {
+ getFolderFromPathAsync(fullPath).then(getMetadataForFolder,
+ function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ }
+ );
+ },
+
+ getParent: function (win, fail, args) { // ["fullPath"]
+ var fs = getFilesystemFromURL(args[0]);
+ var path = pathFromURL(args[0]);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ if (!path || (new RegExp('/[^/]*/?$')).test(path)) {
+ win(new DirectoryEntry(fs.root.name, fs.root.fullPath, fs.name, fs.makeNativeURL(fs.root.fullPath)));
+ return;
+ }
+
+ var parpath = path.replace(new RegExp('/[^/]+/?$', 'g'), '');
+ var parname = path.substr(parpath.length);
+ var fullPath = cordovaPathToNative(fs.winpath + parpath);
+
+ var result = new DirectoryEntry(parname, parpath, fs.name, fs.makeNativeURL(parpath));
+ getFolderFromPathAsync(fullPath).done(
+ function () { win(result); },
+ function () { fail(FileError.INVALID_STATE_ERR); }
+ );
+ },
+
+ readAsText: function (win, fail, args) {
+
+ var url = args[0];
+ var enc = args[1];
+ var startPos = args[2];
+ var endPos = args[3];
+
+ var fs = getFilesystemFromURL(url);
+ var path = pathFromURL(url);
+ if (!fs) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
+
+ var encoding = Windows.Storage.Streams.UnicodeEncoding.utf8;
+ if (enc === 'Utf16LE' || enc === 'utf16LE') {
+ encoding = Windows.Storage.Streams.UnicodeEncoding.utf16LE;
+ } else if (enc === 'Utf16BE' || enc === 'utf16BE') {
+ encoding = Windows.Storage.Streams.UnicodeEncoding.utf16BE;
+ }
+
+ getFileFromPathAsync(wpath).then(function (file) {
+ return file.openReadAsync();
+ }).then(function (stream) {
+ startPos = (startPos < 0) ? Math.max(stream.size + startPos, 0) : Math.min(stream.size, startPos);
+ endPos = (endPos < 0) ? Math.max(endPos + stream.size, 0) : Math.min(stream.size, endPos);
+ stream.seek(startPos);
+
+ var readSize = endPos - startPos;
+ var buffer = new Windows.Storage.Streams.Buffer(readSize);
+
+ return stream.readAsync(buffer, readSize, Windows.Storage.Streams.InputStreamOptions.none);
+ }).done(function (buffer) {
+ try {
+ win(Windows.Security.Cryptography.CryptographicBuffer.convertBinaryToString(encoding, buffer));
+ } catch (e) {
+ fail(FileError.ENCODING_ERR);
+ }
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ });
+ },
+
+ readAsBinaryString: function (win, fail, args) {
+ var url = args[0];
+ var startPos = args[1];
+ var endPos = args[2];
+
+ var fs = getFilesystemFromURL(url);
+ var path = pathFromURL(url);
+ if (!fs) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
+
+ getFileFromPathAsync(wpath).then(
+ function (storageFile) {
+ Windows.Storage.FileIO.readBufferAsync(storageFile).done(
+ function (buffer) {
+ var dataReader = Windows.Storage.Streams.DataReader.fromBuffer(buffer);
+ // var fileContent = dataReader.readString(buffer.length);
+ var byteArray = new Uint8Array(buffer.length);
+ var byteString = '';
+ dataReader.readBytes(byteArray);
+ dataReader.close();
+ for (var i = 0; i < byteArray.length; i++) {
+ var charByte = byteArray[i];
+ // var charRepresentation = charByte <= 127 ? String.fromCharCode(charByte) : charByte.toString(16);
+ var charRepresentation = String.fromCharCode(charByte);
+ byteString += charRepresentation;
+ }
+ win(byteString.slice(startPos, endPos));
+ }
+ );
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ },
+
+ readAsArrayBuffer: function (win, fail, args) {
+ var url = args[0];
+ var fs = getFilesystemFromURL(url);
+ var path = pathFromURL(url);
+ if (!fs) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
+
+ getFileFromPathAsync(wpath).then(
+ function (storageFile) {
+ var blob = MSApp.createFileFromStorageFile(storageFile);
+ var url = URL.createObjectURL(blob, { oneTimeOnly: true }); // eslint-disable-line no-undef
+ var xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function () {
+ var resultArrayBuffer = xhr.response;
+ // get start and end position of bytes in buffer to be returned
+ var startPos = args[1] || 0;
+ var endPos = args[2] || resultArrayBuffer.length;
+ // if any of them is specified, we'll slice output array
+ if (startPos !== 0 || endPos !== resultArrayBuffer.length) {
+ // slice method supported only on Windows 8.1, so we need to check if it's available
+ // see http://msdn.microsoft.com/en-us/library/ie/dn641192(v=vs.94).aspx
+ if (resultArrayBuffer.slice) {
+ resultArrayBuffer = resultArrayBuffer.slice(startPos, endPos);
+ } else {
+ // if slice isn't available, we'll use workaround method
+ var tempArray = new Uint8Array(resultArrayBuffer);
+ var resBuffer = new ArrayBuffer(endPos - startPos);
+ var resArray = new Uint8Array(resBuffer);
+
+ for (var i = 0; i < resArray.length; i++) {
+ resArray[i] = tempArray[i + startPos];
+ }
+ resultArrayBuffer = resBuffer;
+ }
+ }
+ win(resultArrayBuffer);
+ };
+ xhr.send();
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ },
+
+ readAsDataURL: function (win, fail, args) {
+ var url = args[0];
+ var fs = getFilesystemFromURL(url);
+ var path = pathFromURL(url);
+ if (!fs) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
+
+ getFileFromPathAsync(wpath).then(
+ function (storageFile) {
+ Windows.Storage.FileIO.readBufferAsync(storageFile).done(
+ function (buffer) {
+ var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
+ // the method encodeToBase64String will add "77u/" as a prefix, so we should remove it
+ if (String(strBase64).substr(0, 4) === '77u/') {
+ strBase64 = strBase64.substr(4);
+ }
+ var mediaType = storageFile.contentType;
+ var result = 'data:' + mediaType + ';base64,' + strBase64;
+ win(result);
+ }
+ );
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ },
+
+ getDirectory: function (win, fail, args) {
+ var dirurl = args[0];
+ var path = args[1];
+ var options = args[2];
+
+ var fs = getFilesystemFromURL(dirurl);
+ var dirpath = pathFromURL(dirurl);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var fspath = sanitize(dirpath + '/' + path);
+ var completePath = sanitize(fs.winpath + fspath);
+
+ var name = completePath.substring(completePath.lastIndexOf('/') + 1);
+
+ var wpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/')));
+
+ var flag = '';
+ if (options) {
+ flag = new Flags(options.create, options.exclusive);
+ } else {
+ flag = new Flags(false, false);
+ }
+
+ getFolderFromPathAsync(wpath).done(
+ function (storageFolder) {
+ if (flag.create === true && flag.exclusive === true) {
+ storageFolder.createFolderAsync(name, Windows.Storage.CreationCollisionOption.failIfExists).done(
+ function (storageFolder) {
+ win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }, function (err) { // eslint-disable-line handle-callback-err
+ fail(FileError.PATH_EXISTS_ERR);
+ }
+ );
+ } else if (flag.create === true && flag.exclusive === false) {
+ storageFolder.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists).done(
+ function (storageFolder) {
+ win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }, function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+ } else if (flag.create === false) {
+ storageFolder.getFolderAsync(name).done(
+ function (storageFolder) {
+ win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ },
+ function () {
+ // check if path actually points to a file
+ storageFolder.getFileAsync(name).done(
+ function () {
+ fail(FileError.TYPE_MISMATCH_ERR);
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ }
+ );
+ }
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ },
+
+ remove: function (win, fail, args) {
+ var fs = getFilesystemFromURL(args[0]);
+ var path = pathFromURL(args[0]);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+
+ // FileSystem root can't be removed!
+ if (!path || path === '/') {
+ fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+ return;
+ }
+ var fullPath = cordovaPathToNative(fs.winpath + path);
+
+ getFileFromPathAsync(fullPath).then(
+ function (storageFile) {
+ storageFile.deleteAsync().done(win, function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ });
+ },
+ function () {
+ getFolderFromPathAsync(fullPath).done(
+ function (sFolder) {
+ sFolder.getFilesAsync()
+ // check for files
+ .then(function (fileList) {
+ if (fileList) {
+ if (fileList.length === 0) {
+ return sFolder.getFoldersAsync();
+ } else {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ }
+ })
+ // check for folders
+ .done(function (folderList) {
+ if (folderList) {
+ if (folderList.length === 0) {
+ sFolder.deleteAsync().done(
+ win,
+ function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+ } else {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ }
+ });
+ },
+ function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ }
+ );
+ },
+
+ removeRecursively: function (successCallback, fail, args) {
+
+ var fs = getFilesystemFromURL(args[0]);
+ var path = pathFromURL(args[0]);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+
+ // FileSystem root can't be removed!
+ if (!path || path === '/') {
+ fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+ return;
+ }
+ var fullPath = cordovaPathToNative(fs.winpath + path);
+
+ getFolderFromPathAsync(fullPath).done(function (storageFolder) {
+ storageFolder.deleteAsync().done(function (res) {
+ successCallback(res);
+ }, function (err) {
+ fail(err);
+ });
+
+ }, function () {
+ fail(FileError.FILE_NOT_FOUND_ERR);
+ });
+ },
+
+ getFile: function (win, fail, args) {
+
+ var dirurl = args[0];
+ var path = args[1];
+ var options = args[2];
+
+ var fs = getFilesystemFromURL(dirurl);
+ var dirpath = pathFromURL(dirurl);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var fspath = sanitize(dirpath + '/' + path);
+ var completePath = sanitize(fs.winpath + fspath);
+
+ var fileName = completePath.substring(completePath.lastIndexOf('/') + 1);
+
+ var wpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/')));
+
+ var flag = '';
+ if (options !== null) {
+ flag = new Flags(options.create, options.exclusive);
+ } else {
+ flag = new Flags(false, false);
+ }
+
+ getFolderFromPathAsync(wpath).done(
+ function (storageFolder) {
+ if (flag.create === true && flag.exclusive === true) {
+ storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.failIfExists).done(
+ function (storageFile) {
+ win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }, function () {
+ fail(FileError.PATH_EXISTS_ERR);
+ }
+ );
+ } else if (flag.create === true && flag.exclusive === false) {
+ storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.openIfExists).done(
+ function (storageFile) {
+ win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }, function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+ } else if (flag.create === false) {
+ storageFolder.getFileAsync(fileName).done(
+ function (storageFile) {
+ win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }, function () {
+ // check if path actually points to a folder
+ storageFolder.getFolderAsync(fileName).done(
+ function () {
+ fail(FileError.TYPE_MISMATCH_ERR);
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ });
+ }
+ );
+ }
+ }, function (err) {
+ fail(
+ err.number === WinError.accessDenied ?
+ FileError.SECURITY_ERR :
+ FileError.NOT_FOUND_ERR
+ );
+ }
+ );
+ },
+
+ readEntries: function (win, fail, args) { // ["fullPath"]
+ var fs = getFilesystemFromURL(args[0]);
+ var path = pathFromURL(args[0]);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var fullPath = cordovaPathToNative(fs.winpath + path);
+
+ var result = [];
+
+ getFolderFromPathAsync(fullPath).done(function (storageFolder) {
+ var promiseArr = [];
+ var index = 0;
+ promiseArr[index++] = storageFolder.getFilesAsync().then(function (fileList) {
+ if (fileList !== null) {
+ for (var i = 0; i < fileList.length; i++) {
+ var fspath = getFsPathForWinPath(fs, fileList[i].path);
+ if (!fspath) {
+ fail(FileError.NOT_FOUND_ERR);
+ return;
+ }
+ result.push(new FileEntry(fileList[i].name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }
+ }
+ });
+ promiseArr[index++] = storageFolder.getFoldersAsync().then(function (folderList) {
+ if (folderList !== null) {
+ for (var j = 0; j < folderList.length; j++) {
+ var fspath = getFsPathForWinPath(fs, folderList[j].path);
+ if (!fspath) {
+ fail(FileError.NOT_FOUND_ERR);
+ return;
+ }
+ result.push(new DirectoryEntry(folderList[j].name, fspath, fs.name, fs.makeNativeURL(fspath)));
+ }
+ }
+ });
+ WinJS.Promise.join(promiseArr).then(function () {
+ win(result);
+ });
+
+ }, function () { fail(FileError.NOT_FOUND_ERR); });
+ },
+
+ write: function (win, fail, args) {
+
+ var url = args[0];
+ var data = args[1];
+ var position = args[2];
+ var isBinary = args[3];
+
+ var fs = getFilesystemFromURL(url);
+ var path = pathFromURL(url);
+ if (!fs) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var completePath = sanitize(fs.winpath + path);
+ var fileName = completePath.substring(completePath.lastIndexOf('/') + 1);
+ var dirpath = completePath.substring(0, completePath.lastIndexOf('/'));
+ var wpath = cordovaPathToNative(dirpath);
+
+ function getWriteMethodForData (data, isBinary) {
+
+ if (data instanceof Blob) { // eslint-disable-line no-undef
+ return writeBlobAsync;
+ }
+
+ if (data instanceof ArrayBuffer) {
+ return writeArrayBufferAsync;
+ }
+
+ if (isBinary) {
+ return writeBytesAsync;
+ }
+
+ if (typeof data === 'string') {
+ return writeTextAsync;
+ }
+
+ throw new Error('Unsupported data type for write method');
+ }
+
+ var writePromise = getWriteMethodForData(data, isBinary);
+
+ getFolderFromPathAsync(wpath).done(
+ function (storageFolder) {
+ storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.openIfExists).done(
+ function (storageFile) {
+ writePromise(storageFile, data, position).done(
+ function (bytesWritten) {
+ var written = bytesWritten || data.length;
+ win(written);
+ },
+ function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+ },
+ function () {
+ fail(FileError.INVALID_MODIFICATION_ERR);
+ }
+ );
+
+ },
+ function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ },
+
+ truncate: function (win, fail, args) { // ["fileName","size"]
+ var url = args[0];
+ var size = args[1];
+
+ var fs = getFilesystemFromURL(url);
+ var path = pathFromURL(url);
+ if (!fs) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ var completePath = sanitize(fs.winpath + path);
+ var wpath = cordovaPathToNative(completePath);
+ var dirwpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/')));
+
+ getFileFromPathAsync(wpath).done(function (storageFile) {
+ // the current length of the file.
+ var leng = 0;
+
+ storageFile.getBasicPropertiesAsync().then(function (basicProperties) {
+ leng = basicProperties.size;
+ if (Number(size) >= leng) {
+ win(this.length);
+ return;
+ }
+ if (Number(size) >= 0) {
+ Windows.Storage.FileIO.readTextAsync(storageFile, Windows.Storage.Streams.UnicodeEncoding.utf8).then(function (fileContent) {
+ fileContent = fileContent.substr(0, size);
+ var name = storageFile.name;
+ storageFile.deleteAsync().then(function () {
+ return getFolderFromPathAsync(dirwpath);
+ }).done(function (storageFolder) {
+ storageFolder.createFileAsync(name).then(function (newStorageFile) {
+ Windows.Storage.FileIO.writeTextAsync(newStorageFile, fileContent).done(function () {
+ win(String(fileContent).length);
+ }, function () {
+ fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+ });
+ }, function () {
+ fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+ });
+ });
+ }, function () { fail(FileError.NOT_FOUND_ERR); });
+ }
+ });
+ }, function () { fail(FileError.NOT_FOUND_ERR); });
+ },
+
+ copyTo: function (success, fail, args) { // ["fullPath","parent", "newName"]
+ transport(success, fail, args,
+ {
+ fileOp: function (file, folder, name, coll) {
+ return file.copyAsync(folder, name, coll);
+ },
+ folderOp: function (src, dst, name) {
+ return copyFolder(src, dst, name);
+ }}
+ );
+ },
+
+ moveTo: function (success, fail, args) {
+ transport(success, fail, args,
+ {
+ fileOp: function (file, folder, name, coll) {
+ return file.moveAsync(folder, name, coll);
+ },
+ folderOp: function (src, dst, name) {
+ return moveFolder(src, dst, name);
+ }}
+ );
+ },
+ tempFileSystem: null,
+
+ persistentFileSystem: null,
+
+ requestFileSystem: function (win, fail, args) {
+
+ var type = args[0];
+ var size = args[1];
+ var MAX_SIZE = 10000000000;
+ if (size > MAX_SIZE) {
+ fail(FileError.QUOTA_EXCEEDED_ERR);
+ return;
+ }
+
+ var fs;
+ switch (type) {
+ case LocalFileSystem.TEMPORARY:
+ fs = getFS('temporary');
+ break;
+ case LocalFileSystem.PERSISTENT:
+ fs = getFS('persistent');
+ break;
+ }
+ if (fs) { win(fs); } else { fail(FileError.NOT_FOUND_ERR); }
+ },
+
+ resolveLocalFileSystemURI: function (success, fail, args) {
+
+ var uri = args[0];
+
+ var path = pathFromURL(uri);
+ var fs = getFilesystemFromURL(uri);
+ if (!fs || !validName(path)) {
+ fail(FileError.ENCODING_ERR);
+ return;
+ }
+ if (path.indexOf(fs.winpath) === 0) { path = path.substr(fs.winpath.length); }
+ var abspath = cordovaPathToNative(fs.winpath + path);
+
+ getFileFromPathAsync(abspath).done(
+ function (storageFile) {
+ success(new FileEntry(storageFile.name, path, fs.name, fs.makeNativeURL(path)));
+ }, function () {
+ getFolderFromPathAsync(abspath).done(
+ function (storageFolder) {
+ success(new DirectoryEntry(storageFolder.name, path, fs.name, fs.makeNativeURL(path)));
+ }, function () {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ );
+ }
+ );
+ }
+
+};
+
+require('cordova/exec/proxy').add('File', module.exports);
diff --git a/StoneIsland/plugins/cordova-plugin-file/tests/package.json b/StoneIsland/plugins/cordova-plugin-file/tests/package.json
new file mode 100644
index 00000000..33cd4cf9
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/tests/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "cordova-plugin-file-tests",
+ "version": "6.0.2",
+ "description": "",
+ "cordova": {
+ "id": "cordova-plugin-file-tests",
+ "platforms": [
+ "android"
+ ]
+ },
+ "keywords": [
+ "ecosystem:cordova",
+ "cordova-android"
+ ],
+ "author": "",
+ "license": "Apache-2.0"
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/tests/plugin.xml b/StoneIsland/plugins/cordova-plugin-file/tests/plugin.xml
new file mode 100644
index 00000000..38f3f228
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/tests/plugin.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ id="cordova-plugin-file-tests"
+ version="6.0.2">
+
+ <name>Cordova File Plugin Tests</name>
+ <license>Apache 2.0</license>
+
+ <js-module src="tests.js" name="tests">
+ </js-module>
+
+ <platform name="android">
+ <source-file src="src/android/TestContentProvider.java" target-dir="src/org/apache/cordova/file/test" />
+ <config-file target="AndroidManifest.xml" parent="/*/application">
+ <provider
+ android:name="org.apache.cordova.file.test.TestContentProvider"
+ android:authorities="org.apache.cordova.file.testprovider"
+ android:exported="false" />
+ </config-file>
+ <asset src="www/fixtures/asset-test" target="fixtures/asset-test" />
+ <dependency id="cordova-plugin-contacts" />
+ </platform>
+</plugin>
diff --git a/StoneIsland/plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java b/StoneIsland/plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java
new file mode 100644
index 00000000..9eba0d06
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java
@@ -0,0 +1,93 @@
+/*
+ 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.
+ */
+package org.apache.cordova.file.test;
+
+import android.content.ContentProvider;
+import android.net.Uri;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
+
+import org.apache.cordova.CordovaResourceApi;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+public class TestContentProvider extends ContentProvider {
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ String fileName = uri.getQueryParameter("realPath");
+ if (fileName == null) {
+ fileName = uri.getPath();
+ }
+ if (fileName == null || fileName.length() < 1) {
+ throw new FileNotFoundException();
+ }
+ CordovaResourceApi resourceApi = new CordovaResourceApi(getContext(), null);
+ try {
+ File f = File.createTempFile("test-content-provider", ".tmp");
+ resourceApi.copyResource(Uri.parse("file:///android_asset" + fileName), Uri.fromFile(f));
+ return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new FileNotFoundException("IO error: " + e.toString());
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "text/html";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+
+}
diff --git a/StoneIsland/plugins/cordova-plugin-file/tests/tests.js b/StoneIsland/plugins/cordova-plugin-file/tests/tests.js
new file mode 100644
index 00000000..a9ae20aa
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/tests/tests.js
@@ -0,0 +1,4163 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* eslint-env jasmine */
+/* global WebKitBlobBuilder */
+
+exports.defineAutoTests = function () {
+ /* eslint-disable no-undef */
+ var isBrowser = (cordova.platformId === 'browser');
+ // Use feature detection to determine current browser instead of checking user-agent
+ var isChrome = isBrowser && window.webkitRequestFileSystem && window.webkitResolveLocalFileSystemURL;
+ var isIE = isBrowser && (window.msIndexedDB);
+ var isIndexedDBShim = isBrowser && !isChrome; // Firefox and IE for example
+
+ var isWindows = (cordova.platformId === 'windows' || cordova.platformId === 'windows8');
+ /* eslint-enable no-undef */
+ var MEDIUM_TIMEOUT = 15000;
+
+ describe('File API', function () {
+ // Adding a Jasmine helper matcher, to report errors when comparing to FileError better.
+ var fileErrorMap = {
+ 1: 'NOT_FOUND_ERR',
+ 2: 'SECURITY_ERR',
+ 3: 'ABORT_ERR',
+ 4: 'NOT_READABLE_ERR',
+ 5: 'ENCODING_ERR',
+ 6: 'NO_MODIFICATION_ALLOWED_ERR',
+ 7: 'INVALID_STATE_ERR',
+ 8: 'SYNTAX_ERR',
+ 9: 'INVALID_MODIFICATION_ERR',
+ 10: 'QUOTA_EXCEEDED_ERR',
+ 11: 'TYPE_MISMATCH_ERR',
+ 12: 'PATH_EXISTS_ERR'
+ };
+ var root;
+ var temp_root;
+ var persistent_root;
+ beforeEach(function (done) {
+ // Custom Matchers
+ jasmine.Expectation.addMatchers({
+ toBeFileError: function () {
+ return {
+ compare: function (error, code) {
+ var pass = error.code === code;
+ return {
+ pass: pass,
+ message: 'Expected FileError with code ' + fileErrorMap[error.code] + ' (' + error.code + ') to be ' + fileErrorMap[code] + '(' + code + ')'
+ };
+ }
+ };
+ },
+ toCanonicallyMatch: function () {
+ return {
+ compare: function (currentPath, path) {
+ var a = path.split('/').join('').split('\\').join('');
+ var b = currentPath.split('/').join('').split('\\').join('');
+ var pass = a === b;
+ return {
+ pass: pass,
+ message: 'Expected paths to match : ' + path + ' should be ' + currentPath
+ };
+ }
+ };
+ },
+ toFailWithMessage: function () {
+ return {
+ compare: function (error, message) { // eslint-disable-line handle-callback-err
+ var pass = false;
+ return {
+ pass: pass,
+ message: message
+ };
+ }
+ };
+ },
+ toBeDataUrl: function () {
+ return {
+ compare: function (url) {
+ var pass = false;
+ // "data:application/octet-stream;base64,"
+ var header = url.substr(0, url.indexOf(','));
+ var headerParts = header.split(/[:;]/);
+ if (headerParts.length === 3 &&
+ headerParts[0] === 'data' &&
+ headerParts[2] === 'base64') {
+ pass = true;
+ }
+ var message = 'Expected ' + url + ' to be a valid data url. ' + header + ' is not valid header for data uris';
+ return {
+ pass: pass,
+ message: message
+ };
+ }
+ };
+ }
+ });
+ // Define global variables
+ var onError = function (e) {
+ console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+ console.log(JSON.stringify(e));
+ };
+ window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) { // eslint-disable-line no-undef
+ root = fileSystem.root;
+ // set in file.tests.js
+ persistent_root = root;
+ window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) { // eslint-disable-line no-undef
+ temp_root = fileSystem.root;
+ // set in file.tests.js
+ done();
+ }, onError);
+ }, onError);
+ });
+ // HELPER FUNCTIONS
+ // deletes specified file or directory
+ var deleteEntry = function (name, success, error) {
+ // deletes entry, if it exists
+ // entry.remove success callback is required: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#the-entry-interface
+ success = success || function () {};
+ error = error || failed.bind(null, success, 'deleteEntry failed.');
+
+ window.resolveLocalFileSystemURL(root.toURL() + '/' + name, function (entry) {
+ if (entry.isDirectory === true) {
+ entry.removeRecursively(success, error);
+ } else {
+ entry.remove(success, error);
+ }
+ }, success);
+ };
+ // deletes file, if it exists, then invokes callback
+ var deleteFile = function (fileName, callback) {
+ // entry.remove success callback is required: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#the-entry-interface
+ callback = callback || function () {};
+
+ root.getFile(fileName, null, // remove file system entry
+ function (entry) {
+ entry.remove(callback, function () {
+ console.log('[ERROR] deleteFile cleanup method invoked fail callback.');
+ });
+ }, // doesn't exist
+ callback);
+ };
+ // deletes and re-creates the specified file
+ var createFile = function (fileName, success, error) {
+ deleteEntry(fileName, function () {
+ root.getFile(fileName, {
+ create: true
+ }, success, error);
+ }, error);
+ };
+ // deletes and re-creates the specified directory
+ var createDirectory = function (dirName, success, error) {
+ deleteEntry(dirName, function () {
+ root.getDirectory(dirName, {
+ create: true
+ }, success, error);
+ }, error);
+ };
+ function failed (done, msg, error) {
+ var info = typeof msg === 'undefined' ? 'Unexpected error callback' : msg;
+ var codeMsg = (error && error.code) ? (': ' + fileErrorMap[error.code]) : '';
+ expect(true).toFailWithMessage(info + '\n' + JSON.stringify(error) + codeMsg);
+ done();
+ }
+ var succeed = function (done, msg) {
+ var info = typeof msg === 'undefined' ? 'Unexpected success callback' : msg;
+ expect(true).toFailWithMessage(info);
+ done();
+ };
+ var joinURL = function (base, extension) {
+ if (base.charAt(base.length - 1) !== '/' && extension.charAt(0) !== '/') {
+ return base + '/' + extension;
+ }
+ if (base.charAt(base.length - 1) === '/' && extension.charAt(0) === '/') {
+ return base + extension.substring(1);
+ }
+ return base + extension;
+ };
+ describe('FileError object', function () {
+ /* eslint-disable no-undef */
+ it('file.spec.1 should define FileError constants', function () {
+ expect(FileError.NOT_FOUND_ERR).toBe(1);
+ expect(FileError.SECURITY_ERR).toBe(2);
+ expect(FileError.ABORT_ERR).toBe(3);
+ expect(FileError.NOT_READABLE_ERR).toBe(4);
+ expect(FileError.ENCODING_ERR).toBe(5);
+ expect(FileError.NO_MODIFICATION_ALLOWED_ERR).toBe(6);
+ expect(FileError.INVALID_STATE_ERR).toBe(7);
+ expect(FileError.SYNTAX_ERR).toBe(8);
+ expect(FileError.INVALID_MODIFICATION_ERR).toBe(9);
+ expect(FileError.QUOTA_EXCEEDED_ERR).toBe(10);
+ expect(FileError.TYPE_MISMATCH_ERR).toBe(11);
+ expect(FileError.PATH_EXISTS_ERR).toBe(12);
+ });
+ });
+ describe('LocalFileSystem', function () {
+ it('file.spec.2 should define LocalFileSystem constants', function () {
+ expect(LocalFileSystem.TEMPORARY).toBe(0);
+ expect(LocalFileSystem.PERSISTENT).toBe(1);
+ /* eslint-enable no-undef */
+ });
+ describe('window.requestFileSystem', function () {
+ it('file.spec.3 should be defined', function () {
+ expect(window.requestFileSystem).toBeDefined();
+ });
+ it('file.spec.4 should be able to retrieve a PERSISTENT file system', function (done) {
+ var win = function (fileSystem) {
+ expect(fileSystem).toBeDefined();
+ expect(fileSystem.name).toBeDefined();
+ if (isChrome) {
+ expect(fileSystem.name).toContain('Persistent');
+ } else {
+ expect(fileSystem.name).toBe('persistent');
+ }
+ expect(fileSystem.root).toBeDefined();
+ expect(fileSystem.root.filesystem).toBeDefined();
+ // Shouldn't use cdvfile by default.
+ expect(fileSystem.root.toURL()).not.toMatch(/^cdvfile:/);
+ // All DirectoryEntry URLs should always have a trailing slash.
+ expect(fileSystem.root.toURL()).toMatch(/\/$/);
+ done();
+ };
+
+ // Request a little bit of space on the filesystem, unless we're running in a browser where that could cause a prompt.
+ var spaceRequired = isBrowser ? 0 : 1024;
+
+ // retrieve PERSISTENT file system
+ window.requestFileSystem(LocalFileSystem.PERSISTENT, spaceRequired, win, failed.bind(null, done, 'window.requestFileSystem - Error retrieving PERSISTENT file system')); // eslint-disable-line no-undef
+ });
+ it('file.spec.5 should be able to retrieve a TEMPORARY file system', function (done) {
+ var win = function (fileSystem) {
+ expect(fileSystem).toBeDefined();
+ if (isChrome) {
+ expect(fileSystem.name).toContain('Temporary');
+ } else {
+ expect(fileSystem.name).toBe('temporary');
+ }
+ expect(fileSystem.root).toBeDefined();
+ expect(fileSystem.root.filesystem).toBeDefined();
+ expect(fileSystem.root.filesystem).toBe(fileSystem);
+ done();
+ };
+ // retrieve TEMPORARY file system
+ window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, win, failed.bind(null, done, 'window.requestFileSystem - Error retrieving TEMPORARY file system')); // eslint-disable-line no-undef
+ });
+ it('file.spec.6 should error if you request a file system that is too large', function (done) {
+ if (isBrowser) {
+ /* window.requestFileSystem TEMPORARY and PERSISTENT filesystem quota is not limited in Chrome.
+ Firefox filesystem size is not limited but every 50MB request user permission.
+ IE10 allows up to 10mb of combined AppCache and IndexedDB used in implementation
+ of filesystem without prompting, once you hit that level you will be asked if you
+ want to allow it to be increased up to a max of 250mb per site.
+ So `size` parameter for `requestFileSystem` function does not affect on filesystem in Firefox and IE. */
+ pending();
+ }
+
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ expect(error).toBeFileError(FileError.QUOTA_EXCEEDED_ERR); // eslint-disable-line no-undef
+ done();
+ };
+ // win = createWin('window.requestFileSystem');
+ // Request the file system
+ window.requestFileSystem(LocalFileSystem.TEMPORARY, 1000000000000000, failed.bind(null, done, 'window.requestFileSystem - Error retrieving TEMPORARY file system'), fail); // eslint-disable-line no-undef
+ });
+ it('file.spec.7 should error out if you request a file system that does not exist', function (done) {
+
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of SYNTAX_ERR(code: 8)
+ on requesting of a non-existant filesystem. */
+ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+ } else {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // Request the file system
+ window.requestFileSystem(-1, 0, succeed.bind(null, done, 'window.requestFileSystem'), fail);
+ });
+ });
+ describe('window.resolveLocalFileSystemURL', function () {
+ it('file.spec.8 should be defined', function () {
+ expect(window.resolveLocalFileSystemURL).toBeDefined();
+ });
+ it('file.spec.9 should resolve a valid file name', function (done) {
+ var fileName = 'file.spec.9';
+ var win = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(true);
+ expect(fileEntry.isDirectory).toBe(false);
+ expect(fileEntry.name).toCanonicallyMatch(fileName);
+ expect(fileEntry.toURL()).not.toMatch(/^cdvfile:/, 'should not use cdvfile URL');
+ expect(fileEntry.toURL()).not.toMatch(/\/$/, 'URL should not end with a slash');
+ // Clean-up
+ deleteEntry(fileName, done);
+ };
+ createFile(fileName, function (entry) {
+ window.resolveLocalFileSystemURL(entry.toURL(), win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving file URL: ' + entry.toURL()));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName), failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.9.1 should resolve a file even with a terminating slash', function (done) {
+ var fileName = 'file.spec.9.1';
+ var win = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(true);
+ expect(fileEntry.isDirectory).toBe(false);
+ expect(fileEntry.name).toCanonicallyMatch(fileName);
+ expect(fileEntry.toURL()).not.toMatch(/^cdvfile:/, 'should not use cdvfile URL');
+ expect(fileEntry.toURL()).not.toMatch(/\/$/, 'URL should not end with a slash');
+ // Clean-up
+ deleteEntry(fileName, done);
+ };
+ createFile(fileName, function (entry) {
+ var entryURL = entry.toURL() + '/';
+ window.resolveLocalFileSystemURL(entryURL, win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving file URL: ' + entryURL));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName), failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.9.5 should resolve a directory', function (done) {
+ var fileName = 'file.spec.9.5';
+ var win = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(false);
+ expect(fileEntry.isDirectory).toBe(true);
+ expect(fileEntry.name).toCanonicallyMatch(fileName);
+ expect(fileEntry.toURL()).not.toMatch(/^cdvfile:/, 'should not use cdvfile URL');
+ expect(fileEntry.toURL()).toMatch(/\/$/, 'URL end with a slash');
+ // cleanup
+ deleteEntry(fileName, done);
+ };
+ function gotDirectory (entry) {
+ // lookup file system entry
+ window.resolveLocalFileSystemURL(entry.toURL(), win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving directory URL: ' + entry.toURL()));
+ }
+ createDirectory(fileName, gotDirectory, failed.bind(null, done, 'createDirectory - Error creating directory: ' + fileName), failed.bind(null, done, 'createDirectory - Error creating directory: ' + fileName));
+ });
+ it('file.spec.9.6 should resolve a directory even without a terminating slash', function (done) {
+ var fileName = 'file.spec.9.6';
+ var win = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(false);
+ expect(fileEntry.isDirectory).toBe(true);
+ expect(fileEntry.name).toCanonicallyMatch(fileName);
+ expect(fileEntry.toURL()).not.toMatch(/^cdvfile:/, 'should not use cdvfile URL');
+ expect(fileEntry.toURL()).toMatch(/\/$/, 'URL end with a slash');
+ // cleanup
+ deleteEntry(fileName, done);
+ };
+ function gotDirectory (entry) {
+ // lookup file system entry
+ var entryURL = entry.toURL();
+ entryURL = entryURL.substring(0, entryURL.length - 1);
+ window.resolveLocalFileSystemURL(entryURL, win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving directory URL: ' + entryURL));
+ }
+ createDirectory(fileName, gotDirectory, failed.bind(null, done, 'createDirectory - Error creating directory: ' + fileName), failed.bind(null, done, 'createDirectory - Error creating directory: ' + fileName));
+ });
+
+ it('file.spec.9.7 should resolve a file with valid nativeURL', function (done) {
+ if (isBrowser) {
+ pending('browsers doesn\'t return nativeURL');
+ }
+ var fileName = 'de.create.file';
+ var win = function (entry) {
+ var path = entry.nativeURL.split('///')[1];
+ expect(/\/{2,}/.test(path)).toBeFalsy();
+ // cleanup
+ deleteEntry(entry.name, done);
+ };
+ root.getFile(fileName, {
+ create: true
+ }, win, succeed.bind(null, done, 'root.getFile - Error unexpected callback, file should not exists: ' + fileName));
+ });
+
+ it('file.spec.10 resolve valid file name with parameters', function (done) {
+ var fileName = 'resolve.file.uri.params';
+ var win = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ if (fileEntry.toURL().toLowerCase().substring(0, 10) === 'cdvfile://') {
+ expect(fileEntry.fullPath).toBe('/' + fileName + '?1234567890');
+ }
+ expect(fileEntry.name).toBe(fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ };
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ window.resolveLocalFileSystemURL(entry.toURL() + '?1234567890', win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving file URI: ' + entry.toURL()));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.11 should error (NOT_FOUND_ERR) when resolving (non-existent) invalid file name', function (done) {
+ var fileName = cordova.platformId === 'windowsphone' ? root.toURL() + '/' + 'this.is.not.a.valid.file.txt' : joinURL(root.toURL(), 'this.is.not.a.valid.file.txt'); // eslint-disable-line no-undef
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // lookup file system entry
+ window.resolveLocalFileSystemURL(fileName, succeed.bind(null, done, 'window.resolveLocalFileSystemURL - Error unexpected callback resolving file URI: ' + fileName), fail);
+ });
+ it('file.spec.12 should error (ENCODING_ERR) when resolving invalid URI with leading /', function (done) {
+ var fileName = '/this.is.not.a.valid.url';
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // O.o chrome returns error code 0
+ } else {
+ expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // lookup file system entry
+ window.resolveLocalFileSystemURL(fileName, succeed.bind(null, done, 'window.resolveLocalFileSystemURL - Error unexpected callback resolving file URI: ' + fileName), fail);
+ });
+ });
+ });
+ // LocalFileSystem
+ describe('Metadata interface', function () {
+ it('file.spec.13 should exist and have the right properties', function () {
+ var metadata = new Metadata(); // eslint-disable-line no-undef
+ expect(metadata).toBeDefined();
+ expect(metadata.modificationTime).toBeDefined();
+ });
+ });
+ describe('Flags interface', function () {
+ it('file.spec.14 should exist and have the right properties', function () {
+ var flags = new Flags(false, true); // eslint-disable-line no-undef
+ expect(flags).toBeDefined();
+ expect(flags.create).toBeDefined();
+ expect(flags.create).toBe(false);
+ expect(flags.exclusive).toBeDefined();
+ expect(flags.exclusive).toBe(true);
+ });
+ });
+ describe('FileSystem interface', function () {
+ it('file.spec.15 should have a root that is a DirectoryEntry', function (done) {
+ var win = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(false);
+ expect(entry.isDirectory).toBe(true);
+ expect(entry.name).toBeDefined();
+ expect(entry.fullPath).toBeDefined();
+ expect(entry.getMetadata).toBeDefined();
+ expect(entry.moveTo).toBeDefined();
+ expect(entry.copyTo).toBeDefined();
+ expect(entry.toURL).toBeDefined();
+ expect(entry.remove).toBeDefined();
+ expect(entry.getParent).toBeDefined();
+ expect(entry.createReader).toBeDefined();
+ expect(entry.getFile).toBeDefined();
+ expect(entry.getDirectory).toBeDefined();
+ expect(entry.removeRecursively).toBeDefined();
+ done();
+ };
+ window.resolveLocalFileSystemURL(root.toURL(), win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving file URI: ' + root.toURL()));
+ });
+ });
+ describe('DirectoryEntry', function () {
+ it('file.spec.16 getFile: get Entry for file that does not exist', function (done) {
+ var fileName = 'de.no.file';
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // create:false, exclusive:false, file does not exist
+ root.getFile(fileName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Error unexpected callback, file should not exists: ' + fileName), fail);
+ });
+ it('file.spec.17 getFile: create new file', function (done) {
+ var fileName = 'de.create.file';
+ var filePath = joinURL(root.fullPath, fileName);
+ var win = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(fileName);
+ expect(entry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(entry.name, done);
+ };
+ // create:true, exclusive:false, file does not exist
+ root.getFile(fileName, {
+ create: true
+ }, win, succeed.bind(null, done, 'root.getFile - Error unexpected callback, file should not exists: ' + fileName));
+ });
+ it('file.spec.18 getFile: create new file (exclusive)', function (done) {
+ var fileName = 'de.create.exclusive.file';
+ var filePath = joinURL(root.fullPath, fileName);
+ var win = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toBe(fileName);
+ expect(entry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(entry.name, done);
+ };
+ // create:true, exclusive:true, file does not exist
+ root.getFile(fileName, {
+ create: true,
+ exclusive: true
+ }, win, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.19 getFile: create file that already exists', function (done) {
+ var fileName = 'de.create.existing.file';
+ var filePath = joinURL(root.fullPath, fileName);
+
+ function win (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(fileName);
+ expect(entry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(entry.name, done);
+ }
+
+ function getFile (file) {
+ // create:true, exclusive:false, file exists
+ root.getFile(fileName, {
+ create: true
+ }, win, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ }
+
+ // create file to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, getFile, failed.bind(null, done, 'root.getFile - Error on initial creating file: ' + fileName));
+ });
+ it('file.spec.20 getFile: create file that already exists (exclusive)', function (done) {
+ var fileName = 'de.create.exclusive.existing.file';
+ var existingFile;
+
+ function fail (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of PATH_EXISTS_ERR(code: 12)
+ on trying to exclusively create a file, which already exists in Chrome. */
+ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+ } else {
+ expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(existingFile.name, done);
+ }
+
+ function getFile (file) {
+ existingFile = file;
+ // create:true, exclusive:true, file exists
+ root.getFile(fileName, {
+ create: true,
+ exclusive: true
+ }, succeed.bind(null, done, 'root.getFile - getFile function - Error unexpected callback, file should exists: ' + fileName), fail);
+ }
+
+ // create file to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, getFile, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.21 DirectoryEntry.getFile: get Entry for existing file', function (done) {
+ var fileName = 'de.get.file';
+ var filePath = joinURL(root.fullPath, fileName);
+ var win = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(fileName);
+ expect(entry.fullPath).toCanonicallyMatch(filePath);
+ expect(entry.filesystem).toBeDefined();
+ expect(entry.filesystem).toBe(root.filesystem);
+ // clean up
+ deleteEntry(entry.name, done);
+ };
+ var getFile = function (file) {
+ // create:false, exclusive:false, file exists
+ root.getFile(fileName, {
+ create: false
+ }, win, failed.bind(null, done, 'root.getFile - Error getting file entry: ' + fileName));
+ };
+ // create file to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, getFile, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.22 DirectoryEntry.getFile: get FileEntry for invalid path', function (done) {
+ if (isBrowser) {
+ /* The plugin does not follow to ["8.3 Naming restrictions"]
+ (http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). */
+ pending();
+ }
+
+ var fileName = 'de:invalid:path';
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef
+ done();
+ };
+ // create:false, exclusive:false, invalid path
+ root.getFile(fileName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Error unexpected callback, file should not exists: ' + fileName), fail);
+ });
+ it('file.spec.23 DirectoryEntry.getDirectory: get Entry for directory that does not exist', function (done) {
+ var dirName = 'de.no.dir';
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // create:false, exclusive:false, directory does not exist
+ root.getDirectory(dirName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getDirectory - Error unexpected callback, directory should not exists: ' + dirName), fail);
+ });
+ it('file.spec.24 DirectoryEntry.getDirectory: create new dir with space then resolveLocalFileSystemURL', function (done) {
+ var dirName = 'de create dir';
+
+ function win (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ expect(directory.fullPath).toCanonicallyMatch(joinURL(root.fullPath, dirName));
+ // cleanup
+ deleteEntry(directory.name, done);
+ }
+
+ function getDir (dirEntry) {
+ expect(dirEntry.filesystem).toBeDefined();
+ expect(dirEntry.filesystem).toBe(root.filesystem);
+ var dirURI = dirEntry.toURL();
+ // now encode URI and try to resolve
+ window.resolveLocalFileSystemURL(dirURI, win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - getDir function - Error resolving directory: ' + dirURI));
+ }
+
+ // create:true, exclusive:false, directory does not exist
+ root.getDirectory(dirName, {
+ create: true
+ }, getDir, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ // This test is excluded, and should probably be removed. Filesystem
+ // should always be properly encoded URLs, and *not* raw paths, and it
+ // doesn't make sense to double-encode the URLs and expect that to be
+ // handled by the implementation.
+ // If a particular platform uses paths internally rather than URLs, // then that platform should careful to pass them correctly to its
+ // backend.
+ xit('file.spec.25 DirectoryEntry.getDirectory: create new dir with space resolveLocalFileSystemURL with encoded URI', function (done) {
+ var dirName = 'de create dir2';
+ var dirPath = joinURL(root.fullPath, dirName);
+
+ function win (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ expect(directory.fullPath).toCanonicallyMatch(dirPath);
+ // cleanup
+ deleteEntry(directory.name, done);
+ }
+
+ function getDir (dirEntry) {
+ var dirURI = dirEntry.toURL();
+ // now encode URI and try to resolve
+ window.resolveLocalFileSystemURL(encodeURI(dirURI), win, failed.bind(null, done, 'window.resolveLocalFileSystemURL - getDir function - Error resolving directory: ' + dirURI));
+ }
+
+ // create:true, exclusive:false, directory does not exist
+ root.getDirectory(dirName, {
+ create: true
+ }, getDir, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.26 DirectoryEntry.getDirectory: create new directory', function (done) {
+ var dirName = 'de.create.dir';
+ var dirPath = joinURL(root.fullPath, dirName);
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ expect(directory.fullPath).toCanonicallyMatch(dirPath);
+ expect(directory.filesystem).toBeDefined();
+ expect(directory.filesystem).toBe(root.filesystem);
+ // cleanup
+ deleteEntry(directory.name, done);
+ };
+ // create:true, exclusive:false, directory does not exist
+ root.getDirectory(dirName, {
+ create: true
+ }, win, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.27 DirectoryEntry.getDirectory: create new directory (exclusive)', function (done) {
+ var dirName = 'de.create.exclusive.dir';
+ var dirPath = joinURL(root.fullPath, dirName);
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ expect(directory.fullPath).toCanonicallyMatch(dirPath);
+ expect(directory.filesystem).toBeDefined();
+ expect(directory.filesystem).toBe(root.filesystem);
+ // cleanup
+ deleteEntry(directory.name, done);
+ };
+ // create:true, exclusive:true, directory does not exist
+ root.getDirectory(dirName, {
+ create: true,
+ exclusive: true
+ }, win, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.28 DirectoryEntry.getDirectory: create directory that already exists', function (done) {
+ var dirName = 'de.create.existing.dir';
+ var dirPath = joinURL(root.fullPath, dirName);
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ expect(directory.fullPath).toCanonicallyMatch(dirPath);
+ // cleanup
+ deleteEntry(directory.name, done);
+ };
+ // create directory to kick off it
+ root.getDirectory(dirName, {
+ create: true
+ }, function () {
+ root.getDirectory(dirName, {
+ create: true
+ }, win, failed.bind(null, done, 'root.getDirectory - Error creating existent second directory : ' + dirName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.29 DirectoryEntry.getDirectory: create directory that already exists (exclusive)', function (done) {
+
+ var dirName = 'de.create.exclusive.existing.dir';
+ var existingDir;
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of PATH_EXISTS_ERR(code: 12)
+ on trying to exclusively create a file or directory, which already exists (Chrome). */
+ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+ } else {
+ expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(existingDir.name, done);
+ };
+ // create directory to kick off it
+ root.getDirectory(dirName, {
+ create: true
+ }, function (directory) {
+ existingDir = directory;
+ // create:true, exclusive:true, directory exists
+ root.getDirectory(dirName, {
+ create: true,
+ exclusive: true
+ }, failed.bind(null, done, 'root.getDirectory - Unexpected success callback, second directory should not be created : ' + dirName), fail);
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.30 DirectoryEntry.getDirectory: get Entry for existing directory', function (done) {
+ var dirName = 'de.get.dir';
+ var dirPath = joinURL(root.fullPath, dirName);
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ expect(directory.fullPath).toCanonicallyMatch(dirPath);
+ // cleanup
+ deleteEntry(directory.name, done);
+ };
+ // create directory to kick it off
+ root.getDirectory(dirName, {
+ create: true
+ }, function () {
+ root.getDirectory(dirName, {
+ create: false
+ }, win, failed.bind(null, done, 'root.getDirectory - Error getting directory entry : ' + dirName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.31 DirectoryEntry.getDirectory: get DirectoryEntry for invalid path', function (done) {
+ if (isBrowser) {
+ /* The plugin does not follow to ["8.3 Naming restrictions"]
+ (http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). */
+ pending();
+ }
+
+ var dirName = 'de:invalid:path';
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef
+ done();
+ };
+ // create:false, exclusive:false, invalid path
+ root.getDirectory(dirName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getDirectory - Unexpected success callback, directory should not exists: ' + dirName), fail);
+ });
+ it('file.spec.32 DirectoryEntry.getDirectory: get DirectoryEntry for existing file', function (done) {
+ var fileName = 'de.existing.file';
+ var existingFile;
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns an unknown error with code 17
+ } else {
+ expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(existingFile.name, done);
+ };
+ // create file to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, function (file) {
+ existingFile = file;
+ root.getDirectory(fileName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getDirectory - Unexpected success callback, directory should not exists: ' + fileName), fail);
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.33 DirectoryEntry.getFile: get FileEntry for existing directory', function (done) {
+ var dirName = 'de.existing.dir';
+ var existingDir;
+ var fail = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns an unknown error with code 17
+ } else {
+ expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(existingDir.name, done);
+ };
+ // create directory to kick off it
+ root.getDirectory(dirName, {
+ create: true
+ }, function (directory) {
+ existingDir = directory;
+ root.getFile(dirName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Unexpected success callback, file should not exists: ' + dirName), fail);
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.34 DirectoryEntry.removeRecursively on directory', function (done) {
+ var dirName = 'de.removeRecursively';
+ var subDirName = 'dir';
+ var dirExists = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // create a new directory entry to kick off it
+ root.getDirectory(dirName, {
+ create: true
+ }, function (entry) {
+ entry.getDirectory(subDirName, {
+ create: true
+ }, function (dir) {
+ entry.removeRecursively(function () {
+ root.getDirectory(dirName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getDirectory - Unexpected success callback, directory should not exists: ' + dirName), dirExists);
+ }, failed.bind(null, done, 'entry.removeRecursively - Error removing directory recursively : ' + dirName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + subDirName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.35 createReader: create reader on existing directory', function () {
+ // create reader for root directory
+ var reader = root.createReader();
+ expect(reader).toBeDefined();
+ expect(typeof reader.readEntries).toBe('function');
+ });
+ it('file.spec.36 removeRecursively on root file system', function (done) {
+
+ var remove = function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of
+ NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively
+ on the root file system (Chrome). */
+ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+ } else {
+ expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ // remove root file system
+ root.removeRecursively(succeed.bind(null, done, 'root.removeRecursively - Unexpected success callback, root cannot be removed'), remove);
+ });
+ });
+ describe('DirectoryReader interface', function () {
+ describe('readEntries', function () {
+ it('file.spec.37 should read contents of existing directory', function (done) {
+ var reader;
+ var win = function (entries) {
+ expect(entries).toBeDefined();
+ expect(entries instanceof Array).toBe(true);
+ done();
+ };
+ // create reader for root directory
+ reader = root.createReader();
+ // read entries
+ reader.readEntries(win, failed.bind(null, done, 'reader.readEntries - Error reading entries'));
+ });
+ it('file.spec.37.1 should read contents of existing directory', function (done) {
+ var dirName = 'readEntries.dir';
+ var fileName = 'readeEntries.file';
+ root.getDirectory(dirName, {
+ create: true
+ }, function (directory) {
+ directory.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ var reader = directory.createReader();
+ reader.readEntries(function (entries) {
+ expect(entries).toBeDefined();
+ expect(entries instanceof Array).toBe(true);
+ expect(entries.length).toBe(1);
+ expect(entries[0].fullPath).toCanonicallyMatch(fileEntry.fullPath);
+ expect(entries[0].filesystem).not.toBe(null);
+ if (isChrome) {
+ // Slicing '[object {type}]' -> '{type}'
+ expect(entries[0].filesystem.toString().slice(8, -1)).toEqual('DOMFileSystem');
+ } else {
+ expect(entries[0].filesystem instanceof FileSystem).toBe(true); // eslint-disable-line no-undef
+ }
+
+ // cleanup
+ deleteEntry(directory.name, done);
+ }, failed.bind(null, done, 'reader.readEntries - Error reading entries from directory: ' + dirName));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + fileName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.109 should return an empty entry list on the second call', function (done) {
+ var reader;
+ var fileName = 'test109.txt';
+ // Add a file to ensure the root directory is non-empty and then read the contents of the directory.
+ root.getFile(fileName, {
+ create: true
+ }, function (entry) {
+ reader = root.createReader();
+ // First read
+ reader.readEntries(function (entries) {
+ expect(entries).toBeDefined();
+ expect(entries instanceof Array).toBe(true);
+ expect(entries.length).not.toBe(0);
+ // Second read
+ reader.readEntries(function (entries_) {
+ expect(entries_).toBeDefined();
+ expect(entries_ instanceof Array).toBe(true);
+ expect(entries_.length).toBe(0);
+ // Clean up
+ deleteEntry(entry.name, done);
+ }, failed.bind(null, done, 'reader.readEntries - Error during SECOND reading of entries from [root] directory'));
+ }, failed.bind(null, done, 'reader.readEntries - Error during FIRST reading of entries from [root] directory'));
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + fileName));
+ });
+ });
+ it('file.spec.38 should read contents of directory that has been removed', function (done) {
+ var dirName = 'de.createReader.notfound';
+ // create a new directory entry to kick off it
+ root.getDirectory(dirName, {
+ create: true
+ }, function (directory) {
+ directory.removeRecursively(function () {
+ var reader = directory.createReader();
+ reader.readEntries(succeed.bind(null, done, 'reader.readEntries - Unexpected success callback, it should not read entries from deleted dir: ' + dirName), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ root.getDirectory(dirName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getDirectory - Unexpected success callback, it should not get deleted directory: ' + dirName), function (err) {
+ expect(err).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ });
+ });
+ }, failed.bind(null, done, 'directory.removeRecursively - Error removing directory recursively : ' + dirName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ });
+ // DirectoryReader interface
+ describe('File', function () {
+ it('file.spec.39 constructor should be defined', function () {
+ expect(File).toBeDefined(); // eslint-disable-line no-undef
+ expect(typeof File).toBe('function');
+ });
+ it('file.spec.40 should be define File attributes', function () {
+ var file = new File(); // eslint-disable-line no-undef
+ expect(file.name).toBeDefined();
+ expect(file.type).toBeDefined();
+ expect(file.lastModifiedDate).toBeDefined();
+ expect(file.size).toBeDefined();
+ });
+ });
+ // File
+ describe('FileEntry', function () {
+
+ it('file.spec.41 should be define FileEntry methods', function (done) {
+ var fileName = 'fe.methods';
+ var testFileEntry = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(typeof fileEntry.createWriter).toBe('function');
+ expect(typeof fileEntry.file).toBe('function');
+ // cleanup
+ deleteEntry(fileEntry.name, done);
+ };
+ // create a new file entry to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, testFileEntry, failed.bind(null, done, 'root.getFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.42 createWriter should return a FileWriter object', function (done) {
+ var fileName = 'fe.createWriter';
+ var testFile;
+ var testWriter = function (writer) {
+ expect(writer).toBeDefined();
+ if (isChrome) {
+ // Slicing '[object {type}]' -> '{type}'
+ expect(writer.toString().slice(8, -1)).toEqual('FileWriter');
+ } else {
+ expect(writer instanceof FileWriter).toBe(true); // eslint-disable-line no-undef
+ }
+
+ // cleanup
+ deleteEntry(testFile.name, done);
+ };
+ // create a new file entry to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ testFile = fileEntry;
+ fileEntry.createWriter(testWriter, failed.bind(null, done, 'fileEntry.createWriter - Error creating Writer from entry'));
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.43 file should return a File object', function (done) {
+ var fileName = 'fe.file';
+ var newFile;
+ var testFile = function (file) {
+ expect(file).toBeDefined();
+ if (isChrome) {
+ // Slicing '[object {type}]' -> '{type}'
+ expect(file.toString().slice(8, -1)).toEqual('File');
+ } else {
+ expect(file instanceof File).toBe(true); // eslint-disable-line no-undef
+ }
+
+ // cleanup
+ deleteEntry(newFile.name, done);
+ };
+ // create a new file entry to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ newFile = fileEntry;
+ fileEntry.file(testFile, failed.bind(null, done, 'fileEntry.file - Error reading file using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.44 file: on File that has been removed', function (done) {
+ var fileName = 'fe.no.file';
+ // create a new file entry to kick off it
+ root.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ fileEntry.remove(function () {
+ fileEntry.file(succeed.bind(null, done, 'fileEntry.file - Unexpected success callback, file it should not be created from removed entry'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ });
+ }, failed.bind(null, done, 'fileEntry.remove - Error removing entry : ' + fileName));
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + fileName));
+ });
+ });
+ // FileEntry
+ describe('Entry', function () {
+ it('file.spec.45 Entry object', function (done) {
+ var fileName = 'entry';
+ var fullPath = joinURL(root.fullPath, fileName);
+ var winEntry = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(fileName);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ expect(typeof entry.getMetadata).toBe('function');
+ expect(typeof entry.setMetadata).toBe('function');
+ expect(typeof entry.moveTo).toBe('function');
+ expect(typeof entry.copyTo).toBe('function');
+ expect(typeof entry.toURL).toBe('function');
+ expect(typeof entry.remove).toBe('function');
+ expect(typeof entry.getParent).toBe('function');
+ expect(typeof entry.createWriter).toBe('function');
+ expect(typeof entry.file).toBe('function');
+ // Clean up
+ deleteEntry(fileName, done);
+ };
+ // create a new file entry
+ createFile(fileName, winEntry, failed.bind(null, done, 'createFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.46 Entry.getMetadata on file', function (done) {
+ var fileName = 'entry.metadata.file';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ entry.getMetadata(function (metadata) {
+ expect(metadata).toBeDefined();
+ expect(metadata.modificationTime instanceof Date).toBe(true);
+ expect(typeof metadata.size).toBe('number');
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'entry.getMetadata - Error getting metadata from entry : ' + fileName));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.47 Entry.getMetadata on directory', function (done) {
+ if (isIndexedDBShim) {
+ /* Does not support metadata for directories (Firefox, IE) */
+ pending();
+ }
+
+ var dirName = 'entry.metadata.dir';
+ // create a new directory entry
+ createDirectory(dirName, function (entry) {
+ entry.getMetadata(function (metadata) {
+ expect(metadata).toBeDefined();
+ expect(metadata.modificationTime instanceof Date).toBe(true);
+ expect(typeof metadata.size).toBe('number');
+ expect(metadata.size).toBe(0);
+ // cleanup
+ deleteEntry(dirName, done);
+ }, failed.bind(null, done, 'entry.getMetadata - Error getting metadata from entry : ' + dirName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.48 Entry.getParent on file in root file system', function (done) {
+ var fileName = 'entry.parent.file';
+ var rootPath = root.fullPath;
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ entry.getParent(function (parent) {
+ expect(parent).toBeDefined();
+ expect(parent.fullPath).toCanonicallyMatch(rootPath);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'entry.getParent - Error getting parent directory of file : ' + fileName));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.49 Entry.getParent on directory in root file system', function (done) {
+ var dirName = 'entry.parent.dir';
+ var rootPath = root.fullPath;
+ // create a new directory entry
+ createDirectory(dirName, function (entry) {
+ entry.getParent(function (parent) {
+ expect(parent).toBeDefined();
+ expect(parent.fullPath).toCanonicallyMatch(rootPath);
+ // cleanup
+ deleteEntry(dirName, done);
+ }, failed.bind(null, done, 'entry.getParent - Error getting parent directory of directory : ' + dirName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.50 Entry.getParent on root file system', function (done) {
+ var rootPath = root.fullPath;
+ var winParent = function (parent) {
+ expect(parent).toBeDefined();
+ expect(parent.fullPath).toCanonicallyMatch(rootPath);
+ done();
+ };
+ // create a new directory entry
+ root.getParent(winParent, failed.bind(null, done, 'root.getParent - Error getting parent directory of root'));
+ });
+ it('file.spec.51 Entry.toURL on file', function (done) {
+ var fileName = 'entry.uri.file';
+ var rootPath = root.fullPath;
+ var winURI = function (entry) {
+ var uri = entry.toURL();
+ expect(uri).toBeDefined();
+ expect(uri.indexOf(rootPath)).not.toBe(-1);
+ // cleanup
+ deleteEntry(fileName, done);
+ };
+ // create a new file entry
+ createFile(fileName, winURI, failed.bind(null, done, 'createFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.52 Entry.toURL on directory', function (done) {
+ var dirName_1 = 'num 1';
+ var dirName_2 = 'num 2';
+ var rootPath = root.fullPath;
+ createDirectory(dirName_1, function (entry) {
+ entry.getDirectory(dirName_2, {
+ create: true
+ }, function (entryFile) {
+ var uri = entryFile.toURL();
+ expect(uri).toBeDefined();
+ expect(uri).toContain('/num%201/num%202/');
+ expect(uri.indexOf(rootPath)).not.toBe(-1);
+ // cleanup
+ deleteEntry(dirName_1, done);
+ }, failed.bind(null, done, 'entry.getDirectory - Error creating directory : ' + dirName_2));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dirName_1));
+ });
+ it('file.spec.53 Entry.remove on file', function (done) {
+ var fileName = 'entr .rm.file';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ expect(entry).toBeDefined();
+ entry.remove(function () {
+ root.getFile(fileName, null, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get deleted file : ' + fileName), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(fileName, done);
+ });
+ }, failed.bind(null, done, 'entry.remove - Error removing entry : ' + fileName));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.53.1 Entry.remove on filename with #s', function (done) {
+ if (isBrowser) {
+ pending('Browsers can\'t do that');
+ }
+ var fileName = 'entry.#rm#.file';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ expect(entry).toBeDefined();
+ entry.remove(function () {
+ root.getFile(fileName, null, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get deleted file : ' + fileName), function (error) {
+ expect(error).toBeDefined();
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ // cleanup
+ deleteEntry(fileName, done);
+ });
+ }, failed.bind(null, done, 'entry.remove - Error removing entry : ' + fileName));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + fileName));
+ });
+ it('file.spec.54 remove on empty directory', function (done) {
+ var dirName = 'entry.rm.dir';
+ // create a new directory entry
+ createDirectory(dirName, function (entry) {
+ expect(entry).toBeDefined();
+ entry.remove(function () {
+ root.getDirectory(dirName, null, succeed.bind(null, done, 'root.getDirectory - Unexpected success callback, it should not get deleted directory : ' + dirName), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(dirName, done);
+ });
+ }, failed.bind(null, done, 'entry.remove - Error removing entry : ' + dirName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.55 remove on non-empty directory', function (done) {
+ if (isIndexedDBShim) {
+ /* Both Entry.remove and directoryEntry.removeRecursively don't fail when removing
+ non-empty directories - directories being removed are cleaned
+ along with contents instead (Firefox, IE) */
+ pending();
+ }
+
+ var dirName = 'ent y.rm.dir.not.empty';
+ var fileName = 're ove.txt';
+ var fullPath = joinURL(root.fullPath, dirName);
+ // create a new directory entry
+ createDirectory(dirName, function (entry) {
+ entry.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ entry.remove(succeed.bind(null, done, 'entry.remove - Unexpected success callback, it should not remove a directory that contains files : ' + dirName), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome is returning unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ root.getDirectory(dirName, null, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ // cleanup
+ deleteEntry(dirName, done);
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory : ' + dirName));
+ });
+ }, failed.bind(null, done, 'entry.getFile - Error creating file : ' + fileName + ' inside of ' + dirName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dirName));
+ });
+ it('file.spec.56 remove on root file system', function (done) {
+
+ // remove entry that doesn't exist
+ root.remove(succeed.bind(null, done, 'entry.remove - Unexpected success callback, it should not remove entry that it does not exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of
+ NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively
+ on the root file system. */
+ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+ } else {
+ expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ });
+ });
+ it('file.spec.57 copyTo: file', function (done) {
+ var file1 = 'entry copy.file1';
+ var file2 = 'entry copy.file2';
+ var fullPath = joinURL(root.fullPath, file2);
+ // create a new file entry to kick off it
+ deleteEntry(file2, function () {
+ createFile(file1, function (fileEntry) {
+ // copy file1 to file2
+ fileEntry.copyTo(root, file2, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ expect(entry.name).toCanonicallyMatch(file2);
+ root.getFile(file2, {
+ create: false
+ }, function (entry2) {
+ expect(entry2).toBeDefined();
+ expect(entry2.isFile).toBe(true);
+ expect(entry2.isDirectory).toBe(false);
+ expect(entry2.fullPath).toCanonicallyMatch(fullPath);
+ expect(entry2.name).toCanonicallyMatch(file2);
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(file2, done);
+ });
+ }, failed.bind(null, done, 'root.getFile - Error getting copied file : ' + file2));
+ }, failed.bind(null, done, 'fileEntry.copyTo - Error copying file : ' + file2));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'deleteEntry - Error removing file : ' + file2));
+ });
+ it('file.spec.58 copyTo: file onto itself', function (done) {
+ var file1 = 'entry.copy.fos.file1';
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // copy file1 onto itself
+ entry.copyTo(root, null, succeed.bind(null, done, 'entry.copyTo - Unexpected success callback, it should not copy a null file'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(file1, done);
+ });
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ });
+ it('file.spec.59 copyTo: directory', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.copy.srcDir';
+ var dstDir = 'entry.copy.dstDir';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ var filePath = joinURL(dstPath, file1);
+ // create a new directory entry to kick off it
+ deleteEntry(dstDir, function () {
+ createDirectory(srcDir, function (directory) {
+ // create a file within new directory
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ directory.copyTo(root, dstDir, function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.fullPath).toCanonicallyMatch(dstPath);
+ expect(directory.name).toCanonicallyMatch(dstDir);
+ root.getDirectory(dstDir, {
+ create: false
+ }, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.isFile).toBe(false);
+ expect(dirEntry.isDirectory).toBe(true);
+ expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+ expect(dirEntry.name).toCanonicallyMatch(dstDir);
+ dirEntry.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(true);
+ expect(fileEntry.isDirectory).toBe(false);
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ expect(fileEntry.name).toCanonicallyMatch(file1);
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ }, failed.bind(null, done, 'dirEntry.getFile - Error getting file : ' + file1));
+ }, failed.bind(null, done, 'root.getDirectory - Error getting copied directory : ' + dstDir));
+ }, failed.bind(null, done, 'directory.copyTo - Error copying directory : ' + srcDir + ' to :' + dstDir));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.60 copyTo: directory to backup at same root directory', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.copy srcDirSame';
+ var dstDir = 'entry.copy srcDirSame-backup';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ var filePath = joinURL(dstPath, file1);
+ // create a new directory entry to kick off it
+ deleteEntry(dstDir, function () {
+ createDirectory(srcDir, function (directory) {
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ directory.copyTo(root, dstDir, function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.fullPath).toCanonicallyMatch(dstPath);
+ expect(directory.name).toCanonicallyMatch(dstDir);
+ root.getDirectory(dstDir, {
+ create: false
+ }, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.isFile).toBe(false);
+ expect(dirEntry.isDirectory).toBe(true);
+ expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+ expect(dirEntry.name).toCanonicallyMatch(dstDir);
+ dirEntry.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(true);
+ expect(fileEntry.isDirectory).toBe(false);
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ expect(fileEntry.name).toCanonicallyMatch(file1);
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ }, failed.bind(null, done, 'dirEntry.getFile - Error getting file : ' + file1));
+ }, failed.bind(null, done, 'root.getDirectory - Error getting copied directory : ' + dstDir));
+ }, failed.bind(null, done, 'directory.copyTo - Error copying directory : ' + srcDir + ' to :' + dstDir));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.61 copyTo: directory onto itself', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.copy.dos.srcDir';
+ var srcPath = joinURL(root.fullPath, srcDir);
+ var filePath = joinURL(srcPath, file1);
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // create a file within new directory
+ directory.getFile(file1, {
+ create: true
+ }, function (fileEntry) {
+ // copy srcDir onto itself
+ directory.copyTo(root, null, succeed.bind(null, done, 'directory.copyTo - Unexpected success callback, it should not copy file: ' + srcDir + ' to a null destination'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+ dirEntry.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(srcDir, done);
+ }, failed.bind(null, done, 'dirEntry.getFile - Error getting file : ' + file1));
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory : ' + srcDir));
+ });
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.62 copyTo: directory into itself', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var srcDir = 'entry.copy.dis.srcDir';
+ var dstDir = 'entry.copy.dis.dstDir';
+ var srcPath = joinURL(root.fullPath, srcDir);
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // copy source directory into itself
+ directory.copyTo(directory, dstDir, succeed.bind(null, done, 'directory.copyTo - Unexpected success callback, it should not copy a directory ' + srcDir + ' into itself'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (dirEntry) {
+ // returning confirms existence so just check fullPath entry
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+ // cleanup
+ deleteEntry(srcDir, done);
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory : ' + srcDir));
+ });
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.63 copyTo: directory that does not exist', function (done) {
+ var file1 = 'entry.copy.dnf.file1';
+ var dirName = 'dir-foo';
+ createFile(file1, function (fileEntry) {
+ createDirectory(dirName, function (dirEntry) {
+ dirEntry.remove(function () {
+ fileEntry.copyTo(dirEntry, null, succeed.bind(null, done, 'fileEntry.copyTo - Unexpected success callback, it should not copy a file ' + file1 + ' into a removed directory'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ });
+ }, failed.bind(null, done, 'dirEntry.remove - Error removing directory : ' + dirName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dirName));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ });
+ it('file.spec.64 copyTo: invalid target name', function (done) {
+ if (isBrowser) {
+ /* The plugin does not follow ["8.3 Naming restrictions"]
+ (http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions */
+ pending();
+ }
+
+ var file1 = 'entry.copy.itn.file1';
+ var file2 = 'bad:file:name';
+ // create a new file entry
+ createFile(file1, function (entry) {
+ // copy file1 to file2
+ entry.copyTo(root, file2, succeed.bind(null, done, 'entry.copyTo - Unexpected success callback, it should not copy a file ' + file1 + ' to an invalid file name: ' + file2), function (error) {
+ expect(error).toBeDefined();
+ expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef
+ // cleanup
+ deleteEntry(file1, done);
+ });
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ });
+ it('file.spec.65 moveTo: file to same parent', function (done) {
+ var file1 = 'entry.move.fsp.file1';
+ var file2 = 'entry.move.fsp.file2';
+ var dstPath = joinURL(root.fullPath, file2);
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // move file1 to file2
+ entry.moveTo(root, file2, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.fullPath).toCanonicallyMatch(dstPath);
+ expect(entry.name).toCanonicallyMatch(file2);
+ root.getFile(file2, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(dstPath);
+ root.getFile(file1, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get invalid or moved file: ' + file1), function (error) {
+ // expect(navigator.fileMgr.testFileExists(srcPath) === false, "original file should not exist.");
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(file2, done);
+ });
+ });
+ }, failed.bind(null, done, 'root.getFile - Error getting file : ' + file2));
+ }, failed.bind(null, done, 'entry.moveTo - Error moving file : ' + file1 + ' to root as: ' + file2));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ });
+ it('file.spec.66 moveTo: file to new parent', function (done) {
+ var file1 = 'entry.move.fnp.file1';
+ var dir = 'entry.move.fnp.dir';
+ var dstPath = joinURL(joinURL(root.fullPath, dir), file1);
+ // ensure destination directory is cleaned up first
+ deleteEntry(dir, function () {
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // create a parent directory to move file to
+ root.getDirectory(dir, {
+ create: true
+ }, function (directory) {
+ // move file1 to new directory
+ // move the file
+ entry.moveTo(directory, null, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.fullPath).toCanonicallyMatch(dstPath);
+ expect(entry.name).toCanonicallyMatch(file1);
+ // test the moved file exists
+ directory.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(dstPath);
+ root.getFile(file1, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get invalid or moved file: ' + file1), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(dir, done);
+ });
+ });
+ }, failed.bind(null, done, 'directory.getFile - Error getting file : ' + file1 + ' from: ' + dir));
+ }, failed.bind(null, done, 'entry.moveTo - Error moving file : ' + file1 + ' to: ' + dir + ' with the same name'));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dir));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dir));
+ });
+ it('file.spec.67 moveTo: directory to same parent', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.move.dsp.srcDir';
+ var dstDir = 'entry.move.dsp.dstDir';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ var filePath = joinURL(dstPath, file1);
+ // ensure destination directory is cleaned up before it
+ deleteEntry(dstDir, function () {
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // create a file within directory
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ // move srcDir to dstDir
+ directory.moveTo(root, dstDir, function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.fullPath).toCanonicallyMatch(dstPath);
+ expect(directory.name).toCanonicallyMatch(dstDir);
+ // test that moved file exists in destination dir
+ directory.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // check that the moved file no longer exists in original dir
+ root.getFile(file1, {
+ create: false
+ }, succeed.bind(null, done, 'directory.getFile - Unexpected success callback, it should not get invalid or moved file: ' + file1), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ });
+ }, failed.bind(null, done, 'directory.getFile - Error getting file : ' + file1 + ' from: ' + srcDir));
+ }, failed.bind(null, done, 'entry.moveTo - Error moving directory : ' + srcDir + ' to root as: ' + dstDir));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.68 moveTo: directory to same parent with same name', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.move.dsp.srcDir';
+ var dstDir = 'entry.move.dsp.srcDir-backup';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ var filePath = joinURL(dstPath, file1);
+ // ensure destination directory is cleaned up before it
+ deleteEntry(dstDir, function () {
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // create a file within directory
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ // move srcDir to dstDir
+ directory.moveTo(root, dstDir, function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.fullPath).toCanonicallyMatch(dstPath);
+ expect(directory.name).toCanonicallyMatch(dstDir);
+ // check that moved file exists in destination dir
+ directory.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // check that the moved file no longer exists in original dir
+ root.getFile(file1, {
+ create: false
+ }, succeed.bind(null, done, 'directory.getFile - Unexpected success callback, it should not get invalid or moved file: ' + file1), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ });
+ }, failed.bind(null, done, 'directory.getFile - Error getting file : ' + file1 + ' from: ' + srcDir));
+ }, failed.bind(null, done, 'entry.moveTo - Error moving directory : ' + srcDir + ' to root as: ' + dstDir));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.69 moveTo: directory to new parent', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.move.dnp.srcDir';
+ var dstDir = 'entry.move.dnp.dstDir';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ var filePath = joinURL(dstPath, file1);
+ // ensure destination directory is cleaned up before it
+ deleteEntry(dstDir, function () {
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // create a file within directory
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ // move srcDir to dstDir
+ directory.moveTo(root, dstDir, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.isFile).toBe(false);
+ expect(dirEntry.isDirectory).toBe(true);
+ expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+ expect(dirEntry.name).toCanonicallyMatch(dstDir);
+ // test that moved file exists in destination dir
+ dirEntry.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // test that the moved file no longer exists in original dir
+ root.getFile(file1, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get invalid or moved file: ' + file1), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ });
+ }, failed.bind(null, done, 'directory.getFile - Error getting file : ' + file1 + ' from: ' + dstDir));
+ }, failed.bind(null, done, 'directory.moveTo - Error moving directory : ' + srcDir + ' to root as: ' + dstDir));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+
+ it('file.spec.131 moveTo: directories tree to new parent', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var srcDir = 'entry.move.dnp.srcDir';
+ var srcDirNestedFirst = 'entry.move.dnp.srcDir.Nested1';
+ var srcDirNestedSecond = 'entry.move.dnp.srcDir.Nested2';
+ var dstDir = 'entry.move.dnp.dstDir';
+
+ createDirectory(dstDir, function (dstDirectory) {
+ createDirectory(srcDir, function (srcDirectory) {
+ srcDirectory.getDirectory(srcDirNestedFirst, { create: true }, function () {
+ srcDirectory.getDirectory(srcDirNestedSecond, { create: true }, function () {
+ srcDirectory.moveTo(dstDirectory, srcDir, function successMove (transferredDirectory) {
+ var directoryReader = transferredDirectory.createReader();
+ directoryReader.readEntries(function successRead (entries) {
+ expect(entries.length).toBe(2);
+ if (!isChrome) {
+ expect(entries[0].name).toBe(srcDirNestedFirst);
+ expect(entries[1].name).toBe(srcDirNestedSecond);
+ }
+ deleteEntry(dstDir, done);
+ }, failed.bind(null, done, 'Error getting entries from: ' + transferredDirectory));
+ }, failed.bind(null, done, 'directory.moveTo - Error moving directory : ' + srcDir + ' to root as: ' + dstDir));
+ }, failed.bind(null, done, 'directory.getDirectory - Error creating directory : ' + srcDirNestedSecond));
+ }, failed.bind(null, done, 'directory.getDirectory - Error creating directory : ' + srcDirNestedFirst));
+ }, failed.bind(null, done, 'createDirectory - Error creating source directory : ' + srcDir));
+ }, failed.bind(null, done, 'createDirectory - Error creating dest directory : ' + dstDir));
+ });
+
+ it('file.spec.70 moveTo: directory onto itself', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.move.dos.srcDir';
+ var srcPath = joinURL(root.fullPath, srcDir);
+ var filePath = joinURL(srcPath, file1);
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // create a file within new directory
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ // move srcDir onto itself
+ directory.moveTo(root, null, succeed.bind(null, done, 'directory.moveTo - Unexpected success callback, it should not move directory to invalid path'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // test that original dir still exists
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (dirEntry) {
+ // returning confirms existence so just check fullPath entry
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+ dirEntry.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(srcDir, done);
+ }, failed.bind(null, done, 'dirEntry.getFile - Error getting file : ' + file1 + ' from: ' + srcDir));
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory : ' + srcDir));
+ });
+ }, failed.bind(null, done, 'directory.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.71 moveTo: directory into itself', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var srcDir = 'entry.move.dis.srcDir';
+ var dstDir = 'entry.move.dis.dstDir';
+ var srcPath = joinURL(root.fullPath, srcDir);
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // move source directory into itself
+ directory.moveTo(directory, dstDir, succeed.bind(null, done, 'directory.moveTo - Unexpected success callback, it should not move a directory into itself: ' + srcDir), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // make sure original directory still exists
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.fullPath).toCanonicallyMatch(srcPath);
+ // cleanup
+ deleteEntry(srcDir, done);
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory, making sure that original directory still exists: ' + srcDir));
+ });
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.130 moveTo: directory into similar directory', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var srcDir = 'entry.move.dis.srcDir';
+ var dstDir = 'entry.move.dis.srcDir-backup';
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (srcDirEntry) {
+ deleteEntry(dstDir, function () {
+ createDirectory(dstDir, function (dstDirEntry) {
+ // move source directory into itself
+ srcDirEntry.moveTo(dstDirEntry, 'file', function (newDirEntry) {
+ expect(newDirEntry).toBeDefined();
+ deleteEntry(dstDir, done);
+ }, failed.bind(null, done, 'directory.moveTo - Error moving a directory into a similarly-named directory: ' + srcDir));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + dstDir));
+ }, failed.bind(null, done, 'deleteEntry - Error deleting directory : ' + dstDir));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.72 moveTo: file onto itself', function (done) {
+ var file1 = 'entry.move.fos.file1';
+ var filePath = joinURL(root.fullPath, file1);
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // move file1 onto itself
+ entry.moveTo(root, null, succeed.bind(null, done, 'entry.moveTo - Unexpected success callback, it should not move a file: ' + file1 + ' into the same parent'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // test that original file still exists
+ root.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(file1, done);
+ }, failed.bind(null, done, 'root.getFile - Error getting file, making sure that original file still exists: ' + file1));
+ });
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ });
+ it('file.spec.73 moveTo: file onto existing directory', function (done) {
+ var file1 = 'entry.move.fod.file1';
+ var dstDir = 'entry.move.fod.dstDir';
+ var subDir = 'subDir';
+ var dirPath = joinURL(joinURL(root.fullPath, dstDir), subDir);
+ var filePath = joinURL(root.fullPath, file1);
+ // ensure destination directory is cleaned up before it
+ deleteEntry(dstDir, function () {
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // create top level directory
+ root.getDirectory(dstDir, {
+ create: true
+ }, function (directory) {
+ // create sub-directory
+ directory.getDirectory(subDir, {
+ create: true
+ }, function (subDirectory) {
+ // move file1 onto sub-directory
+ entry.moveTo(directory, subDir, succeed.bind(null, done, 'entry.moveTo - Unexpected success callback, it should not move a file: ' + file1 + ' into directory: ' + dstDir + '\n' + subDir + ' directory already exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // check that original dir still exists
+ directory.getDirectory(subDir, {
+ create: false
+ }, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(dirPath);
+ // check that original file still exists
+ root.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(dstDir, done);
+ });
+ }, failed.bind(null, done, 'root.getFile - Error getting file, making sure that original file still exists: ' + file1));
+ }, failed.bind(null, done, 'directory.getDirectory - Error getting directory, making sure that original directory still exists: ' + subDir));
+ });
+ }, failed.bind(null, done, 'directory.getDirectory - Error creating directory : ' + subDir));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory : ' + dstDir));
+ }, failed.bind(null, done, 'createFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.74 moveTo: directory onto existing file', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'entry.move.dof.file1';
+ var srcDir = 'entry.move.dof.srcDir';
+ var dirPath = joinURL(root.fullPath, srcDir);
+ var filePath = joinURL(root.fullPath, file1);
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (entry) {
+ // create file
+ root.getFile(file1, {
+ create: true
+ }, function (fileEntry) {
+ // move directory onto file
+ entry.moveTo(root, file1, succeed.bind(null, done, 'entry.moveTo - Unexpected success callback, it should not move : \n' + srcDir + ' into root directory renamed as ' + file1 + '\n' + file1 + ' file already exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // test that original directory exists
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (dirEntry) {
+ // returning confirms existence so just check fullPath entry
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(dirPath);
+ // test that original file exists
+ root.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(srcDir, done);
+ });
+ }, failed.bind(null, done, 'root.getFile - Error getting file, making sure that original file still exists: ' + file1));
+ }, failed.bind(null, done, 'directory.getDirectory - Error getting directory, making sure that original directory still exists: ' + srcDir));
+ });
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.75 copyTo: directory onto existing file', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'entry.copy.dof.file1';
+ var srcDir = 'entry.copy.dof.srcDir';
+ var dirPath = joinURL(root.fullPath, srcDir);
+ var filePath = joinURL(root.fullPath, file1);
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (entry) {
+ // create file
+ root.getFile(file1, {
+ create: true
+ }, function () {
+ // copy directory onto file
+ entry.copyTo(root, file1, succeed.bind(null, done, 'entry.copyTo - Unexpected success callback, it should not copy : \n' + srcDir + ' into root directory renamed as ' + file1 + '\n' + file1 + ' file already exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // check that original dir still exists
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (dirEntry) {
+ // returning confirms existence so just check fullPath entry
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(dirPath);
+ // test that original file still exists
+ root.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(srcDir, done);
+ });
+ }, failed.bind(null, done, 'root.getFile - Error getting file, making sure that original file still exists: ' + file1));
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory, making sure that original directory still exists: ' + srcDir));
+ });
+ }, failed.bind(null, done, 'root.getFile - Error creating file : ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ });
+ it('file.spec.76 moveTo: directory onto directory that is not empty', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var srcDir = 'entry.move.dod.srcDir';
+ var dstDir = 'entry.move.dod.dstDir';
+ var subDir = 'subDir';
+ var srcPath = joinURL(root.fullPath, srcDir);
+ var dstPath = joinURL(joinURL(root.fullPath, dstDir), subDir);
+ // ensure destination directory is cleaned up before it
+ deleteEntry(dstDir, function () {
+ // create a new file entry to kick off it
+ createDirectory(srcDir, function (entry) {
+ // create top level directory
+ root.getDirectory(dstDir, {
+ create: true
+ }, function (directory) {
+ // create sub-directory
+ directory.getDirectory(subDir, {
+ create: true
+ }, function () {
+ // move srcDir onto dstDir (not empty)
+ entry.moveTo(root, dstDir, succeed.bind(null, done, 'entry.moveTo - Unexpected success callback, it should not copy : \n' + srcDir + ' into root directory renamed as ' + dstDir + '\n' + dstDir + ' directory already exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ // chrome returns unknown error with code 13
+ } else {
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ }
+ // making sure destination directory still exists
+ directory.getDirectory(subDir, {
+ create: false
+ }, function (dirEntry) {
+ // returning confirms existence so just check fullPath entry
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+ // making sure source directory exists
+ root.getDirectory(srcDir, {
+ create: false
+ }, function (srcEntry) {
+ expect(srcEntry).toBeDefined();
+ expect(srcEntry.fullPath).toCanonicallyMatch(srcPath);
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ }, failed.bind(null, done, 'root.getDirectory - Error getting directory, making sure that original directory still exists: ' + srcDir));
+ }, failed.bind(null, done, 'directory.getDirectory - Error getting directory, making sure that original directory still exists: ' + subDir));
+ });
+ }, failed.bind(null, done, 'directory.getDirectory - Error creating directory : ' + subDir));
+ }, failed.bind(null, done, 'directory.getDirectory - Error creating directory : ' + subDir));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory : ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.77 moveTo: file replace existing file', function (done) {
+ var file1 = 'entry.move.frf.file1';
+ var file2 = 'entry.move.frf.file2';
+ var file2Path = joinURL(root.fullPath, file2);
+ // create a new directory entry to kick off it
+ createFile(file1, function (entry) {
+ // create file
+ root.getFile(file2, {
+ create: true
+ }, function () {
+ // replace file2 with file1
+ entry.moveTo(root, file2, function (entry2) {
+ expect(entry2).toBeDefined();
+ expect(entry2.isFile).toBe(true);
+ expect(entry2.isDirectory).toBe(false);
+ expect(entry2.fullPath).toCanonicallyMatch(file2Path);
+ expect(entry2.name).toCanonicallyMatch(file2);
+ // old file should not exists
+ root.getFile(file1, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Unexpected success callback, file: ' + file1 + ' should not exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // test that new file exists
+ root.getFile(file2, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(file2Path);
+ // cleanup
+ deleteEntry(file1, function () {
+ deleteEntry(file2, done);
+ });
+ }, failed.bind(null, done, 'root.getFile - Error getting moved file: ' + file2));
+ });
+ }, failed.bind(null, done, 'entry.moveTo - Error moving file : ' + file1 + ' to root as: ' + file2));
+ }, failed.bind(null, done, 'root.getFile - Error creating file: ' + file2));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + file1));
+ });
+ it('file.spec.78 moveTo: directory replace empty directory', function (done) {
+ if (isIndexedDBShim) {
+ /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */
+ pending();
+ }
+
+ var file1 = 'file1';
+ var srcDir = 'entry.move.drd.srcDir';
+ var dstDir = 'entry.move.drd.dstDir';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ var filePath = dstPath + '/' + file1;
+ // ensure destination directory is cleaned up before it
+ deleteEntry(dstDir, function () {
+ // create a new directory entry to kick off it
+ createDirectory(srcDir, function (directory) {
+ // create a file within source directory
+ directory.getFile(file1, {
+ create: true
+ }, function () {
+ // create destination directory
+ root.getDirectory(dstDir, {
+ create: true
+ }, function () {
+ // move srcDir to dstDir
+ directory.moveTo(root, dstDir, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.isFile).toBe(false);
+ expect(dirEntry.isDirectory).toBe(true);
+ expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+ expect(dirEntry.name).toCanonicallyMatch(dstDir);
+ // check that old directory contents have been moved
+ dirEntry.getFile(file1, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+ // check that old directory no longer exists
+ root.getDirectory(srcDir, {
+ create: false
+ }, succeed.bind(null, done, 'root.getDirectory - Unexpected success callback, directory: ' + srcDir + ' should not exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(srcDir, function () {
+ deleteEntry(dstDir, done);
+ });
+ });
+ }, failed.bind(null, done, 'dirEntry.getFile - Error getting moved file: ' + file1));
+ }, failed.bind(null, done, 'entry.moveTo - Error moving directory : ' + srcDir + ' to root as: ' + dstDir));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory: ' + dstDir));
+ }, failed.bind(null, done, 'root.getFile - Error creating file: ' + file1));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory: ' + srcDir));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ });
+ it('file.spec.79 moveTo: directory that does not exist', function (done) {
+ if (isChrome) {
+ pending('chrome freak out about non-existend dir not being a DirectoryEntry');
+ }
+ var file1 = 'entry.move.dnf.file1';
+ var dstDir = 'entry.move.dnf.dstDir';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // move file to directory that does not exist
+ var directory = new DirectoryEntry(); // eslint-disable-line no-undef
+ directory.filesystem = root.filesystem;
+ directory.fullPath = dstPath;
+ entry.moveTo(directory, null, succeed.bind(null, done, 'entry.moveTo - Unexpected success callback, parent directory: ' + dstPath + ' should not exists'), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ /* INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NOT_FOUND_ERR(code: 1)
+ on trying to moveTo directory that does not exist. */
+ expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ // cleanup
+ deleteEntry(file1, done);
+ });
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + file1));
+ });
+ it('file.spec.80 moveTo: invalid target name', function (done) {
+ if (isBrowser) {
+ /* The plugin does not follow ["8.3 Naming restrictions"]
+ (http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions */
+ pending();
+ }
+
+ var file1 = 'entry.move.itn.file1';
+ var file2 = 'bad:file:name';
+ // create a new file entry to kick off it
+ createFile(file1, function (entry) {
+ // move file1 to file2
+ entry.moveTo(root, file2, succeed.bind(null, done, 'entry.moveTo - Unexpected success callback, : ' + file1 + ' to root as: ' + file2), function (error) {
+ expect(error).toBeDefined();
+ expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef
+ // cleanup
+ deleteEntry(file1, done);
+ });
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + file1));
+ });
+ });
+ // Entry
+ describe('FileReader', function () {
+ it('file.spec.81 should have correct methods', function () {
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ expect(reader).toBeDefined();
+ expect(typeof reader.readAsBinaryString).toBe('function');
+ expect(typeof reader.readAsDataURL).toBe('function');
+ expect(typeof reader.readAsText).toBe('function');
+ expect(typeof reader.readAsArrayBuffer).toBe('function');
+ expect(typeof reader.abort).toBe('function');
+ expect(reader.result).toBe(null);
+ });
+ });
+ // FileReader
+ describe('Read method', function () {
+ it('file.spec.82 should error out on non-existent file', function (done) {
+ var fileName = cordova.platformId === 'windowsphone' ? root.toURL() + '/' + 'somefile.txt' : 'somefile.txt'; // eslint-disable-line no-undef
+ var verifier = function (evt) {
+ expect(evt).toBeDefined();
+ if (isChrome) {
+ expect(evt.target.error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(evt.target.error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ };
+ root.getFile(fileName, {
+ create: true
+ }, function (entry) {
+ entry.file(function (file) {
+ deleteEntry(fileName, function () {
+ // Create FileReader
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onerror = verifier;
+ reader.onload = succeed.bind(null, done, 'reader.onload - Unexpected success callback, file: ' + fileName + ' it should not exists');
+ reader.readAsText(file);
+ }, failed.bind(null, done, 'deleteEntry - Error removing file: ' + fileName));
+ }, failed.bind(null, done, 'entry.file - Error reading file: ' + fileName));
+ }, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.83 should be able to read native blob objects', function (done) {
+ // Skip test if blobs are not supported (e.g.: Android 2.3).
+ if (typeof window.Blob === 'undefined' || typeof window.Uint8Array === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ }
+ var contents = 'asdf';
+ var uint8Array = new Uint8Array(contents.length);
+ for (var i = 0; i < contents.length; ++i) {
+ uint8Array[i] = contents.charCodeAt(i);
+ }
+ var Builder = window.BlobBuilder || window.WebKitBlobBuilder;
+ var blob;
+ if (Builder) {
+ var builder = new Builder();
+ builder.append(uint8Array.buffer);
+ builder.append(contents);
+ blob = builder.getBlob('text/plain');
+ } else {
+ try {
+ // iOS 6 does not support Views, so pass in the buffer.
+ blob = new Blob([uint8Array.buffer, contents]); // eslint-disable-line no-undef
+ } catch (e) {
+ // Skip the test if we can't create a blob (e.g.: iOS 5).
+ if (e instanceof TypeError) {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ }
+ throw e;
+ }
+ }
+ var verifier = function (evt) {
+ expect(evt).toBeDefined();
+ expect(evt.target.result).toBe('asdfasdf');
+ done();
+ };
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onloadend = verifier;
+ reader.readAsText(blob);
+ });
+ function writeDummyFile (writeBinary, callback, done, fileContents) {
+ var fileName = 'dummy.txt';
+ var fileEntry = null;
+ // use default string if file data is not provided
+ var fileData = fileContents !== undefined ? fileContents :
+ '\u20AC\xEB - There is an exception to every rule. Except this one.';
+ var fileDataAsBinaryString = fileContents !== undefined ? fileContents :
+ '\xe2\x82\xac\xc3\xab - There is an exception to every rule. Except this one.';
+
+ function createWriter (fe) {
+ fileEntry = fe;
+ fileEntry.createWriter(writeFile, failed.bind(null, done, 'fileEntry.createWriter - Error reading file: ' + fileName));
+ }
+
+ // writes file and reads it back in
+ function writeFile (writer) {
+ writer.onwriteend = function () {
+ fileEntry.file(function (f) {
+ callback(fileEntry, f, fileData, fileDataAsBinaryString);
+ }, failed.bind(null, done, 'writer.onwriteend - Error writing data on file: ' + fileName));
+ };
+ writer.write(fileData);
+ }
+
+ fileData += writeBinary ? 'bin:\x01\x00' : '';
+ fileDataAsBinaryString += writeBinary ? 'bin:\x01\x00' : '';
+ // create a file, write to it, and read it in again
+ createFile(fileName, createWriter, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ }
+ function runReaderTest (funcName, writeBinary, done, progressFunc, verifierFunc, sliceStart, sliceEnd, fileContents) {
+ writeDummyFile(writeBinary, function (fileEntry, file, fileData, fileDataAsBinaryString) {
+ var verifier = function (evt) {
+ expect(evt).toBeDefined();
+ verifierFunc(evt, fileData, fileDataAsBinaryString);
+ };
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onprogress = progressFunc;
+ reader.onload = verifier;
+ reader.onerror = failed.bind(null, done, 'reader.onerror - Error reading file: ' + file + ' using function: ' + funcName);
+ if (sliceEnd !== undefined) {
+ // 'type' is specified so that is will be preserved in the resulting file:
+ // http://www.w3.org/TR/FileAPI/#slice-method-algo -> "6.4.1. The slice method" -> 4. A), 6. c)
+ file = file.slice(sliceStart, sliceEnd, file.type);
+ } else if (sliceStart !== undefined) {
+ file = file.slice(sliceStart, file.size, file.type);
+ }
+ reader[funcName](file);
+ }, done, fileContents);
+ }
+ function arrayBufferEqualsString (ab, str) {
+ var buf = new Uint8Array(ab);
+ var match = buf.length === str.length;
+ for (var i = 0; match && i < buf.length; i++) {
+ match = buf[i] === str.charCodeAt(i);
+ }
+ return match;
+ }
+ it('file.spec.84 should read file properly, readAsText', function (done) {
+ runReaderTest('readAsText', false, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe(fileData);
+ done();
+ });
+ });
+ it('file.spec.84.1 should read JSON file properly, readAsText', function (done) {
+ var testObject = {key1: 'value1', key2: 2};
+ runReaderTest('readAsText', false, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toEqual(JSON.stringify(testObject));
+ done();
+ }, undefined, undefined, JSON.stringify(testObject));
+ });
+ it('file.spec.85 should read file properly, Data URI', function (done) {
+ runReaderTest('readAsDataURL', true, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ /* `readAsDataURL` function is supported, but the mediatype in Chrome depends on entry name extension,
+ mediatype in IE is always empty (which is the same as `text-plain` according the specification),
+ the mediatype in Firefox is always `application/octet-stream`.
+ For example, if the content is `abcdefg` then Firefox returns `data:application/octet-stream;base64,YWJjZGVmZw==`,
+ IE returns `data:;base64,YWJjZGVmZw==`, Chrome returns `data:<mediatype depending on extension of entry name>;base64,YWJjZGVmZw==`. */
+ expect(evt.target.result).toBeDataUrl();
+
+ // The atob function it is completely ignored during mobilespec execution, besides the returned object: evt
+ // it is encoded and the atob function is aimed to decode a string. Even with btoa (encode) the function it gets stucked
+ // because of the Unicode characters that contains the fileData object.
+ // Issue reported at JIRA with all the details: CB-7095
+
+ // expect(evt.target.result.slice(23)).toBe(atob(fileData));
+
+ done();
+ });
+ });
+ it('file.spec.86 should read file properly, readAsBinaryString', function (done) {
+ if (isIE) {
+ /* `readAsBinaryString` function is not supported by IE and has not the stub. */
+ pending();
+ }
+
+ runReaderTest('readAsBinaryString', true, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe(fileDataAsBinaryString);
+ done();
+ });
+ });
+ it('file.spec.87 should read file properly, readAsArrayBuffer', function (done) {
+ // Skip test if ArrayBuffers are not supported (e.g.: Android 2.3).
+ if (typeof window.ArrayBuffer === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ }
+ runReaderTest('readAsArrayBuffer', true, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(arrayBufferEqualsString(evt.target.result, fileDataAsBinaryString)).toBe(true);
+ done();
+ });
+ });
+ it('file.spec.88 should read sliced file: readAsText', function (done) {
+ runReaderTest('readAsText', false, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe(fileDataAsBinaryString.slice(10, 40));
+ done();
+ }, 10, 40);
+ });
+ it('file.spec.89 should read sliced file: slice past eof', function (done) {
+ runReaderTest('readAsText', false, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe(fileData.slice(-5, 9999));
+ done();
+ }, -5, 9999);
+ });
+ it('file.spec.90 should read sliced file: slice to eof', function (done) {
+ runReaderTest('readAsText', false, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe(fileData.slice(-5));
+ done();
+ }, -5);
+ });
+ it('file.spec.91 should read empty slice', function (done) {
+ runReaderTest('readAsText', false, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe('');
+ done();
+ }, 0, 0);
+ });
+ it('file.spec.92 should read sliced file properly, readAsDataURL', function (done) {
+ runReaderTest('readAsDataURL', true, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ /* `readAsDataURL` function is supported, but the mediatype in Chrome depends on entry name extension,
+ mediatype in IE is always empty (which is the same as `text-plain` according the specification),
+ the mediatype in Firefox is always `application/octet-stream`.
+ For example, if the content is `abcdefg` then Firefox returns `data:application/octet-stream;base64,YWJjZGVmZw==`,
+ IE returns `data:;base64,YWJjZGVmZw==`, Chrome returns `data:<mediatype depending on extension of entry name>;base64,YWJjZGVmZw==`. */
+ expect(evt.target.result).toBeDataUrl();
+
+ // The atob function it is completely ignored during mobilespec execution, besides the returned object: evt
+ // it is encoded and the atob function is aimed to decode a string. Even with btoa (encode) the function it gets stucked
+ // because of the Unicode characters that contains the fileData object.
+ // Issue reported at JIRA with all the details: CB-7095
+
+ // expect(evt.target.result.slice(23)).toBe(atob(fileDataAsBinaryString.slice(10, -3)));
+
+ done();
+ }, 10, -3);
+ });
+ it('file.spec.93 should read sliced file properly, readAsBinaryString', function (done) {
+ if (isIE) {
+ /* `readAsBinaryString` function is not supported by IE and has not the stub. */
+ pending();
+ }
+
+ runReaderTest('readAsBinaryString', true, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(evt.target.result).toBe(fileDataAsBinaryString.slice(-10, -5));
+ done();
+ }, -10, -5);
+ });
+ it('file.spec.94 should read sliced file properly, readAsArrayBuffer', function (done) {
+ // Skip test if ArrayBuffers are not supported (e.g.: Android 2.3).
+ if (typeof window.ArrayBuffer === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ }
+ runReaderTest('readAsArrayBuffer', true, done, null, function (evt, fileData, fileDataAsBinaryString) {
+ expect(arrayBufferEqualsString(evt.target.result, fileDataAsBinaryString.slice(0, -1))).toBe(true);
+ done();
+ }, 0, -1);
+ });
+ it('file.spec.94.5 should read large file in multiple chunks, readAsArrayBuffer', function (done) {
+ // Skip test if ArrayBuffers are not supported (e.g.: Android 2.3).
+ if (typeof window.ArrayBuffer === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ }
+
+ var largeText = '';
+ for (var i = 0; i < 1000; i++) {
+ largeText += 'Test ' + i + '\n';
+ }
+
+ // Set the chunk size so that the read will take 5 chunks
+ FileReader.READ_CHUNK_SIZE = Math.floor(largeText.length / 4) + 1; // eslint-disable-line no-undef
+
+ var chunkCount = 0;
+ var lastProgressValue = -1;
+ var progressFunc = function (evt) {
+ expect(evt.loaded).toBeDefined();
+ expect(evt.total).toBeDefined();
+
+ expect(evt.total >= largeText.length).toBe(true);
+ expect(evt.total <= largeText.length + 5).toBe(true);
+ expect(evt.loaded > lastProgressValue).toBe(true);
+ expect(evt.loaded <= evt.total).toBe(true);
+
+ lastProgressValue = evt.loaded;
+ chunkCount++;
+ };
+
+ runReaderTest(
+ 'readAsArrayBuffer', true, done, progressFunc,
+ function (evt, fileData, fileDataAsBinaryString) {
+ expect(arrayBufferEqualsString(evt.target.result, fileDataAsBinaryString.slice(0, -1))).toBe(true);
+ expect(lastProgressValue >= largeText.length).toBe(true);
+ expect(lastProgressValue <= largeText.length + 5).toBe(true);
+ if (!isChrome) {
+ // chrome downloads it in one chunk -.-
+ expect(chunkCount).toBe(5);
+ }
+ done();
+ },
+ 0, -1, largeText);
+ });
+ it('file.spec.94.6 should read large file in multiple chunks, readAsDataURL', function (done) {
+ var largeText = '';
+ for (var i = 0; i < 10; i++) {
+ largeText += 'Test ' + i + '\n';
+ }
+
+ // Set the chunk size so that the read will take 5 chunks
+ FileReader.READ_CHUNK_SIZE = Math.floor(largeText.length / 4) + 1; // eslint-disable-line no-undef
+
+ var lastProgressValue = 0;
+ var progressFunc = function (evt) {
+ expect(evt.total).toBeDefined();
+ expect(evt.total).toEqual(largeText.length);
+
+ expect(evt.loaded).toBeDefined();
+ expect(evt.loaded).toBeGreaterThan(lastProgressValue);
+ expect(evt.loaded).toBeLessThan(evt.total + 1);
+
+ lastProgressValue = evt.loaded;
+ };
+
+ runReaderTest('readAsDataURL', false, done, progressFunc,
+ function (evt, fileData, fileDataAsBinaryString) {
+ expect(function () {
+ // Cut off data uri prefix
+ var base64Data = evt.target.result.substring(evt.target.result.indexOf(',') + 1);
+ expect(window.atob(base64Data)).toEqual(fileData);
+ }).not.toThrow();
+
+ expect(lastProgressValue).toEqual(largeText.length);
+ done();
+ },
+ undefined, undefined, largeText);
+ });
+ });
+ // Read method
+ describe('FileWriter', function () {
+ it('file.spec.95 should have correct methods', function (done) {
+ // retrieve a FileWriter object
+ var fileName = 'writer.methods';
+ // FileWriter
+ root.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ expect(writer).toBeDefined();
+ expect(typeof writer.write).toBe('function');
+ expect(typeof writer.seek).toBe('function');
+ expect(typeof writer.truncate).toBe('function');
+ expect(typeof writer.abort).toBe('function');
+ // cleanup
+ deleteFile(fileName, done);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.96 should be able to write and append to file, createWriter', function (done) {
+ var fileName = 'writer.append.createWriter'; // file content
+ var content = 'There is an exception to every rule.'; // for checkin file length
+ var exception = ' Except this one.';
+ var length = content.length;
+ // create file, then write and append to it
+ createFile(fileName, function (fileEntry) {
+ // writes initial file content
+ fileEntry.createWriter(function (writer) {
+ // Verifiers declaration
+ function verifier (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // Append some more data
+ writer.onwriteend = secondVerifier;
+ length += exception.length;
+ writer.seek(writer.length);
+ writer.write(exception);
+ }
+ function secondVerifier (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onloadend = thirdVerifier;
+ reader.onerror = failed.bind(null, done, 'reader.onerror - Error reading file: ' + fileName);
+ fileEntry.file(function (f) { reader.readAsText(f); });
+ }
+ function thirdVerifier (evt) {
+ expect(evt.target.result).toBe(content + exception);
+ // cleanup
+ deleteFile(fileName, done);
+ }
+
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.97 should be able to write and append to file, File object', function (done) {
+ var fileName = 'writer.append.File'; // file content
+ var content = 'There is an exception to every rule.'; // for checkin file length
+ var exception = ' Except this one.';
+ var length = content.length;
+ root.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifiers declaration
+ function verifier () {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // Append some more data
+ writer.onwriteend = secondVerifier;
+ length += exception.length;
+ writer.seek(writer.length);
+ writer.write(exception);
+ }
+ function secondVerifier () {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onloadend = thirdVerifier;
+ reader.onerror = failed.bind(null, done, 'reader.onerror - Error reading file: ' + fileName);
+ fileEntry.file(function (f) { reader.readAsText(f); });
+ }
+ function thirdVerifier (evt) {
+ expect(evt.target.result).toBe(content + exception);
+ // cleanup
+ deleteFile(fileName, done);
+ }
+
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'root.getFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.98 should be able to seek to the middle of the file and write more data than file.length', function (done) {
+ var fileName = 'writer.seek.write'; // file content
+ var content = 'This is our sentence.'; // for checking file length
+ var exception = 'newer sentence.';
+ var length = content.length;
+ // create file, then write and append to it
+ createFile(fileName, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifiers declaration
+ function verifier (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // Append some more data
+ writer.onwriteend = secondVerifier;
+ length = 12 + exception.length;
+ writer.seek(12);
+ writer.write(exception);
+ }
+ function secondVerifier (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onloadend = thirdVerifier;
+ reader.onerror = failed.bind(null, done, 'reader.onerror - Error reading file: ' + fileName);
+ fileEntry.file(function (f) { reader.readAsText(f); });
+ }
+ function thirdVerifier (evt) {
+ expect(evt.target.result).toBe(content.substr(0, 12) + exception);
+ // cleanup
+ deleteFile(fileName, done);
+ }
+
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.99 should be able to seek to the middle of the file and write less data than file.length', function (done) {
+ if (isBrowser) {
+ /* Browser (re)writes as follows: "This is our sentence." -> "This is new.sentence.",
+ i.e. the length is not being changed from content.length and writer length will be equal 21 */
+ pending();
+ }
+
+ var fileName = 'writer.seek.write2'; // file content
+ var content = 'This is our sentence.'; // for checking file length
+ var exception = 'new.';
+ var length = content.length;
+ // create file, then write and append to it
+ createFile(fileName, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifiers declaration
+ function verifier (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // Append some more data
+ writer.onwriteend = secondVerifier;
+ length = 8 + exception.length;
+ writer.seek(8);
+ writer.write(exception);
+ }
+ function secondVerifier (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onloadend = thirdVerifier;
+ reader.onerror = failed.bind(null, done, 'reader.onerror - Error reading file: ' + fileName);
+ fileEntry.file(function (f) { reader.readAsText(f); });
+ }
+ function thirdVerifier (evt) {
+ expect(evt.target.result).toBe(content.substr(0, 8) + exception);
+ // cleanup
+ deleteFile(fileName, done);
+ }
+
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.100 should be able to write XML data', function (done) {
+ var fileName = 'writer.xml'; // file content
+ var content = '<?xml version="1.0" encoding="UTF-8"?>\n<test prop="ack">\nData\n</test>\n'; // for testing file length
+ var length = content.length;
+ // creates file, then write XML data
+ createFile(fileName, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifier content
+ var verifier = function (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // cleanup
+ deleteFile(fileName, done);
+ };
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.101 should be able to write JSON data', function (done) {
+ var fileName = 'writer.json'; // file content
+ var content = '{ "name": "Guy Incognito", "email": "here@there.com" }'; // for testing file length
+ var length = content.length;
+ // creates file, then write JSON content
+ createFile(fileName, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifier declaration
+ var verifier = function (evt) {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // cleanup
+ deleteFile(fileName, done);
+ };
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.102 should be able to seek', function (done) {
+ var fileName = 'writer.seek'; // file content
+ var content = 'There is an exception to every rule. Except this one.'; // for testing file length
+ var length = content.length;
+ // creates file, then write JSON content
+ createFile(fileName, function (fileEntry) {
+ // writes file content and tests writer.seek
+ fileEntry.createWriter(function (writer) {
+ // Verifier declaration
+ var verifier = function () {
+ expect(writer.position).toBe(length);
+ writer.seek(-5);
+ expect(writer.position).toBe(length - 5);
+ writer.seek(length + 100);
+ expect(writer.position).toBe(length);
+ writer.seek(10);
+ expect(writer.position).toBe(10);
+ // cleanup
+ deleteFile(fileName, done);
+ };
+ // Write process
+ writer.onwriteend = verifier;
+ writer.seek(-100);
+ expect(writer.position).toBe(0);
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.103 should be able to truncate', function (done) {
+ if (isIndexedDBShim) {
+ /* `abort` and `truncate` functions are not supported (Firefox, IE) */
+ pending();
+ }
+
+ var fileName = 'writer.truncate';
+ var content = 'There is an exception to every rule. Except this one.';
+ // creates file, writes to it, then truncates it
+ createFile(fileName, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifier declaration
+ var verifier = function () {
+ expect(writer.length).toBe(36);
+ expect(writer.position).toBe(36);
+ // cleanup
+ deleteFile(fileName, done);
+ };
+ // Write process
+ writer.onwriteend = function () {
+ // Truncate process after write
+ writer.onwriteend = verifier;
+ writer.truncate(36);
+ };
+ writer.write(content);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.104 should be able to write binary data from an ArrayBuffer', function (done) {
+ // Skip test if ArrayBuffers are not supported (e.g.: Android 2.3).
+ if (typeof window.ArrayBuffer === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ return;
+ }
+ var fileName = 'bufferwriter.bin'; // file content
+ var data = new ArrayBuffer(32);
+ var dataView = new Int8Array(data); // for verifying file length
+ var length = 32;
+ for (var i = 0; i < dataView.length; i++) {
+ dataView[i] = i;
+ }
+ // creates file, then write content
+ createFile(fileName, function (fileEntry) {
+ // writes file content
+ fileEntry.createWriter(function (writer) {
+ // Verifier declaration
+ var verifier = function () {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // cleanup
+ deleteFile(fileName, done);
+ };
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(data);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.105 should be able to write binary data from a Blob', function (done) {
+ // Skip test if Blobs are not supported (e.g.: Android 2.3).
+ if ((typeof window.Blob === 'undefined' && typeof window.WebKitBlobBuilder === 'undefined') || typeof window.ArrayBuffer === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ return;
+ }
+ var fileName = 'blobwriter.bin'; // file content
+ var data = new ArrayBuffer(32);
+ var dataView = new Int8Array(data);
+ var blob; // for verifying file length
+ var length = 32;
+ for (var i = 0; i < dataView.length; i++) {
+ dataView[i] = i;
+ }
+ try {
+ // Mobile Safari: Use Blob constructor
+ blob = new Blob([data], { // eslint-disable-line no-undef
+ 'type': 'application/octet-stream'
+ });
+ } catch (e) {
+ if (window.WebKitBlobBuilder) {
+ // Android Browser: Use deprecated BlobBuilder
+ var builder = new WebKitBlobBuilder();
+ builder.append(data);
+ blob = builder.getBlob('application/octet-stream');
+ } else {
+ // We have no way defined to create a Blob, so fail
+ fail();
+ }
+ }
+ if (typeof blob !== 'undefined') {
+ // creates file, then write content
+ createFile(fileName, function (fileEntry) {
+ fileEntry.createWriter(function (writer) {
+ // Verifier declaration
+ var verifier = function () {
+ expect(writer.length).toBe(length);
+ expect(writer.position).toBe(length);
+ // cleanup
+ deleteFile(fileName, done);
+ };
+ // Write process
+ writer.onwriteend = verifier;
+ writer.write(blob);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ }
+ });
+ it('file.spec.106 should be able to write a File to a FileWriter', function (done) {
+ var dummyFileName = 'dummy.txt';
+ var outputFileName = 'verify.txt';
+ var dummyFileText = 'This text should be written to two files';
+ var verifier = function (outputFileWriter) {
+ expect(outputFileWriter.length).toBe(dummyFileText.length);
+ expect(outputFileWriter.position).toBe(dummyFileText.length);
+ deleteFile(outputFileName, done);
+ };
+ var writeFile = function (fileName, fileData, win) {
+ var theWriter;
+ var write_file = function (fileEntry) {
+ // writes file content to new file
+ fileEntry.createWriter(function (writer) {
+ theWriter = writer;
+ writer.onwriteend = function (ev) {
+ if (typeof fileData.length !== 'undefined') {
+ expect(theWriter.length).toBe(fileData.length);
+ expect(theWriter.position).toBe(fileData.length);
+ }
+ win(theWriter);
+ };
+ writer.onerror = failed.bind(null, done, 'writer.onerror - Error writing content on file: ' + fileName);
+ writer.write(fileData);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ };
+ createFile(fileName, write_file, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ };
+ var openFile = function (fileName, callback) {
+ root.getFile(fileName, {
+ create: false
+ }, function (fileEntry) {
+ fileEntry.file(callback, failed.bind(null, done, 'fileEntry.file - Error reading file using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'root.getFile - Error getting file: ' + fileName));
+ };
+ writeFile(dummyFileName, dummyFileText, function (dummyFileWriter) {
+ openFile(dummyFileName, function (file) {
+ writeFile(outputFileName, file, verifier);
+ });
+ });
+ });
+ it('file.spec.107 should be able to write a sliced File to a FileWriter', function (done) {
+ var dummyFileName = 'dummy2.txt';
+ var outputFileName = 'verify2.txt';
+ var dummyFileText = 'This text should be written to two files';
+ var verifier = function (outputFileWriter) {
+ expect(outputFileWriter.length).toBe(10);
+ expect(outputFileWriter.position).toBe(10);
+ deleteFile(outputFileName, done);
+ };
+ var writeFile = function (fileName, fileData, win) {
+ var theWriter;
+ var write_file = function (fileEntry) {
+ // writes file content to new file
+ fileEntry.createWriter(function (writer) {
+ theWriter = writer;
+ writer.onwriteend = function (ev) {
+ if (typeof fileData.length !== 'undefined') {
+ expect(theWriter.length).toBe(fileData.length);
+ expect(theWriter.position).toBe(fileData.length);
+ }
+ win(theWriter);
+ };
+ writer.onerror = failed.bind(null, done, 'writer.onerror - Error writing content on file: ' + fileName);
+ writer.write(fileData);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ };
+ createFile(fileName, write_file, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ };
+ var openFile = function (fileName, callback) {
+ root.getFile(fileName, {
+ create: false
+ }, function (fileEntry) {
+ fileEntry.file(callback, failed.bind(null, done, 'fileEntry.file - Error reading file using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'root.getFile - Error getting file: ' + fileName));
+ };
+ writeFile(dummyFileName, dummyFileText, function (dummyFileWriter) {
+ openFile(dummyFileName, function (file) {
+ writeFile(outputFileName, file.slice(10, 20), verifier);
+ });
+ });
+ });
+ it('file.spec.108 should be able to write binary data from a File', function (done) {
+ // Skip test if Blobs are not supported (e.g.: Android 2.3).
+ if (typeof window.Blob === 'undefined' && typeof window.WebKitBlobBuilder === 'undefined') {
+ expect(true).toFailWithMessage('Platform does not supported this feature');
+ done();
+ }
+ var dummyFileName = 'blobwriter.bin';
+ var outputFileName = 'verify.bin'; // file content
+ var data = new ArrayBuffer(32);
+ var dataView = new Int8Array(data);
+ var blob; // for verifying file length
+ var length = 32;
+ var verifier = function (outputFileWriter) {
+ expect(outputFileWriter.length).toBe(length);
+ expect(outputFileWriter.position).toBe(length);
+ // cleanup
+ deleteFile(outputFileName);
+ done();
+ };
+ var writeFile = function (fileName, fileData, win) {
+ var theWriter;
+ var write_file = function (fileEntry) {
+ // writes file content to new file
+ fileEntry.createWriter(function (writer) {
+ theWriter = writer;
+ writer.onwriteend = function (ev) {
+ if (typeof fileData.length !== 'undefined') {
+ expect(theWriter.length).toBe(fileData.length);
+ expect(theWriter.position).toBe(fileData.length);
+ }
+ win(theWriter);
+ };
+ writer.onerror = failed.bind(null, done, 'writer.onerror - Error writing content on file: ' + fileName);
+ writer.write(fileData);
+ }, failed.bind(null, done, 'fileEntry.createWriter - Error creating writer using fileEntry: ' + fileEntry.name));
+ };
+ createFile(fileName, write_file, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ };
+ var openFile = function (fileName, callback) {
+ root.getFile(fileName, {
+ create: false
+ }, function (fileEntry) {
+ fileEntry.file(callback, failed.bind(null, done, 'fileEntry.file - Error reading file using fileEntry: ' + fileEntry.name));
+ }, failed.bind(null, done, 'root.getFile - Error getting file: ' + fileName));
+ };
+ for (var i = 0; i < dataView.length; i++) {
+ dataView[i] = i;
+ }
+ try {
+ // Mobile Safari: Use Blob constructor
+ blob = new Blob([data], { // eslint-disable-line no-undef
+ 'type': 'application/octet-stream'
+ });
+ } catch (e) {
+ if (window.WebKitBlobBuilder) {
+ // Android Browser: Use deprecated BlobBuilder
+ var builder = new WebKitBlobBuilder();
+ builder.append(data);
+ blob = builder.getBlob('application/octet-stream');
+ } else {
+ // We have no way defined to create a Blob, so fail
+ fail();
+ }
+ }
+ if (typeof blob !== 'undefined') {
+ // creates file, then write content
+ writeFile(dummyFileName, blob, function (dummyFileWriter) {
+ openFile(dummyFileName, function (file) {
+ writeFile(outputFileName, file, verifier);
+ });
+ });
+ }
+ });
+ });
+ // FileWritter
+ describe('Backwards compatibility', function () {
+ /* These specs exist to test that the File plugin can still recognize file:///
+ * URLs, and can resolve them to FileEntry and DirectoryEntry objects.
+ * They rely on an undocumented interface to File which provides absolute file
+ * paths, which are not used internally anymore.
+ * If that interface is not present, then these tests will silently succeed.
+ */
+ it('file.spec.109 should be able to resolve a file:/// URL', function (done) {
+ var localFilename = 'file.txt';
+ var originalEntry;
+ root.getFile(localFilename, {
+ create: true
+ }, function (entry) {
+ originalEntry = entry;
+ /* 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 ft.download() to make sure that previously-stored
+ * paths are still valid.
+ */
+ cordova.exec(function (localPath) { // eslint-disable-line no-undef
+ window.resolveLocalFileSystemURL('file://' + encodeURI(localPath), function (fileEntry) {
+ expect(fileEntry.toURL()).toEqual(originalEntry.toURL());
+ // cleanup
+ deleteFile(localFilename);
+ done();
+ }, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving URI: file://' + encodeURI(localPath)));
+ }, done, 'File', '_getLocalFilesystemPath', [entry.toURL()]);
+ }, failed.bind(null, done, 'root.getFile - Error creating file: ' + localFilename));
+ });
+ });
+ // Backwards Compatibility
+ describe('Parent References', function () {
+ /* These specs verify that paths with parent references i("..") in them
+ * work correctly, and do not cause the application to crash.
+ */
+ it('file.spec.110 should not throw exception resolving parent refefences', function (done) {
+ /* This is a direct copy of file.spec.9, with the filename changed, * as reported in CB-5721.
+ */
+ var fileName = 'resolve.file.uri';
+ var dirName = 'resolve.dir.uri';
+ // create a new file entry
+ createDirectory(dirName, function () {
+ createFile(dirName + '/../' + fileName, function (entry) {
+ // lookup file system entry
+ window.resolveLocalFileSystemURL(entry.toURL(), function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.name).toCanonicallyMatch(fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'window.resolveLocalFileSystemURL - Error resolving URI: ' + entry.toURL()));
+ }, failed.bind(null, done, 'createFile - Error creating file: ../' + fileName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory: ' + dirName));
+ });
+ it('file.spec.111 should not traverse above above the root directory', function (done) {
+ var fileName = 'traverse.file.uri';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ // lookup file system entry
+ root.getFile('../' + fileName, {
+ create: false
+ }, function (fileEntry) {
+ // Note: we expect this to still resolve, as the correct behaviour is to ignore the ../, not to fail out.
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.name).toBe(fileName);
+ expect(fileEntry.fullPath).toCanonicallyMatch(root.fullPath + '/' + fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'root.getFile - Error getting file: ../' + fileName));
+ }, failed.bind(null, done, 'createFile - Error creating file: ../' + fileName));
+ });
+ it('file.spec.112 should traverse above above the current directory', function (done) {
+ var fileName = 'traverse2.file.uri';
+ var dirName = 'traverse2.subdir';
+ // create a new directory and a file entry
+ createFile(fileName, function () {
+ createDirectory(dirName, function (entry) {
+ // lookup file system entry
+ entry.getFile('../' + fileName, {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.name).toBe(fileName);
+ expect(fileEntry.fullPath).toCanonicallyMatch('/' + fileName);
+ // cleanup
+ deleteEntry(fileName, function () {
+ deleteEntry(dirName, done);
+ });
+ }, failed.bind(null, done, 'entry.getFile - Error getting file: ' + fileName + ' recently created above: ' + dirName));
+ }, failed.bind(null, done, 'createDirectory - Error creating directory: ' + dirName));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.113 getFile: get Entry should error for missing file above root directory', function (done) {
+ var fileName = '../missing.file';
+ // create:false, exclusive:false, file does not exist
+ root.getFile(fileName, {
+ create: false
+ }, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not locate nonexistent file: ' + fileName), function (error) {
+ expect(error).toBeDefined();
+ if (isChrome) {
+ expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef
+ } else {
+ expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef
+ }
+ done();
+ });
+ });
+ });
+ // Parent References
+ describe('toNativeURL interface', function () {
+ /* These specs verify that FileEntries have a toNativeURL method
+ * which appears to be sane.
+ */
+ var pathExpect = cordova.platformId === 'windowsphone' ? '//nativ' : 'file://'; // eslint-disable-line no-undef
+ if (isChrome) {
+ pathExpect = 'filesystem:http://';
+ }
+ it('file.spec.114 fileEntry should have a toNativeURL method', function (done) {
+ var fileName = 'native.file.uri';
+ if (isWindows) {
+ var rootPath = root.fullPath;
+ pathExpect = rootPath.substr(0, rootPath.indexOf(':'));
+ }
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ expect(entry.toNativeURL).toBeDefined();
+ expect(entry.name).toCanonicallyMatch(fileName);
+ expect(typeof entry.toNativeURL).toBe('function');
+ var nativeURL = entry.toNativeURL();
+ expect(typeof nativeURL).toBe('string');
+ expect(nativeURL.substring(0, pathExpect.length)).toEqual(pathExpect);
+ expect(nativeURL.substring(nativeURL.length - fileName.length)).toEqual(fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.115 DirectoryReader should return entries with toNativeURL method', function (done) {
+ var dirName = 'nativeEntries.dir';
+ var fileName = 'nativeEntries.file';
+ var directory;
+ var checkEntries = function (entries) {
+ expect(entries).toBeDefined();
+ expect(entries instanceof Array).toBe(true);
+ expect(entries.length).toBe(1);
+ expect(entries[0].toNativeURL).toBeDefined();
+ expect(typeof entries[0].toNativeURL).toBe('function');
+ var nativeURL = entries[0].toNativeURL();
+ expect(typeof nativeURL).toBe('string');
+ expect(nativeURL.substring(0, pathExpect.length)).toEqual(pathExpect);
+ expect(nativeURL.substring(nativeURL.length - fileName.length)).toEqual(fileName);
+ // cleanup
+ directory.removeRecursively(function () {}, null);
+ done();
+ };
+ // create a new file entry
+ root.getDirectory(dirName, {
+ create: true
+ }, function (dir) {
+ directory = dir;
+ directory.getFile(fileName, {
+ create: true
+ }, function (fileEntry) {
+ var reader = directory.createReader();
+ reader.readEntries(checkEntries, failed.bind(null, done, 'reader.readEntries - Error reading entries from directory: ' + dirName));
+ }, failed.bind(null, done, 'directory.getFile - Error creating file: ' + fileName));
+ }, failed.bind(null, done, 'root.getDirectory - Error creating directory: ' + dirName));
+ });
+ it('file.spec.116 resolveLocalFileSystemURL should return entries with toNativeURL method', function (done) {
+ var fileName = 'native.resolve.uri';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ resolveLocalFileSystemURL(entry.toURL(), function (entry) { // eslint-disable-line no-undef
+ expect(entry.toNativeURL).toBeDefined();
+ expect(entry.name).toCanonicallyMatch(fileName);
+ expect(typeof entry.toNativeURL).toBe('function');
+ var nativeURL = entry.toNativeURL();
+ expect(typeof nativeURL).toBe('string');
+ expect(nativeURL.substring(0, pathExpect.length)).toEqual(pathExpect);
+ expect(nativeURL.substring(nativeURL.length - fileName.length)).toEqual(fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL - Error resolving file URL: ' + entry.toURL()));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ });
+ // toNativeURL interface
+ describe('resolveLocalFileSystemURL on file://', function () {
+ /* These specs verify that window.resolveLocalFileSystemURL works correctly on file:// URLs
+ */
+ it('file.spec.117 should not resolve native URLs outside of FS roots', function (done) {
+ // lookup file system entry
+ window.resolveLocalFileSystemURL('file:///this.is.an.invalid.url', succeed.bind(null, done, 'window.resolveLocalFileSystemURL - Unexpected success callback, it should not resolve invalid URL: file:///this.is.an.invalid.url'), function (error) {
+ expect(error).toBeDefined();
+ done();
+ });
+ });
+ it('file.spec.118 should not resolve native URLs outside of FS roots', function (done) {
+ // lookup file system entry
+ window.resolveLocalFileSystemURL('file://localhost/this.is.an.invalid.url', succeed.bind(null, done, 'window.resolveLocalFileSystemURL - Unexpected success callback, it should not resolve invalid URL: file://localhost/this.is.an.invalid.url'), function (error) {
+ expect(error).toBeDefined();
+ done();
+ });
+ });
+ it('file.spec.119 should not resolve invalid native URLs', function (done) {
+ // lookup file system entry
+ window.resolveLocalFileSystemURL('file://localhost', succeed.bind(null, done, 'window.resolveLocalFileSystemURL - Unexpected success callback, it should not resolve invalid URL: file://localhost'), function (error) {
+ expect(error).toBeDefined();
+ done();
+ });
+ });
+ it('file.spec.120 should not resolve invalid native URLs with query strings', function (done) {
+ // lookup file system entry
+ window.resolveLocalFileSystemURL('file://localhost?test/test', succeed.bind(null, done, 'window.resolveLocalFileSystemURL - Unexpected success callback, it should not resolve invalid URL: file://localhost?test/test'), function (error) {
+ expect(error).toBeDefined();
+ done();
+ });
+ });
+ it('file.spec.121 should resolve native URLs returned by API', function (done) {
+ var fileName = 'native.resolve.uri1';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ /* eslint-disable no-undef */
+ resolveLocalFileSystemURL(entry.toNativeURL(), function (fileEntry) {
+ expect(fileEntry.fullPath).toCanonicallyMatch(root.fullPath + '/' + fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL - Error resolving file URL: ' + entry.toNativeURL()));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.122 should resolve native URLs returned by API with localhost', function (done) {
+ var fileName = 'native.resolve.uri2';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ var url = entry.toNativeURL();
+ url = url.replace('///', '//localhost/');
+ resolveLocalFileSystemURL(url, function (fileEntry) {
+ expect(fileEntry.fullPath).toCanonicallyMatch(root.fullPath + '/' + fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL - Error resolving file URL: ' + url));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.123 should resolve native URLs returned by API with query string', function (done) {
+ var fileName = 'native.resolve.uri3';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ var url = entry.toNativeURL();
+ url = url + '?test/test';
+ resolveLocalFileSystemURL(url, function (fileEntry) {
+ expect(fileEntry.fullPath).toCanonicallyMatch(root.fullPath + '/' + fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL - Error resolving file URL: ' + url));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ it('file.spec.124 should resolve native URLs returned by API with localhost and query string', function (done) {
+ var fileName = 'native.resolve.uri4';
+ // create a new file entry
+ createFile(fileName, function (entry) {
+ var url = entry.toNativeURL();
+ url = url.replace('///', '//localhost/') + '?test/test';
+ resolveLocalFileSystemURL(url, function (fileEntry) {
+ /* eslint-enable no-undef */
+ expect(fileEntry.fullPath).toCanonicallyMatch(root.fullPath + '/' + fileName);
+ // cleanup
+ deleteEntry(fileName, done);
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL - Error resolving file URL: ' + url));
+ }, failed.bind(null, done, 'createFile - Error creating file: ' + fileName));
+ });
+ });
+ // resolveLocalFileSystemURL on file://
+ describe('cross-file-system copy and move', function () {
+ /* These specs verify that Entry.copyTo and Entry.moveTo work correctly
+ * when crossing filesystem boundaries.
+ */
+ it('file.spec.125 copyTo: temporary -> persistent', function (done) {
+ var file1 = 'entry.copy.file1a';
+ var file2 = 'entry.copy.file2a';
+ var sourceEntry;
+ var fullPath = joinURL(root.fullPath, file2);
+ var validateFile = function (entry) {
+ // a bit redundant since copy returned this entry already
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(file2);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ expect(entry.filesystem).toBeDefined();
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Persistent');
+ } else {
+ expect(entry.filesystem.name).toEqual('persistent');
+ }
+ // cleanup
+ deleteEntry(entry.name);
+ deleteEntry(sourceEntry.name, done);
+ };
+ var createSourceAndTransfer = function () {
+ temp_root.getFile(file1, {
+ create: true
+ }, function (entry) {
+ expect(entry.filesystem).toBeDefined();
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Temporary');
+ } else {
+ expect(entry.filesystem.name).toEqual('temporary');
+ }
+ sourceEntry = entry;
+ // Save for later cleanup
+ entry.copyTo(persistent_root, file2, validateFile, failed.bind(null, done, 'entry.copyTo - Error copying file: ' + file1 + ' to PERSISTENT root as: ' + file2));
+ }, failed.bind(null, done, 'temp_root.getFile - Error creating file: ' + file1 + 'at TEMPORAL root'));
+ };
+ // Delete any existing file to start things off
+ persistent_root.getFile(file2, {}, function (entry) {
+ entry.remove(createSourceAndTransfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+ }, createSourceAndTransfer);
+ });
+ it('file.spec.126 copyTo: persistent -> temporary', function (done) {
+ var file1 = 'entry.copy.file1b';
+ var file2 = 'entry.copy.file2b';
+ var sourceEntry;
+ var fullPath = joinURL(temp_root.fullPath, file2);
+ var validateFile = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(file2);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Temporary');
+ } else {
+ expect(entry.filesystem.name).toEqual('temporary');
+ }
+ // cleanup
+ deleteEntry(entry.name);
+ deleteEntry(sourceEntry.name, done);
+ };
+ var createSourceAndTransfer = function () {
+ persistent_root.getFile(file1, {
+ create: true
+ }, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.filesystem).toBeDefined();
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Persistent');
+ } else {
+ expect(entry.filesystem.name).toEqual('persistent');
+ }
+ sourceEntry = entry;
+ // Save for later cleanup
+ entry.copyTo(temp_root, file2, validateFile, failed.bind(null, done, 'entry.copyTo - Error copying file: ' + file1 + ' to TEMPORAL root as: ' + file2));
+ }, failed.bind(null, done, 'persistent_root.getFile - Error creating file: ' + file1 + 'at PERSISTENT root'));
+ };
+ // Delete any existing file to start things off
+ temp_root.getFile(file2, {}, function (entry) {
+ entry.remove(createSourceAndTransfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+ }, createSourceAndTransfer);
+ });
+ it('file.spec.127 moveTo: temporary -> persistent', function (done) {
+ var file1 = 'entry.copy.file1a';
+ var file2 = 'entry.copy.file2a';
+ var sourceEntry;
+ var fullPath = joinURL(root.fullPath, file2);
+ var validateFile = function (entry) {
+ // a bit redundant since copy returned this entry already
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(file2);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ expect(entry.filesystem).toBeDefined();
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Persistent');
+ } else {
+ expect(entry.filesystem.name).toEqual('persistent');
+ }
+ // cleanup
+ deleteEntry(entry.name);
+ deleteEntry(sourceEntry.name, done);
+ };
+ var createSourceAndTransfer = function () {
+ temp_root.getFile(file1, {
+ create: true
+ }, function (entry) {
+ expect(entry.filesystem).toBeDefined();
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Temporary');
+ } else {
+ expect(entry.filesystem.name).toEqual('temporary');
+ }
+ sourceEntry = entry;
+ // Save for later cleanup
+ entry.moveTo(persistent_root, file2, validateFile, failed.bind(null, done, 'entry.moveTo - Error moving file: ' + file1 + ' to PERSISTENT root as: ' + file2));
+ }, failed.bind(null, done, 'temp_root.getFile - Error creating file: ' + file1 + 'at TEMPORAL root'));
+ };
+ // Delete any existing file to start things off
+ persistent_root.getFile(file2, {}, function (entry) {
+ entry.remove(createSourceAndTransfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+ }, createSourceAndTransfer);
+ });
+ it('file.spec.128 moveTo: persistent -> temporary', function (done) {
+ var file1 = 'entry.copy.file1b';
+ var file2 = 'entry.copy.file2b';
+ var sourceEntry;
+ var fullPath = joinURL(temp_root.fullPath, file2);
+ var validateFile = function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(file2);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Temporary');
+ } else {
+ expect(entry.filesystem.name).toEqual('temporary');
+ }
+ // cleanup
+ deleteEntry(entry.name);
+ deleteEntry(sourceEntry.name, done);
+ };
+ var createSourceAndTransfer = function () {
+ persistent_root.getFile(file1, {
+ create: true
+ }, function (entry) {
+ expect(entry).toBeDefined();
+ expect(entry.filesystem).toBeDefined();
+ if (isChrome) {
+ expect(entry.filesystem.name).toContain('Persistent');
+ } else {
+ expect(entry.filesystem.name).toEqual('persistent');
+ }
+ sourceEntry = entry;
+ // Save for later cleanup
+ entry.moveTo(temp_root, file2, validateFile, failed.bind(null, done, 'entry.moveTo - Error moving file: ' + file1 + ' to TEMPORAL root as: ' + file2));
+ }, failed.bind(null, done, 'persistent_root.getFile - Error creating file: ' + file1 + 'at PERSISTENT root'));
+ };
+ // Delete any existing file to start things off
+ temp_root.getFile(file2, {}, function (entry) {
+ entry.remove(createSourceAndTransfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+ }, createSourceAndTransfer);
+ });
+ it('file.spec.129 cordova.file.*Directory are set', function () {
+ var expectedPaths = ['applicationDirectory', 'applicationStorageDirectory', 'dataDirectory', 'cacheDirectory'];
+ /* eslint-disable no-undef */
+ if (cordova.platformId === 'android' || cordova.platformId === 'amazon-fireos') {
+ if (cordova.file.externalApplicationStorageDirectory !== null) {
+ // https://issues.apache.org/jira/browse/CB-10411
+ // If external storage can't be mounted, the cordova.file.external* properties are null.
+ expectedPaths.push('externalApplicationStorageDirectory', 'externalRootDirectory', 'externalCacheDirectory', 'externalDataDirectory');
+ }
+ } else if (cordova.platformId === 'blackberry10') {
+ expectedPaths.push('externalRootDirectory', 'sharedDirectory');
+ } else if (cordova.platformId === 'ios') {
+ expectedPaths.push('syncedDataDirectory', 'documentsDirectory', 'tempDirectory');
+ } else if (cordova.platformId === 'osx') {
+ expectedPaths.push('documentsDirectory', 'tempDirectory', 'rootDirectory');
+ } else {
+ console.log('Skipping test due on unsupported platform.');
+ return;
+ }
+ for (var i = 0; i < expectedPaths.length; ++i) {
+ expect(typeof cordova.file[expectedPaths[i]]).toBe('string');
+ expect(cordova.file[expectedPaths[i]]).toMatch(/\/$/, 'Path should end with a slash');
+ }
+ });
+ });
+ describe('resolveLocalFileSystemURL on cdvfile://', function () {
+ it('file.spec.147 should be able to resolve cdvfile applicationDirectory fs root', function (done) {
+ var cdvfileApplicationDirectoryFsRootName;
+ if (cordova.platformId === 'android') {
+ cdvfileApplicationDirectoryFsRootName = 'assets';
+ } else if (cordova.platformId === 'ios') {
+ cdvfileApplicationDirectoryFsRootName = 'bundle';
+ } else {
+ pending();
+ }
+
+ resolveLocalFileSystemURL('cdvfile://localhost/' + cdvfileApplicationDirectoryFsRootName + '/', function (applicationDirectoryRoot) {
+ expect(applicationDirectoryRoot.isFile).toBe(false);
+ expect(applicationDirectoryRoot.isDirectory).toBe(true);
+ expect(applicationDirectoryRoot.name).toCanonicallyMatch('');
+ expect(applicationDirectoryRoot.fullPath).toCanonicallyMatch('/');
+ expect(applicationDirectoryRoot.filesystem.name).toEqual(cdvfileApplicationDirectoryFsRootName);
+
+ // Requires HelloCordova www assets, <allow-navigation href="cdvfile:*" /> in config.xml or
+ // cdvfile: in CSP and <access origin="cdvfile://*" /> in config.xml
+ resolveLocalFileSystemURL('cdvfile://localhost/' + cdvfileApplicationDirectoryFsRootName + '/www/img/logo.png', function (entry) {
+ /* eslint-enable no-undef */
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch('logo.png');
+ expect(entry.fullPath).toCanonicallyMatch('/www/img/logo.png');
+ expect(entry.filesystem.name).toEqual(cdvfileApplicationDirectoryFsRootName);
+
+ var img = new Image(); // eslint-disable-line no-undef
+ img.onerror = function (err) {
+ expect(err).not.toBeDefined();
+ done();
+ };
+ img.onload = function () {
+ done();
+ };
+ img.src = entry.toInternalURL();
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for cdvfile applicationDirectory'));
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for cdvfile applicationDirectory'));
+ });
+ });
+ // cross-file-system copy and move
+ describe('IndexedDB-based impl', function () {
+ it('file.spec.131 Nested file or nested directory should be removed when removing a parent directory', function (done) {
+ var parentDirName = 'deletedDir131';
+ var nestedDirName = 'nestedDir131';
+ var nestedFileName = 'nestedFile131.txt';
+
+ createDirectory(parentDirName, function (parent) {
+ parent.getDirectory(nestedDirName, { create: true }, function () {
+ parent.getFile(nestedFileName, { create: true }, function () {
+ parent.removeRecursively(function () {
+ root.getDirectory(parentDirName, { create: false }, failed.bind(this, done, 'root.getDirectory - unexpected success callback : ' + parentDirName), function () {
+ parent.getFile(nestedFileName, { create: false }, failed.bind(this, done, 'getFile - unexpected success callback : ' + nestedFileName), function () {
+ parent.getDirectory(nestedDirName, { create: false }, failed.bind(this, done, 'getDirectory - unexpected success callback : ' + nestedDirName), done);
+ });
+ });
+ }, failed.bind(this, done, 'removeRecursively - Error removing directory : ' + parentDirName));
+ }, failed.bind(this, done, 'getFile - Error creating file : ' + nestedFileName));
+ }, failed.bind(this, done, 'getDirectory - Error creating directory : ' + nestedDirName));
+ }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + parentDirName));
+ });
+ it('file.spec.132 Entry should be created succesfully when using relative paths if its parent directory exists', function (done) {
+ /* Directory entries have to be created successively.
+ For example, the call `fs.root.getDirectory('dir1/dir2', {create:true}, successCallback, errorCallback)`
+ will fail if dir1 did not exist. */
+ var parentName = 'parentName132';
+ var nestedName = 'nestedName132';
+ var path = parentName + '/' + nestedName;
+
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(nestedName);
+ expect(directory.fullPath).toCanonicallyMatch('/' + path + '/');
+ deleteEntry(directory.name);
+ deleteEntry(parentName, done);
+ };
+
+ createDirectory(parentName, function () {
+ root.getDirectory(parentName + '/' + nestedName, {create: true}, win,
+ failed.bind(this, done, 'root.getDirectory - Error getting directory : ' + path));
+ }, failed.bind(this, done, 'root.getDirectory - Error getting directory : ' + parentName));
+ });
+ it('file.spec.133 A file being removed should not affect another file with name being a prefix of the removed file name.', function (done) {
+
+ // Names include special symbols so that we check the IndexedDB range used
+ var deletedFileName = 'deletedFile.0';
+ var secondFileName = 'deletedFile.0.1';
+
+ var win = function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(true);
+ expect(fileEntry.isDirectory).toBe(false);
+ expect(fileEntry.name).toCanonicallyMatch(secondFileName);
+ deleteEntry(fileEntry.name, done);
+ };
+
+ createFile(deletedFileName, function (deletedFile) {
+ createFile(secondFileName, function () {
+ deletedFile.remove(function () {
+ root.getFile(deletedFileName, {create: false}, failed.bind(this, done, 'getFile - unexpected success callback getting deleted file : ' + deletedFileName), function () {
+ root.getFile(secondFileName, {create: false}, win, failed.bind(this, done, 'getFile - Error getting file after deleting deletedFile : ' + secondFileName));
+ });
+ }, failed.bind(this, done, 'remove - Error removing file : ' + deletedFileName));
+ }, failed.bind(this, done, 'getFile - Error creating file : ' + secondFileName));
+ }, failed.bind(this, done, 'getFile - Error creating file : ' + deletedFileName));
+ });
+ it('file.spec.134 A directory being removed should not affect another directory with name being a prefix of the removed directory name.', function (done) {
+
+ // Names include special symbols so that we check the IndexedDB range used
+ var deletedDirName = 'deletedDir.0';
+ var secondDirName = 'deletedDir.0.1';
+
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(secondDirName);
+ deleteEntry(directory.name, done);
+ };
+
+ createDirectory(deletedDirName, function (deletedDir) {
+ createDirectory(secondDirName, function () {
+ deletedDir.remove(function () {
+ root.getDirectory(deletedDirName, {create: false}, failed.bind(this, done, 'getDirectory - unexpected success callback getting deleted directory : ' + deletedDirName), function () {
+ root.getDirectory(secondDirName, {create: false}, win, failed.bind(this, done, 'getDirectory - Error getting directory after deleting deletedDirectory : ' + secondDirName));
+ });
+ }, failed.bind(this, done, 'remove - Error removing directory : ' + deletedDirName));
+ }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + secondDirName));
+ }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + deletedDirName));
+ });
+ it('file.spec.135 Deletion of a child directory should not affect the parent directory.', function (done) {
+
+ var parentName = 'parentName135';
+ var childName = 'childName135';
+
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(parentName);
+ deleteEntry(directory.name, done);
+ };
+
+ createDirectory(parentName, function (parent) {
+ parent.getDirectory(childName, {create: true}, function (child) {
+ child.removeRecursively(function () {
+ root.getDirectory(parentName, {create: false}, win, failed.bind(this, done, 'root.getDirectory - Error getting parent directory : ' + parentName));
+ },
+ failed.bind(this, done, 'getDirectory - Error removing directory : ' + childName));
+ }, failed.bind(this, done, 'getDirectory - Error creating directory : ' + childName));
+ }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + parentName));
+ });
+ it('file.spec.136 Paths should support Unicode symbols.', function (done) {
+
+ var dirName = '文件插件';
+
+ var win = function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.name).toCanonicallyMatch(dirName);
+ deleteEntry(directory.name, done);
+ };
+
+ createDirectory(dirName, function () {
+ root.getDirectory(dirName, {create: false}, win,
+ failed.bind(this, done, 'root.getDirectory - Error getting directory : ' + dirName));
+ }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + dirName));
+ });
+ });
+ // Content and Asset URLs
+ if (cordova.platformId === 'android') { // eslint-disable-line no-undef
+ describe('content: URLs', function () {
+ // Warning: Default HelloWorld www directory structure is required for these tests (www/index.html at least)
+ function testContentCopy (src, done) {
+ var file2 = 'entry.copy.file2b';
+ var fullPath = joinURL(temp_root.fullPath, file2);
+ var validateFile = function (entry) {
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(file2);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ expect(entry.filesystem.name).toEqual('temporary');
+ // cleanup
+ deleteEntry(entry.name, done);
+ };
+ var transfer = function () {
+ resolveLocalFileSystemURL(src, function (entry) { // eslint-disable-line no-undef
+ expect(entry).toBeDefined();
+ expect(entry.filesystem.name).toEqual('content');
+ entry.copyTo(temp_root, file2, validateFile, failed.bind(null, done, 'entry.copyTo - Error copying file: ' + entry.toURL() + ' to TEMPORAL root as: ' + file2));
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for content provider'));
+ };
+ // Delete any existing file to start things off
+ temp_root.getFile(file2, {}, function (entry) {
+ entry.remove(transfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+ }, transfer);
+ }
+ it('file.spec.138 copyTo: content', function (done) {
+ testContentCopy('content://org.apache.cordova.file.testprovider/www/index.html', done);
+ });
+ it('file.spec.139 copyTo: content /w space and query', function (done) {
+ testContentCopy('content://org.apache.cordova.file.testprovider/?name=foo%20bar&realPath=%2Fwww%2Findex.html', done);
+ });
+ it('file.spec.140 delete: content should fail', function (done) {
+ resolveLocalFileSystemURL('content://org.apache.cordova.file.testprovider/www/index.html', function (entry) { // eslint-disable-line no-undef
+ entry.remove(failed.bind(null, done, 'expected delete to fail'), done);
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for content provider'));
+ });
+ });
+
+ // these tests ensure that you can read and copy from android_asset folder
+ // for details see https://issues.apache.org/jira/browse/CB-6428
+ // and https://mail-archives.apache.org/mod_mbox/cordova-dev/201508.mbox/%3C782154441.8406572.1440182722528.JavaMail.yahoo%40mail.yahoo.com%3E
+ describe('asset: URLs', function () {
+ it('file.spec.141 filePaths.applicationStorage', function () {
+ expect(cordova.file.applicationDirectory).toEqual('file:///android_asset/'); // eslint-disable-line no-undef
+ }, MEDIUM_TIMEOUT);
+ it('file.spec.142 assets should be enumerable', function (done) {
+ resolveLocalFileSystemURL('file:///android_asset/www/fixtures/asset-test', function (entry) { // eslint-disable-line no-undef
+ var reader = entry.createReader();
+ reader.readEntries(function (entries) {
+ expect(entries.length).not.toBe(0);
+ done();
+ }, failed.bind(null, done, 'reader.readEntries - Error during reading of entries from assets directory'));
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for assets'));
+ }, MEDIUM_TIMEOUT);
+ it('file.spec.145 asset subdirectories should be obtainable', function (done) {
+ resolveLocalFileSystemURL('file:///android_asset/www/fixtures', function (entry) { // eslint-disable-line no-undef
+ entry.getDirectory('asset-test', { create: false }, function (subDir) {
+ expect(subDir).toBeDefined();
+ expect(subDir.isFile).toBe(false);
+ expect(subDir.isDirectory).toBe(true);
+ expect(subDir.name).toCanonicallyMatch('asset-test');
+ done();
+ }, failed.bind(null, done, 'entry.getDirectory - Error getting asset subdirectory'));
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for assets'));
+ }, MEDIUM_TIMEOUT);
+ it('file.spec.146 asset files should be readable', function (done) {
+ resolveLocalFileSystemURL('file:///android_asset/www/fixtures/asset-test/asset-test.txt', function (entry) { // eslint-disable-line no-undef
+ expect(entry.isFile).toBe(true);
+ entry.file(function (file) {
+ expect(file).toBeDefined();
+ var reader = new FileReader(); // eslint-disable-line no-undef
+ reader.onerror = failed.bind(null, done, 'reader.readAsText - Error reading asset text file');
+ reader.onloadend = function () {
+ expect(this.result).toBeDefined();
+ expect(this.result.length).not.toBe(0);
+ done();
+ };
+ reader.readAsText(file);
+ }, failed.bind(null, done, 'entry.file - Error reading asset file'));
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for assets'));
+ }, MEDIUM_TIMEOUT);
+ it('file.spec.143 copyTo: asset -> temporary', function (done) {
+ var file2 = 'entry.copy.file2b';
+ var fullPath = joinURL(temp_root.fullPath, file2);
+ var validateFile = function (entry) {
+ expect(entry.isFile).toBe(true);
+ expect(entry.isDirectory).toBe(false);
+ expect(entry.name).toCanonicallyMatch(file2);
+ expect(entry.fullPath).toCanonicallyMatch(fullPath);
+ expect(entry.filesystem.name).toEqual('temporary');
+ // cleanup
+ deleteEntry(entry.name, done);
+ };
+ var transfer = function () {
+ resolveLocalFileSystemURL('file:///android_asset/www/index.html', function (entry) { // eslint-disable-line no-undef
+ expect(entry.filesystem.name).toEqual('assets');
+ entry.copyTo(temp_root, file2, validateFile, failed.bind(null, done, 'entry.copyTo - Error copying file: ' + entry.toURL() + ' to TEMPORAL root as: ' + file2));
+ }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for assets'));
+ };
+ // Delete any existing file to start things off
+ temp_root.getFile(file2, {}, function (entry) {
+ entry.remove(transfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+ }, transfer);
+ }, MEDIUM_TIMEOUT);
+ });
+ it('file.spec.144 copyTo: asset directory', function (done) {
+ var srcUrl = 'file:///android_asset/www/fixtures/asset-test';
+ var dstDir = 'entry.copy.dstDir';
+ var dstPath = joinURL(root.fullPath, dstDir);
+ // create a new directory entry to kick off it
+ deleteEntry(dstDir, function () {
+ resolveLocalFileSystemURL(srcUrl, function (directory) { // eslint-disable-line no-undef
+ directory.copyTo(root, dstDir, function (directory) {
+ expect(directory).toBeDefined();
+ expect(directory.isFile).toBe(false);
+ expect(directory.isDirectory).toBe(true);
+ expect(directory.fullPath).toCanonicallyMatch(dstPath);
+ expect(directory.name).toCanonicallyMatch(dstDir);
+ root.getDirectory(dstDir, {
+ create: false
+ }, function (dirEntry) {
+ expect(dirEntry).toBeDefined();
+ expect(dirEntry.isFile).toBe(false);
+ expect(dirEntry.isDirectory).toBe(true);
+ expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+ expect(dirEntry.name).toCanonicallyMatch(dstDir);
+ dirEntry.getFile('asset-test.txt', {
+ create: false
+ }, function (fileEntry) {
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry.isFile).toBe(true);
+ // cleanup
+ deleteEntry(dstDir, done);
+ }, failed.bind(null, done, 'dirEntry.getFile - Error getting subfile'));
+ }, failed.bind(null, done, 'root.getDirectory - Error getting copied directory'));
+ }, failed.bind(null, done, 'directory.copyTo - Error copying directory'));
+ }, failed.bind(null, done, 'resolving src dir'));
+ }, failed.bind(null, done, 'deleteEntry - Error removing directory : ' + dstDir));
+ }, MEDIUM_TIMEOUT);
+ }
+ });
+
+};
+//* *****************************************************************************************
+//* **************************************Manual Tests***************************************
+//* *****************************************************************************************
+
+exports.defineManualTests = function (contentEl, createActionButton) {
+
+ function resolveFs (fsname) {
+ var fsURL = 'cdvfile://localhost/' + fsname + '/';
+ logMessage('Resolving URL: ' + fsURL);
+ /* eslint-disable no-undef */
+ resolveLocalFileSystemURL(fsURL, function (entry) {
+ logMessage('Success', 'green');
+ logMessage(entry.toURL(), 'blue');
+ logMessage(entry.toInternalURL(), 'blue');
+ logMessage('Resolving URL: ' + entry.toURL());
+ resolveLocalFileSystemURL(entry.toURL(), function (entry2) {
+ logMessage('Success', 'green');
+ logMessage(entry2.toURL(), 'blue');
+ logMessage(entry2.toInternalURL(), 'blue');
+ }, logError('resolveLocalFileSystemURL'));
+ }, logError('resolveLocalFileSystemURL'));
+ }
+
+ function testPrivateURL () {
+ requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) {
+ logMessage('Temporary root is at ' + fileSystem.root.toNativeURL());
+ fileSystem.root.getFile('testfile', {
+ create: true
+ }, function (entry) {
+ logMessage('Temporary file is at ' + entry.toNativeURL());
+ if (entry.toNativeURL().substring(0, 12) === 'file:///var/') {
+ logMessage('File starts with /var/, trying /private/var');
+ var newURL = 'file://localhost/private/var/' + entry.toNativeURL().substring(12) + '?and=another_thing';
+ // var newURL = entry.toNativeURL();
+ logMessage(newURL, 'blue');
+ resolveLocalFileSystemURL(newURL, function (newEntry) {
+ logMessage('Successfully resolved.', 'green');
+ logMessage(newEntry.toURL(), 'blue');
+ logMessage(newEntry.toNativeURL(), 'blue');
+ }, logError('resolveLocalFileSystemURL'));
+ }
+ }, logError('getFile'));
+ }, logError('requestFileSystem'));
+ }
+
+ function resolveFsContactImage () {
+ navigator.contacts.pickContact(function (contact) {
+ var logBox = document.getElementById('logContactBox');
+ logBox.innerHTML = '';
+ var resolveResult = document.createElement('p');
+ if (contact.photos) {
+ var photoURL = contact.photos[0].value;
+ resolveLocalFileSystemURL(photoURL, function (entry) {
+ /* eslint-enable no-undef */
+ var contactImage = document.createElement('img');
+ var contactLabelImage = document.createElement('p');
+ contactLabelImage.innerHTML = 'Result contact image';
+ contactImage.setAttribute('src', entry.toURL());
+ resolveResult.innerHTML = 'Success resolve\n' + entry.toURL();
+ logBox.appendChild(contactLabelImage);
+ logBox.appendChild(contactImage);
+ logBox.appendChild(resolveResult);
+ },
+ function (err) {
+ console.log('resolve error' + err);
+ });
+ } else {
+ resolveResult.innerHTML = 'Contact has no photos';
+ logBox.appendChild(resolveResult);
+ }
+ },
+ function (err) {
+ console.log('contact pick error' + err);
+ });
+ }
+
+ function clearLog () {
+ var log = document.getElementById('info');
+ log.innerHTML = '';
+ }
+
+ function logMessage (message, color) {
+ var log = document.getElementById('info');
+ var logLine = document.createElement('div');
+ if (color) {
+ logLine.style.color = color;
+ }
+ logLine.innerHTML = message;
+ log.appendChild(logLine);
+ }
+
+ function logError (serviceName) {
+ return function (err) {
+ logMessage('ERROR: ' + serviceName + ' ' + JSON.stringify(err), 'red');
+ };
+ }
+
+ var fsRoots = {
+ 'ios': 'library,library-nosync,documents,documents-nosync,cache,bundle,root,private',
+ 'osx': 'library,library-nosync,documents,documents-nosync,cache,bundle,root,private',
+ 'android': 'files,files-external,documents,sdcard,cache,cache-external,assets,root',
+ 'amazon-fireos': 'files,files-external,documents,sdcard,cache,cache-external,root',
+ 'windows': 'temporary,persistent'
+ };
+
+ // Add title and align to content
+ var div = document.createElement('h2');
+ div.appendChild(document.createTextNode('File Systems'));
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+
+ div = document.createElement('h3');
+ div.appendChild(document.createTextNode('Results are displayed in yellow status box below with expected results noted under that'));
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+
+ div = document.createElement('div');
+ div.setAttribute('id', 'button');
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+ /* eslint-disable no-undef */
+ if (fsRoots.hasOwnProperty(cordova.platformId)) {
+ (fsRoots[cordova.platformId].split(',')).forEach(function (fs) {
+ if (cordova.platformId === 'ios' && fs === 'private') {
+ /* eslint-enable no-undef */
+ createActionButton('Test private URL (iOS)', function () {
+ clearLog();
+ testPrivateURL();
+ }, 'button');
+ } else {
+ createActionButton(fs, function () {
+ clearLog();
+ resolveFs(fs);
+ }, 'button');
+ }
+ });
+ }
+
+ div = document.createElement('div');
+ div.setAttribute('id', 'info');
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+
+ div = document.createElement('h3');
+ div.appendChild(document.createTextNode('For each test above, file or directory should be successfully found. ' +
+ 'Status box should say Resolving URL was Success. The first URL resolved is the internal URL. ' +
+ 'The second URL resolved is the absolute URL. Blue URLs must match.'));
+ contentEl.appendChild(div);
+
+ div = document.createElement('h3');
+ div.appendChild(document.createTextNode('For Test private URL (iOS), the private URL (first blue URL in status box) ' +
+ 'should be successfully resolved. Status box should say Successfully resolved. Both blue URLs below ' +
+ 'that should match.'));
+ contentEl.appendChild(div);
+
+ div = document.createElement('h2');
+ div.appendChild(document.createTextNode('Resolving content urls'));
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+
+ div = document.createElement('div');
+ div.setAttribute('id', 'contactButton');
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+
+ div = document.createElement('div');
+ div.setAttribute('id', 'logContactBox');
+ div.setAttribute('align', 'center');
+ contentEl.appendChild(div);
+
+ createActionButton('show-contact-image', function () {
+ resolveFsContactImage();
+ }, 'contactButton');
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/tests/www/fixtures/asset-test/asset-test.txt b/StoneIsland/plugins/cordova-plugin-file/tests/www/fixtures/asset-test/asset-test.txt
new file mode 100644
index 00000000..2ee2df0e
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/tests/www/fixtures/asset-test/asset-test.txt
@@ -0,0 +1 @@
+This file is here for testing purposes \ No newline at end of file
diff --git a/StoneIsland/plugins/cordova-plugin-file/types/index.d.ts b/StoneIsland/plugins/cordova-plugin-file/types/index.d.ts
new file mode 100644
index 00000000..469d8621
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/types/index.d.ts
@@ -0,0 +1,378 @@
+// Type definitions for Apache Cordova File System plugin
+// Project: https://github.com/apache/cordova-plugin-file
+// Definitions by: Microsoft Open Technologies Inc <http://msopentech.com>
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+//
+// Copyright (c) Microsoft Open Technologies, Inc.
+// Licensed under the MIT license.
+
+interface Window {
+ /**
+ * Requests a filesystem in which to store application data.
+ * @param type Whether the filesystem requested should be persistent, as defined above. Use one of TEMPORARY or PERSISTENT.
+ * @param size This is an indicator of how much storage space, in bytes, the application expects to need.
+ * @param successCallback The callback that is called when the user agent provides a filesystem.
+ * @param errorCallback A callback that is called when errors happen, or when the request to obtain the filesystem is denied.
+ */
+ requestFileSystem(
+ type: LocalFileSystem,
+ size: number,
+ successCallback: (fileSystem: FileSystem) => void,
+ errorCallback?: (fileError: FileError) => void): void;
+ /**
+ * Look up file system Entry referred to by local URL.
+ * @param string url URL referring to a local file or directory
+ * @param successCallback invoked with Entry object corresponding to URL
+ * @param errorCallback invoked if error occurs retrieving file system entry
+ */
+ resolveLocalFileSystemURL(url: string,
+ successCallback: (entry: Entry) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Look up file system Entry referred to by local URI.
+ * @param string uri URI referring to a local file or directory
+ * @param successCallback invoked with Entry object corresponding to URI
+ * @param errorCallback invoked if error occurs retrieving file system entry
+ */
+ resolveLocalFileSystemURI(uri: string,
+ successCallback: (entry: Entry) => void,
+ errorCallback?: (error: FileError) => void): void;
+ TEMPORARY: number;
+ PERSISTENT: number;
+}
+
+/** This interface represents a file system. */
+interface FileSystem {
+ /* The name of the file system, unique across the list of exposed file systems. */
+ name: string;
+ /** The root directory of the file system. */
+ root: DirectoryEntry;
+}
+
+/**
+ * An abstract interface representing entries in a file system,
+ * each of which may be a File or DirectoryEntry.
+ */
+interface Entry {
+ /** Entry is a file. */
+ isFile: boolean;
+ /** Entry is a directory. */
+ isDirectory: boolean;
+ /** The name of the entry, excluding the path leading to it. */
+ name: string;
+ /** The full absolute path from the root to the entry. */
+ fullPath: string;
+ /** The file system on which the entry resides. */
+ filesystem: FileSystem;
+ nativeURL: string;
+ /**
+ * Look up metadata about this entry.
+ * @param successCallback A callback that is called with the time of the last modification.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ getMetadata(
+ successCallback: (metadata: Metadata) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Move an entry to a different location on the file system. It is an error to try to:
+ * move a directory inside itself or to any child at any depth;move an entry into its parent if a name different from its current one isn't provided;
+ * move a file to a path occupied by a directory;
+ * move a directory to a path occupied by a file;
+ * move any element to a path occupied by a directory which is not empty.
+ * A move of a file on top of an existing file must attempt to delete and replace that file.
+ * A move of a directory on top of an existing empty directory must attempt to delete and replace that directory.
+ * @param parent The directory to which to move the entry.
+ * @param newName The new name of the entry. Defaults to the Entry's current name if unspecified.
+ * @param successCallback A callback that is called with the Entry for the new location.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ moveTo(parent: DirectoryEntry,
+ newName?: string,
+ successCallback?: (entry: Entry) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Copy an entry to a different location on the file system. It is an error to try to:
+ * copy a directory inside itself or to any child at any depth;
+ * copy an entry into its parent if a name different from its current one isn't provided;
+ * copy a file to a path occupied by a directory;
+ * copy a directory to a path occupied by a file;
+ * copy any element to a path occupied by a directory which is not empty.
+ * A copy of a file on top of an existing file must attempt to delete and replace that file.
+ * A copy of a directory on top of an existing empty directory must attempt to delete and replace that directory.
+ * Directory copies are always recursive--that is, they copy all contents of the directory.
+ * @param parent The directory to which to move the entry.
+ * @param newName The new name of the entry. Defaults to the Entry's current name if unspecified.
+ * @param successCallback A callback that is called with the Entry for the new object.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ copyTo(parent: DirectoryEntry,
+ newName?: string,
+ successCallback?: (entry: Entry) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Returns a URL that can be used as the src attribute of a <video> or <audio> tag.
+ * If that is not possible, construct a cdvfile:// URL.
+ * @return string URL
+ */
+ toURL(): string;
+ /**
+ * Return a URL that can be passed across the bridge to identify this entry.
+ * @return string URL that can be passed across the bridge to identify this entry
+ */
+ toInternalURL(): string;
+ /**
+ * Deletes a file or directory. It is an error to attempt to delete a directory that is not empty. It is an error to attempt to delete the root directory of a filesystem.
+ * @param successCallback A callback that is called on success.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ remove(successCallback: () => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Look up the parent DirectoryEntry containing this Entry. If this Entry is the root of its filesystem, its parent is itself.
+ * @param successCallback A callback that is called with the time of the last modification.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ getParent(successCallback: (entry: Entry) => void,
+ errorCallback?: (error: FileError) => void): void;
+}
+
+/** This interface supplies information about the state of a file or directory. */
+interface Metadata {
+ /** This is the time at which the file or directory was last modified. */
+ modificationTime: Date;
+ /** The size of the file, in bytes. This must return 0 for directories. */
+ size: number;
+}
+
+/** This interface represents a directory on a file system. */
+interface DirectoryEntry extends Entry {
+ /**
+ * Creates a new DirectoryReader to read Entries from this Directory.
+ */
+ createReader(): DirectoryReader;
+ /**
+ * Creates or looks up a file.
+ * @param path Either an absolute path or a relative path from this DirectoryEntry
+ * to the file to be looked up or created.
+ * It is an error to attempt to create a file whose immediate parent does not yet exist.
+ * @param options If create and exclusive are both true, and the path already exists, getFile must fail.
+ * If create is true, the path doesn't exist, and no other error occurs, getFile must create it as a zero-length file and return a corresponding FileEntry.
+ * If create is not true and the path doesn't exist, getFile must fail.
+ * If create is not true and the path exists, but is a directory, getFile must fail.
+ * Otherwise, if no other error occurs, getFile must return a FileEntry corresponding to path.
+ * @param successCallback A callback that is called to return the File selected or created.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ getFile(path: string, options?: Flags,
+ successCallback?: (entry: FileEntry) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Creates or looks up a directory.
+ * @param path Either an absolute path or a relative path from this DirectoryEntry
+ * to the directory to be looked up or created.
+ * It is an error to attempt to create a directory whose immediate parent does not yet exist.
+ * @param options If create and exclusive are both true and the path already exists, getDirectory must fail.
+ * If create is true, the path doesn't exist, and no other error occurs, getDirectory must create and return a corresponding DirectoryEntry.
+ * If create is not true and the path doesn't exist, getDirectory must fail.
+ * If create is not true and the path exists, but is a file, getDirectory must fail.
+ * Otherwise, if no other error occurs, getDirectory must return a DirectoryEntry corresponding to path.
+ * @param successCallback A callback that is called to return the Directory selected or created.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ getDirectory(path: string, options?: Flags,
+ successCallback?: (entry: DirectoryEntry) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Deletes a directory and all of its contents, if any. In the event of an error (e.g. trying
+ * to delete a directory that contains a file that cannot be removed), some of the contents
+ * of the directory may be deleted. It is an error to attempt to delete the root directory of a filesystem.
+ * @param successCallback A callback that is called on success.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ removeRecursively(successCallback: () => void,
+ errorCallback?: (error: FileError) => void): void;
+}
+
+/**
+ * This dictionary is used to supply arguments to methods
+ * that look up or create files or directories.
+ */
+interface Flags {
+ /** Used to indicate that the user wants to create a file or directory if it was not previously there. */
+ create?: boolean;
+ /** By itself, exclusive must have no effect. Used with create, it must cause getFile and getDirectory to fail if the target path already exists. */
+ exclusive?: boolean;
+}
+
+/**
+ * This interface lets a user list files and directories in a directory. If there are
+ * no additions to or deletions from a directory between the first and last call to
+ * readEntries, and no errors occur, then:
+ * A series of calls to readEntries must return each entry in the directory exactly once.
+ * Once all entries have been returned, the next call to readEntries must produce an empty array.
+ * If not all entries have been returned, the array produced by readEntries must not be empty.
+ * The entries produced by readEntries must not include the directory itself ["."] or its parent [".."].
+ */
+interface DirectoryReader {
+ /**
+ * Read the next block of entries from this directory.
+ * @param successCallback Called once per successful call to readEntries to deliver the next
+ * previously-unreported set of Entries in the associated Directory.
+ * If all Entries have already been returned from previous invocations
+ * of readEntries, successCallback must be called with a zero-length array as an argument.
+ * @param errorCallback A callback indicating that there was an error reading from the Directory.
+ */
+ readEntries(
+ successCallback: (entries: Entry[]) => void,
+ errorCallback?: (error: FileError) => void): void;
+}
+
+/** This interface represents a file on a file system. */
+interface FileEntry extends Entry {
+ /**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ * @param successCallback A callback that is called with the new FileWriter.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ createWriter(successCallback: (
+ writer: FileWriter) => void,
+ errorCallback?: (error: FileError) => void): void;
+ /**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ * @param successCallback A callback that is called with the File.
+ * @param errorCallback A callback that is called when errors happen.
+ */
+ file(successCallback: (file: File) => void,
+ errorCallback?: (error: FileError) => void): void;
+}
+
+/**
+ * This interface provides methods to monitor the asynchronous writing of blobs
+ * to disk using progress events and event handler attributes.
+ */
+interface FileSaver extends EventTarget {
+ /** Terminate file operation */
+ abort(): void;
+ /**
+ * The FileSaver object can be in one of 3 states. The readyState attribute, on getting,
+ * must return the current state, which must be one of the following values:
+ * INIT
+ * WRITING
+ * DONE
+ */
+ readyState: number;
+ /** Handler for writestart events. */
+ onwritestart: (event: ProgressEvent) => void;
+ /** Handler for progress events. */
+ onprogress: (event: ProgressEvent) => void;
+ /** Handler for write events. */
+ onwrite: (event: ProgressEvent) => void;
+ /** Handler for abort events. */
+ onabort: (event: ProgressEvent) => void;
+ /** Handler for error events. */
+ onerror: (event: ProgressEvent) => void;
+ /** Handler for writeend events. */
+ onwriteend: (event: ProgressEvent) => void;
+ /** The last error that occurred on the FileSaver. */
+ error: Error;
+}
+
+/**
+ * This interface expands on the FileSaver interface to allow for multiple write
+ * actions, rather than just saving a single Blob.
+ */
+interface FileWriter extends FileSaver {
+ /**
+ * The byte offset at which the next write to the file will occur. This always less or equal than length.
+ * A newly-created FileWriter will have position set to 0.
+ */
+ position: number;
+ /**
+ * The length of the file. If the user does not have read access to the file,
+ * this will be the highest byte offset at which the user has written.
+ */
+ length: number;
+ /**
+ * Write the supplied data to the file at position.
+ * @param {Blob|string} data The blob to write.
+ */
+ write(data: Blob|string): void;
+ /**
+ * The file position at which the next write will occur.
+ * @param offset If nonnegative, an absolute byte offset into the file.
+ * If negative, an offset back from the end of the file.
+ */
+ seek(offset: number): void;
+ /**
+ * Changes the length of the file to that specified. If shortening the file, data beyond the new length
+ * will be discarded. If extending the file, the existing data will be zero-padded up to the new length.
+ * @param size The size to which the length of the file is to be adjusted, measured in bytes.
+ */
+ truncate(size: number): void;
+}
+
+/* FileWriter states */
+declare var FileWriter: {
+ INIT: number;
+ WRITING: number;
+ DONE: number
+};
+
+interface FileError {
+ /** Error code */
+ code: number;
+}
+
+declare var FileError: {
+ new (code: number): FileError;
+ NOT_FOUND_ERR: number;
+ SECURITY_ERR: number;
+ ABORT_ERR: number;
+ NOT_READABLE_ERR: number;
+ ENCODING_ERR: number;
+ NO_MODIFICATION_ALLOWED_ERR: number;
+ INVALID_STATE_ERR: number;
+ SYNTAX_ERR: number;
+ INVALID_MODIFICATION_ERR: number;
+ QUOTA_EXCEEDED_ERR: number;
+ TYPE_MISMATCH_ERR: number;
+ PATH_EXISTS_ERR: number;
+};
+
+/*
+ * Constants defined in fileSystemPaths
+ */
+interface Cordova {
+ file: {
+ /* Read-only directory where the application is installed. */
+ applicationDirectory: string;
+ /* Root of app's private writable storage */
+ applicationStorageDirectory: string;
+ /* Where to put app-specific data files. */
+ dataDirectory: string;
+ /* Cached files that should survive app restarts. Apps should not rely on the OS to delete files in here. */
+ cacheDirectory: string;
+ /* Android: the application space on external storage. */
+ externalApplicationStorageDirectory: string;
+ /* Android: Where to put app-specific data files on external storage. */
+ externalDataDirectory: string;
+ /* Android: the application cache on external storage. */
+ externalCacheDirectory: string;
+ /* Android: the external storage (SD card) root. */
+ externalRootDirectory: string;
+ /* iOS: Temp directory that the OS can clear at will. */
+ tempDirectory: string;
+ /* iOS: Holds app-specific files that should be synced (e.g. to iCloud). */
+ syncedDataDirectory: string;
+ /* iOS: Files private to the app, but that are meaningful to other applciations (e.g. Office files) */
+ documentsDirectory: string;
+ /* BlackBerry10: Files globally available to all apps */
+ sharedDirectory: string
+ }
+}
+
+
+declare enum LocalFileSystem {
+ PERSISTENT=1,
+ TEMPORARY=0
+} \ No newline at end of file
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/DirectoryEntry.js b/StoneIsland/plugins/cordova-plugin-file/www/DirectoryEntry.js
new file mode 100644
index 00000000..d943f6d1
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/DirectoryEntry.js
@@ -0,0 +1,117 @@
+/*
+ *
+ * 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 argscheck = require('cordova/argscheck');
+var utils = require('cordova/utils');
+var exec = require('cordova/exec');
+var Entry = require('./Entry');
+var FileError = require('./FileError');
+var DirectoryReader = require('./DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function (name, fullPath, fileSystem, nativeURL) {
+
+ // add trailing slash if it is missing
+ if ((fullPath) && !/\/$/.test(fullPath)) {
+ fullPath += '/';
+ }
+ // add trailing slash if it is missing
+ if (nativeURL && !/\/$/.test(nativeURL)) {
+ nativeURL += '/';
+ }
+ DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath, fileSystem, nativeURL);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function () {
+ return new DirectoryReader(this.toInternalURL());
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or exclusively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function (path, options, successCallback, errorCallback) {
+ argscheck.checkArgs('sOFF', 'DirectoryEntry.getDirectory', arguments);
+ var fs = this.filesystem;
+ var win = successCallback && function (result) {
+ var entry = new DirectoryEntry(result.name, result.fullPath, fs, result.nativeURL);
+ successCallback(entry);
+ };
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, 'File', 'getDirectory', [this.toInternalURL(), path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function (successCallback, errorCallback) {
+ argscheck.checkArgs('FF', 'DirectoryEntry.removeRecursively', arguments);
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(successCallback, fail, 'File', 'removeRecursively', [this.toInternalURL()]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or exclusively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function (path, options, successCallback, errorCallback) {
+ argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
+ var fs = this.filesystem;
+ var win = successCallback && function (result) {
+ var FileEntry = require('./FileEntry');
+ var entry = new FileEntry(result.name, result.fullPath, fs, result.nativeURL);
+ successCallback(entry);
+ };
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, 'File', 'getFile', [this.toInternalURL(), path, options]);
+};
+
+module.exports = DirectoryEntry;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/DirectoryReader.js b/StoneIsland/plugins/cordova-plugin-file/www/DirectoryReader.js
new file mode 100644
index 00000000..73669533
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/DirectoryReader.js
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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 exec = require('cordova/exec');
+var FileError = require('./FileError');
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader (localURL) {
+ this.localURL = localURL || null;
+ this.hasReadEntries = false;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function (successCallback, errorCallback) {
+ // If we've already read and passed on this directory's entries, return an empty list.
+ if (this.hasReadEntries) {
+ successCallback([]);
+ return;
+ }
+ var reader = this;
+ var win = typeof successCallback !== 'function' ? null : function (result) {
+ var retVal = [];
+ for (var i = 0; i < result.length; i++) {
+ var entry = null;
+ if (result[i].isDirectory) {
+ entry = new (require('./DirectoryEntry'))();
+ } else if (result[i].isFile) {
+ entry = new (require('./FileEntry'))();
+ }
+ entry.isDirectory = result[i].isDirectory;
+ entry.isFile = result[i].isFile;
+ entry.name = result[i].name;
+ entry.fullPath = result[i].fullPath;
+ entry.filesystem = new (require('./FileSystem'))(result[i].filesystemName);
+ entry.nativeURL = result[i].nativeURL;
+ retVal.push(entry);
+ }
+ reader.hasReadEntries = true;
+ successCallback(retVal);
+ };
+ var fail = typeof errorCallback !== 'function' ? null : function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, 'File', 'readEntries', [this.localURL]);
+};
+
+module.exports = DirectoryReader;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/Entry.js b/StoneIsland/plugins/cordova-plugin-file/www/Entry.js
new file mode 100644
index 00000000..8f7a9277
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/Entry.js
@@ -0,0 +1,260 @@
+/*
+ *
+ * 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 argscheck = require('cordova/argscheck');
+var exec = require('cordova/exec');
+var FileError = require('./FileError');
+var Metadata = require('./Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ * {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ * {boolean} true if Entry is a directory (readonly)
+ * @param name
+ * {DOMString} name of the file or directory, excluding the path
+ * leading to it (readonly)
+ * @param fullPath
+ * {DOMString} the absolute full path to the file or directory
+ * (readonly)
+ * @param fileSystem
+ * {FileSystem} the filesystem on which this entry resides
+ * (readonly)
+ * @param nativeURL
+ * {DOMString} an alternate URL which can be used by native
+ * webview controls, for example media players.
+ * (optional, readonly)
+ */
+function Entry (isFile, isDirectory, name, fullPath, fileSystem, nativeURL) {
+ this.isFile = !!isFile;
+ this.isDirectory = !!isDirectory;
+ this.name = name || '';
+ this.fullPath = fullPath || '';
+ this.filesystem = fileSystem || null;
+ this.nativeURL = nativeURL || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ * {Function} is called with a Metadata object
+ * @param errorCallback
+ * {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function (successCallback, errorCallback) {
+ argscheck.checkArgs('FF', 'Entry.getMetadata', arguments);
+ var success = successCallback && function (entryMetadata) {
+ var metadata = new Metadata({
+ size: entryMetadata.size,
+ modificationTime: entryMetadata.lastModifiedDate
+ });
+ successCallback(metadata);
+ };
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(success, fail, 'File', 'getFileMetadata', [this.toInternalURL()]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ * {Function} is called with a Metadata object
+ * @param errorCallback
+ * {Function} is called with a FileError
+ * @param metadataObject
+ * {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function (successCallback, errorCallback, metadataObject) {
+ argscheck.checkArgs('FFO', 'Entry.setMetadata', arguments);
+ exec(successCallback, errorCallback, 'File', 'setMetadata', [this.toInternalURL(), metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ * {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ * {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ * {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ * {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function (parent, newName, successCallback, errorCallback) {
+ argscheck.checkArgs('oSFF', 'Entry.moveTo', arguments);
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ var srcURL = this.toInternalURL();
+ // entry name
+ var name = newName || this.name;
+ var success = function (entry) {
+ if (entry) {
+ if (successCallback) {
+ // create appropriate Entry object
+ var newFSName = entry.filesystemName || (entry.filesystem && entry.filesystem.name);
+ var fs = newFSName ? new FileSystem(newFSName, { name: '', fullPath: '/' }) : new FileSystem(parent.filesystem.name, { name: '', fullPath: '/' }); // eslint-disable-line no-undef
+ var result = (entry.isDirectory) ? new (require('./DirectoryEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL) : new (require('cordova-plugin-file.FileEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL);
+ successCallback(result);
+ }
+ } else {
+ // no Entry object returned
+ if (fail) {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ }
+ };
+
+ // copy
+ exec(success, fail, 'File', 'moveTo', [srcURL, parent.toInternalURL(), name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ * {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ * {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ * {Function} called with the new Entry object
+ * @param errorCallback
+ * {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function (parent, newName, successCallback, errorCallback) {
+ argscheck.checkArgs('oSFF', 'Entry.copyTo', arguments);
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ var srcURL = this.toInternalURL();
+ // entry name
+ var name = newName || this.name;
+ // success callback
+ var success = function (entry) {
+ if (entry) {
+ if (successCallback) {
+ // create appropriate Entry object
+ var newFSName = entry.filesystemName || (entry.filesystem && entry.filesystem.name);
+ var fs = newFSName ? new FileSystem(newFSName, { name: '', fullPath: '/' }) : new FileSystem(parent.filesystem.name, { name: '', fullPath: '/' }); // eslint-disable-line no-undef
+ var result = (entry.isDirectory) ? new (require('./DirectoryEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL) : new (require('cordova-plugin-file.FileEntry'))(entry.name, entry.fullPath, fs, entry.nativeURL);
+ successCallback(result);
+ }
+ } else {
+ // no Entry object returned
+ if (fail) {
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ }
+ };
+
+ // copy
+ exec(success, fail, 'File', 'copyTo', [srcURL, parent.toInternalURL(), name]);
+};
+
+/**
+ * Return a URL that can be passed across the bridge to identify this entry.
+ */
+Entry.prototype.toInternalURL = function () {
+ if (this.filesystem && this.filesystem.__format__) {
+ return this.filesystem.__format__(this.fullPath, this.nativeURL);
+ }
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ * Use a URL that can be used to as the src attribute of a <video> or
+ * <audio> tag. If that is not possible, construct a cdvfile:// URL.
+ */
+Entry.prototype.toURL = function () {
+ if (this.nativeURL) {
+ return this.nativeURL;
+ }
+ // fullPath attribute may contain the full URL in the case that
+ // toInternalURL fails.
+ return this.toInternalURL() || 'file://localhost' + this.fullPath;
+};
+
+/**
+ * Backwards-compatibility: In v1.0.0 - 1.0.2, .toURL would only return a
+ * cdvfile:// URL, and this method was necessary to obtain URLs usable by the
+ * webview.
+ * See CB-6051, CB-6106, CB-6117, CB-6152, CB-6199, CB-6201, CB-6243, CB-6249,
+ * and CB-6300.
+ */
+Entry.prototype.toNativeURL = function () {
+ console.log("DEPRECATED: Update your code to use 'toURL'");
+ return this.toURL();
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function (mimeType) {
+ console.log("DEPRECATED: Update your code to use 'toURL'");
+ return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function (successCallback, errorCallback) {
+ argscheck.checkArgs('FF', 'Entry.remove', arguments);
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(successCallback, fail, 'File', 'remove', [this.toInternalURL()]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function (successCallback, errorCallback) {
+ argscheck.checkArgs('FF', 'Entry.getParent', arguments);
+ var fs = this.filesystem;
+ var win = successCallback && function (result) {
+ var DirectoryEntry = require('./DirectoryEntry');
+ var entry = new DirectoryEntry(result.name, result.fullPath, fs, result.nativeURL);
+ successCallback(entry);
+ };
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, 'File', 'getParent', [this.toInternalURL()]);
+};
+
+module.exports = Entry;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/File.js b/StoneIsland/plugins/cordova-plugin-file/www/File.js
new file mode 100644
index 00000000..30c62025
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/File.js
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function (name, localURL, type, lastModifiedDate, size) {
+ this.name = name || '';
+ this.localURL = localURL || null;
+ this.type = type || null;
+ this.lastModified = lastModifiedDate || null;
+ // For backwards compatibility, store the timestamp in lastModifiedDate as well
+ this.lastModifiedDate = lastModifiedDate || null;
+ this.size = size || 0;
+
+ // These store the absolute start and end for slicing the file.
+ this.start = 0;
+ this.end = this.size;
+};
+
+/**
+ * Returns a "slice" of the file. Since Cordova Files don't contain the actual
+ * content, this really returns a File with adjusted start and end.
+ * Slices of slices are supported.
+ * start {Number} The index at which to start the slice (inclusive).
+ * end {Number} The index at which to end the slice (exclusive).
+ */
+File.prototype.slice = function (start, end) {
+ var size = this.end - this.start;
+ var newStart = 0;
+ var newEnd = size;
+ if (arguments.length) {
+ if (start < 0) {
+ newStart = Math.max(size + start, 0);
+ } else {
+ newStart = Math.min(size, start);
+ }
+ }
+
+ if (arguments.length >= 2) {
+ if (end < 0) {
+ newEnd = Math.max(size + end, 0);
+ } else {
+ newEnd = Math.min(end, size);
+ }
+ }
+
+ var newFile = new File(this.name, this.localURL, this.type, this.lastModified, this.size);
+ newFile.start = this.start + newStart;
+ newFile.end = this.start + newEnd;
+ return newFile;
+};
+
+module.exports = File;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileEntry.js b/StoneIsland/plugins/cordova-plugin-file/www/FileEntry.js
new file mode 100644
index 00000000..8ed230c7
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileEntry.js
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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 utils = require('cordova/utils');
+var exec = require('cordova/exec');
+var Entry = require('./Entry');
+var FileWriter = require('./FileWriter');
+var File = require('./File');
+var FileError = require('./FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function (name, fullPath, fileSystem, nativeURL) {
+ // remove trailing slash if it is present
+ if (fullPath && /\/$/.test(fullPath)) {
+ fullPath = fullPath.substring(0, fullPath.length - 1);
+ }
+ if (nativeURL && /\/$/.test(nativeURL)) {
+ nativeURL = nativeURL.substring(0, nativeURL.length - 1);
+ }
+
+ FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, fileSystem, nativeURL]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function (successCallback, errorCallback) {
+ this.file(function (filePointer) {
+ var writer = new FileWriter(filePointer);
+
+ if (writer.localURL === null || writer.localURL === '') {
+ if (errorCallback) {
+ errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+ }
+ } else {
+ if (successCallback) {
+ successCallback(writer);
+ }
+ }
+ }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function (successCallback, errorCallback) {
+ var localURL = this.toInternalURL();
+ var win = successCallback && function (f) {
+ var file = new File(f.name, localURL, f.type, f.lastModifiedDate, f.size);
+ successCallback(file);
+ };
+ var fail = errorCallback && function (code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, 'File', 'getFileMetadata', [localURL]);
+};
+
+module.exports = FileEntry;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileError.js b/StoneIsland/plugins/cordova-plugin-file/www/FileError.js
new file mode 100644
index 00000000..16bdf31e
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileError.js
@@ -0,0 +1,46 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * FileError
+ */
+function FileError (error) {
+ this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileReader.js b/StoneIsland/plugins/cordova-plugin-file/www/FileReader.js
new file mode 100644
index 00000000..9115f5f5
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileReader.js
@@ -0,0 +1,298 @@
+/*
+ *
+ * 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 exec = require('cordova/exec');
+var modulemapper = require('cordova/modulemapper');
+var utils = require('cordova/utils');
+var FileError = require('./FileError');
+var ProgressEvent = require('./ProgressEvent');
+var origFileReader = modulemapper.getOriginalSymbol(window, 'FileReader');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ * The root directory is the root of the file system.
+ * To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function () {
+ this._readyState = 0;
+ this._error = null;
+ this._result = null;
+ this._progress = null;
+ this._localURL = '';
+ this._realReader = origFileReader ? new origFileReader() : {}; // eslint-disable-line new-cap
+};
+
+/**
+ * Defines the maximum size to read at a time via the native API. The default value is a compromise between
+ * minimizing the overhead of many exec() calls while still reporting progress frequently enough for large files.
+ * (Note attempts to allocate more than a few MB of contiguous memory on the native side are likely to cause
+ * OOM exceptions, while the JS engine seems to have fewer problems managing large strings or ArrayBuffers.)
+ */
+FileReader.READ_CHUNK_SIZE = 256 * 1024;
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+utils.defineGetter(FileReader.prototype, 'readyState', function () {
+ return this._localURL ? this._readyState : this._realReader.readyState;
+});
+
+utils.defineGetter(FileReader.prototype, 'error', function () {
+ return this._localURL ? this._error : this._realReader.error;
+});
+
+utils.defineGetter(FileReader.prototype, 'result', function () {
+ return this._localURL ? this._result : this._realReader.result;
+});
+
+function defineEvent (eventName) {
+ utils.defineGetterSetter(FileReader.prototype, eventName, function () {
+ return this._realReader[eventName] || null;
+ }, function (value) {
+ this._realReader[eventName] = value;
+ });
+}
+defineEvent('onloadstart'); // When the read starts.
+defineEvent('onprogress'); // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
+defineEvent('onload'); // When the read has successfully completed.
+defineEvent('onerror'); // When the read has failed (see errors).
+defineEvent('onloadend'); // When the request has completed (either in success or failure).
+defineEvent('onabort'); // When the read has been aborted. For instance, by invoking the abort() method.
+
+function initRead (reader, file) {
+ // Already loading something
+ if (reader.readyState === FileReader.LOADING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ reader._result = null;
+ reader._error = null;
+ reader._progress = 0;
+ reader._readyState = FileReader.LOADING;
+
+ if (typeof file.localURL === 'string') {
+ reader._localURL = file.localURL;
+ } else {
+ reader._localURL = '';
+ return true;
+ }
+
+ if (reader.onloadstart) {
+ reader.onloadstart(new ProgressEvent('loadstart', {target: reader}));
+ }
+}
+
+/**
+ * Callback used by the following read* functions to handle incremental or final success.
+ * Must be bound to the FileReader's this along with all but the last parameter,
+ * e.g. readSuccessCallback.bind(this, "readAsText", "UTF-8", offset, totalSize, accumulate)
+ * @param readType The name of the read function to call.
+ * @param encoding Text encoding, or null if this is not a text type read.
+ * @param offset Starting offset of the read.
+ * @param totalSize Total number of bytes or chars to read.
+ * @param accumulate A function that takes the callback result and accumulates it in this._result.
+ * @param r Callback result returned by the last read exec() call, or null to begin reading.
+ */
+function readSuccessCallback (readType, encoding, offset, totalSize, accumulate, r) {
+ if (this._readyState === FileReader.DONE) {
+ return;
+ }
+
+ var CHUNK_SIZE = FileReader.READ_CHUNK_SIZE;
+ if (readType === 'readAsDataURL') {
+ // Windows proxy does not support reading file slices as Data URLs
+ // so read the whole file at once.
+ CHUNK_SIZE = cordova.platformId === 'windows' ? totalSize : // eslint-disable-line no-undef
+ // Calculate new chunk size for data URLs to be multiply of 3
+ // Otherwise concatenated base64 chunks won't be valid base64 data
+ FileReader.READ_CHUNK_SIZE - (FileReader.READ_CHUNK_SIZE % 3) + 3;
+ }
+
+ if (typeof r !== 'undefined') {
+ accumulate(r);
+ this._progress = Math.min(this._progress + CHUNK_SIZE, totalSize);
+
+ if (typeof this.onprogress === 'function') {
+ this.onprogress(new ProgressEvent('progress', {loaded: this._progress, total: totalSize}));
+ }
+ }
+
+ if (typeof r === 'undefined' || this._progress < totalSize) {
+ var execArgs = [
+ this._localURL,
+ offset + this._progress,
+ offset + this._progress + Math.min(totalSize - this._progress, CHUNK_SIZE)];
+ if (encoding) {
+ execArgs.splice(1, 0, encoding);
+ }
+ exec(
+ readSuccessCallback.bind(this, readType, encoding, offset, totalSize, accumulate),
+ readFailureCallback.bind(this),
+ 'File', readType, execArgs);
+ } else {
+ this._readyState = FileReader.DONE;
+
+ if (typeof this.onload === 'function') {
+ this.onload(new ProgressEvent('load', {target: this}));
+ }
+
+ if (typeof this.onloadend === 'function') {
+ this.onloadend(new ProgressEvent('loadend', {target: this}));
+ }
+ }
+}
+
+/**
+ * Callback used by the following read* functions to handle errors.
+ * Must be bound to the FileReader's this, e.g. readFailureCallback.bind(this)
+ */
+function readFailureCallback (e) {
+ if (this._readyState === FileReader.DONE) {
+ return;
+ }
+
+ this._readyState = FileReader.DONE;
+ this._result = null;
+ this._error = new FileError(e);
+
+ if (typeof this.onerror === 'function') {
+ this.onerror(new ProgressEvent('error', {target: this}));
+ }
+
+ if (typeof this.onloadend === 'function') {
+ this.onloadend(new ProgressEvent('loadend', {target: this}));
+ }
+}
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function () {
+ if (origFileReader && !this._localURL) {
+ return this._realReader.abort();
+ }
+ this._result = null;
+
+ if (this._readyState === FileReader.DONE || this._readyState === FileReader.EMPTY) {
+ return;
+ }
+
+ this._readyState = FileReader.DONE;
+
+ // If abort callback
+ if (typeof this.onabort === 'function') {
+ this.onabort(new ProgressEvent('abort', {target: this}));
+ }
+ // If load end callback
+ if (typeof this.onloadend === 'function') {
+ this.onloadend(new ProgressEvent('loadend', {target: this}));
+ }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file {File} File object containing file properties
+ * @param encoding [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function (file, encoding) {
+ if (initRead(this, file)) {
+ return this._realReader.readAsText(file, encoding);
+ }
+
+ // Default encoding is UTF-8
+ var enc = encoding || 'UTF-8';
+
+ var totalSize = file.end - file.start;
+ readSuccessCallback.bind(this)('readAsText', enc, file.start, totalSize, function (r) {
+ if (this._progress === 0) {
+ this._result = '';
+ }
+ this._result += r;
+ }.bind(this));
+};
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ * data:[<mediatype>][;base64],<data>
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function (file) {
+ if (initRead(this, file)) {
+ return this._realReader.readAsDataURL(file);
+ }
+
+ var totalSize = file.end - file.start;
+ readSuccessCallback.bind(this)('readAsDataURL', null, file.start, totalSize, function (r) {
+ var commaIndex = r.indexOf(',');
+ if (this._progress === 0) {
+ this._result = r;
+ } else {
+ this._result += r.substring(commaIndex + 1);
+ }
+ }.bind(this));
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function (file) {
+ if (initRead(this, file)) {
+ return this._realReader.readAsBinaryString(file);
+ }
+
+ var totalSize = file.end - file.start;
+ readSuccessCallback.bind(this)('readAsBinaryString', null, file.start, totalSize, function (r) {
+ if (this._progress === 0) {
+ this._result = '';
+ }
+ this._result += r;
+ }.bind(this));
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function (file) {
+ if (initRead(this, file)) {
+ return this._realReader.readAsArrayBuffer(file);
+ }
+
+ var totalSize = file.end - file.start;
+ readSuccessCallback.bind(this)('readAsArrayBuffer', null, file.start, totalSize, function (r) {
+ var resultArray = (this._progress === 0 ? new Uint8Array(totalSize) : new Uint8Array(this._result));
+ resultArray.set(new Uint8Array(r), this._progress);
+ this._result = resultArray.buffer;
+ }.bind(this));
+};
+
+module.exports = FileReader;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/FileSystem.js
new file mode 100644
index 00000000..f90c4583
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileSystem.js
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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 DirectoryEntry = require('./DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function (name, root) {
+ this.name = name;
+ if (root) {
+ this.root = new DirectoryEntry(root.name, root.fullPath, this, root.nativeURL);
+ } else {
+ this.root = new DirectoryEntry(this.name, '/', this);
+ }
+};
+
+FileSystem.prototype.__format__ = function (fullPath, nativeUrl) {
+ return fullPath;
+};
+
+FileSystem.prototype.toJSON = function () {
+ return '<FileSystem: ' + this.name + '>';
+};
+
+// Use instead of encodeURI() when encoding just the path part of a URI rather than an entire URI.
+FileSystem.encodeURIPath = function (path) {
+ // Because # is a valid filename character, it must be encoded to prevent part of the
+ // path from being parsed as a URI fragment.
+ return encodeURI(path).replace(/#/g, '%23');
+};
+
+module.exports = FileSystem;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileUploadOptions.js b/StoneIsland/plugins/cordova-plugin-file/www/FileUploadOptions.js
new file mode 100644
index 00000000..b4aa7c70
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileUploadOptions.js
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String} Name of file request parameter.
+ * @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object} Object with key: value params to send to the server.
+ * @param headers {Object} Keys are header names, values are header values. Multiple
+ * headers of the same name are not supported.
+ */
+var FileUploadOptions = function (fileKey, fileName, mimeType, params, headers, httpMethod) {
+ this.fileKey = fileKey || null;
+ this.fileName = fileName || null;
+ this.mimeType = mimeType || null;
+ this.params = params || null;
+ this.headers = headers || null;
+ this.httpMethod = httpMethod || null;
+};
+
+module.exports = FileUploadOptions;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileUploadResult.js b/StoneIsland/plugins/cordova-plugin-file/www/FileUploadResult.js
new file mode 100644
index 00000000..6c7105ce
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileUploadResult.js
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * FileUploadResult
+ * @constructor
+ */
+module.exports = function FileUploadResult (size, code, content) {
+ this.bytesSent = size;
+ this.responseCode = code;
+ this.response = content;
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/FileWriter.js b/StoneIsland/plugins/cordova-plugin-file/www/FileWriter.js
new file mode 100644
index 00000000..75ec9dce
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/FileWriter.js
@@ -0,0 +1,325 @@
+/*
+ *
+ * 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 exec = require('cordova/exec');
+var FileError = require('./FileError');
+var FileReader = require('./FileReader');
+var ProgressEvent = require('./ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ * The root directory is the root of the file system.
+ * To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function (file) {
+ this.fileName = '';
+ this.length = 0;
+ if (file) {
+ this.localURL = file.localURL || file;
+ this.length = file.size || 0;
+ }
+ // default is to write at the beginning of the file
+ this.position = 0;
+
+ this.readyState = 0; // EMPTY
+
+ this.result = null;
+
+ // Error
+ this.error = null;
+
+ // Event handlers
+ this.onwritestart = null; // When writing starts
+ this.onprogress = null; // While writing the file, and reporting partial file data
+ this.onwrite = null; // When the write has successfully completed.
+ this.onwriteend = null; // When the request has completed (either in success or failure).
+ this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
+ this.onerror = null; // When the write has failed (see errors).
+};
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function () {
+ // check for invalid state
+ if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // set error
+ this.error = new FileError(FileError.ABORT_ERR);
+
+ this.readyState = FileWriter.DONE;
+
+ // If abort callback
+ if (typeof this.onabort === 'function') {
+ this.onabort(new ProgressEvent('abort', {'target': this}));
+ }
+
+ // If write end callback
+ if (typeof this.onwriteend === 'function') {
+ this.onwriteend(new ProgressEvent('writeend', {'target': this}));
+ }
+};
+
+/**
+ * Writes data to the file
+ *
+ * @param data text or blob to be written
+ * @param isPendingBlobReadResult {Boolean} true if the data is the pending blob read operation result
+ */
+FileWriter.prototype.write = function (data, isPendingBlobReadResult) {
+
+ var that = this;
+ var supportsBinary = (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined');
+ /* eslint-disable no-undef */
+ var isProxySupportBlobNatively = (cordova.platformId === 'windows8' || cordova.platformId === 'windows');
+ var isBinary;
+
+ // Check to see if the incoming data is a blob
+ if (data instanceof File || (!isProxySupportBlobNatively && supportsBinary && data instanceof Blob)) {
+ var fileReader = new FileReader();
+ /* eslint-enable no-undef */
+ fileReader.onload = function () {
+ // Call this method again, with the arraybuffer as argument
+ FileWriter.prototype.write.call(that, this.result, true /* isPendingBlobReadResult */);
+ };
+ fileReader.onerror = function () {
+ // DONE state
+ that.readyState = FileWriter.DONE;
+
+ // Save error
+ that.error = this.error;
+
+ // If onerror callback
+ if (typeof that.onerror === 'function') {
+ that.onerror(new ProgressEvent('error', {'target': that}));
+ }
+
+ // If onwriteend callback
+ if (typeof that.onwriteend === 'function') {
+ that.onwriteend(new ProgressEvent('writeend', {'target': that}));
+ }
+ };
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ if (supportsBinary) {
+ fileReader.readAsArrayBuffer(data);
+ } else {
+ fileReader.readAsText(data);
+ }
+ return;
+ }
+
+ // Mark data type for safer transport over the binary bridge
+ isBinary = supportsBinary && (data instanceof ArrayBuffer);
+ if (isBinary && cordova.platformId === 'windowsphone') { // eslint-disable-line no-undef
+ // create a plain array, using the keys from the Uint8Array view so that we can serialize it
+ data = Array.apply(null, new Uint8Array(data));
+ }
+
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING && !isPendingBlobReadResult) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ var me = this;
+
+ // If onwritestart callback
+ if (typeof me.onwritestart === 'function') {
+ me.onwritestart(new ProgressEvent('writestart', {'target': me}));
+ }
+
+ // Write file
+ exec(
+ // Success callback
+ function (r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // position always increases by bytes written because file would be extended
+ me.position += r;
+ // The length of the file is now where we are done writing.
+
+ me.length = me.position;
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // If onwrite callback
+ if (typeof me.onwrite === 'function') {
+ me.onwrite(new ProgressEvent('write', {'target': me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === 'function') {
+ me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+ }
+ },
+ // Error callback
+ function (e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === 'function') {
+ me.onerror(new ProgressEvent('error', {'target': me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === 'function') {
+ me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+ }
+ }, 'File', 'write', [this.localURL, data, this.position, isBinary]);
+};
+
+/**
+ * Moves the file pointer to the location specified.
+ *
+ * If the offset is a negative number the position of the file
+ * pointer is rewound. If the offset is greater than the file
+ * size the position is set to the end of the file.
+ *
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function (offset) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ if (!offset && offset !== 0) {
+ return;
+ }
+
+ // See back from end of file.
+ if (offset < 0) {
+ this.position = Math.max(offset + this.length, 0);
+ // Offset is bigger than file size so set position
+ // to the end of the file.
+ } else if (offset > this.length) {
+ this.position = this.length;
+ // Offset is between 0 and file size so set the position
+ // to start writing.
+ } else {
+ this.position = offset;
+ }
+};
+
+/**
+ * Truncates the file to the size specified.
+ *
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function (size) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ var me = this;
+
+ // If onwritestart callback
+ if (typeof me.onwritestart === 'function') {
+ me.onwritestart(new ProgressEvent('writestart', {'target': this}));
+ }
+
+ // Write file
+ exec(
+ // Success callback
+ function (r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Update the length of the file
+ me.length = r;
+ me.position = Math.min(me.position, r);
+
+ // If onwrite callback
+ if (typeof me.onwrite === 'function') {
+ me.onwrite(new ProgressEvent('write', {'target': me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === 'function') {
+ me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+ }
+ },
+ // Error callback
+ function (e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === 'function') {
+ me.onerror(new ProgressEvent('error', {'target': me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === 'function') {
+ me.onwriteend(new ProgressEvent('writeend', {'target': me}));
+ }
+ }, 'File', 'truncate', [this.localURL, size]);
+};
+
+module.exports = FileWriter;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/Flags.js b/StoneIsland/plugins/cordova-plugin-file/www/Flags.js
new file mode 100644
index 00000000..2f15c88a
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/Flags.js
@@ -0,0 +1,36 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Supplies arguments to methods that lookup or create files and directories.
+ *
+ * @param create
+ * {boolean} file or directory if it doesn't exist
+ * @param exclusive
+ * {boolean} used with create; if true the command will fail if
+ * target path exists
+ */
+function Flags (create, exclusive) {
+ this.create = create || false;
+ this.exclusive = exclusive || false;
+}
+
+module.exports = Flags;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/LocalFileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/LocalFileSystem.js
new file mode 100644
index 00000000..1e8f2eeb
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/LocalFileSystem.js
@@ -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.
+ *
+*/
+
+exports.TEMPORARY = 0;
+exports.PERSISTENT = 1;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/Metadata.js b/StoneIsland/plugins/cordova-plugin-file/www/Metadata.js
new file mode 100644
index 00000000..713d7cb1
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/Metadata.js
@@ -0,0 +1,40 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * Information about the state of the file or directory
+ *
+ * {Date} modificationTime (readonly)
+ */
+var Metadata = function (metadata) {
+ if (typeof metadata === 'object') {
+ this.modificationTime = new Date(metadata.modificationTime);
+ this.size = metadata.size || 0;
+ } else if (typeof metadata === 'undefined') {
+ this.modificationTime = null;
+ this.size = 0;
+ } else {
+ /* Backwards compatiblity with platforms that only return a timestamp */
+ this.modificationTime = new Date(metadata);
+ }
+};
+
+module.exports = Metadata;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/ProgressEvent.js b/StoneIsland/plugins/cordova-plugin-file/www/ProgressEvent.js
new file mode 100644
index 00000000..336a226d
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/ProgressEvent.js
@@ -0,0 +1,67 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill
+// Feature test: See if we can instantiate a native ProgressEvent;
+// if so, use that approach,
+// otherwise fill-in with our own implementation.
+//
+// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview.
+var ProgressEvent = (function () {
+ /*
+ var createEvent = function(data) {
+ var event = document.createEvent('Events');
+ event.initEvent('ProgressEvent', false, false);
+ if (data) {
+ for (var i in data) {
+ if (data.hasOwnProperty(i)) {
+ event[i] = data[i];
+ }
+ }
+ if (data.target) {
+ // TODO: cannot call <some_custom_object>.dispatchEvent
+ // need to first figure out how to implement EventTarget
+ }
+ }
+ return event;
+ };
+ try {
+ var ev = createEvent({type:"abort",target:document});
+ return function ProgressEvent(type, data) {
+ data.type = type;
+ return createEvent(data);
+ };
+ } catch(e){
+ */
+ return function ProgressEvent (type, dict) {
+ this.type = type;
+ this.bubbles = false;
+ this.cancelBubble = false;
+ this.cancelable = false;
+ this.lengthComputable = false;
+ this.loaded = dict && dict.loaded ? dict.loaded : 0;
+ this.total = dict && dict.total ? dict.total : 0;
+ this.target = dict && dict.target ? dict.target : null;
+ };
+ // }
+})();
+
+module.exports = ProgressEvent;
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/android/FileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/android/FileSystem.js
new file mode 100644
index 00000000..438da00e
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/android/FileSystem.js
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+FILESYSTEM_PROTOCOL = 'cdvfile'; // eslint-disable-line no-undef
+
+module.exports = {
+ __format__: function (fullPath, nativeUrl) {
+ var path;
+ var contentUrlMatch = /^content:\/\//.exec(nativeUrl);
+ if (contentUrlMatch) {
+ // When available, use the path from a native content URL, which was already encoded by Android.
+ // This is necessary because JavaScript's encodeURI() does not encode as many characters as
+ // Android, which can result in permission exceptions when the encoding of a content URI
+ // doesn't match the string for which permission was originally granted.
+ path = nativeUrl.substring(contentUrlMatch[0].length - 1);
+ } else {
+ path = FileSystem.encodeURIPath(fullPath); // eslint-disable-line no-undef
+ if (!/^\//.test(path)) {
+ path = '/' + path;
+ }
+
+ var m = /\?.*/.exec(nativeUrl);
+ if (m) {
+ path += m[0];
+ }
+ }
+
+ return FILESYSTEM_PROTOCOL + '://localhost/' + this.name + path; // eslint-disable-line no-undef
+ }
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/browser/FileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/browser/FileSystem.js
new file mode 100644
index 00000000..a7c1d6ca
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/browser/FileSystem.js
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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 FILESYSTEM_PREFIX: true, module */
+
+FILESYSTEM_PREFIX = 'file:///';
+
+module.exports = {
+ __format__: function (fullPath) {
+ return (FILESYSTEM_PREFIX + this.name + (fullPath[0] === '/' ? '' : '/') + FileSystem.encodeURIPath(fullPath)); // eslint-disable-line no-undef
+ }
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/browser/Preparing.js b/StoneIsland/plugins/cordova-plugin-file/www/browser/Preparing.js
new file mode 100644
index 00000000..b826a652
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/browser/Preparing.js
@@ -0,0 +1,192 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+(function () {
+ /* global require */
+
+ // Only Chrome uses this file.
+ if (!require('./isChrome')()) {
+ return;
+ }
+
+ var channel = require('cordova/channel');
+ var FileError = require('./FileError');
+ var PERSISTENT_FS_QUOTA = 5 * 1024 * 1024;
+ var filePluginIsReadyEvent = new Event('filePluginIsReady'); // eslint-disable-line no-undef
+
+ var entryFunctionsCreated = false;
+ var quotaWasRequested = false;
+ var eventWasThrown = false;
+
+ if (!window.requestFileSystem) {
+ window.requestFileSystem = function (type, size, win, fail) {
+ if (fail) {
+ fail('Not supported');
+ }
+ };
+ } else {
+ window.requestFileSystem(window.TEMPORARY, 1, createFileEntryFunctions, function () {});
+ }
+
+ if (!window.resolveLocalFileSystemURL) {
+ window.resolveLocalFileSystemURL = function (url, win, fail) {
+ if (fail) {
+ fail('Not supported');
+ }
+ };
+ }
+
+ // Resolves a filesystem entry by its path - which is passed either in standard (filesystem:file://) or
+ // Cordova-specific (cdvfile://) universal way.
+ // Aligns with specification: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#widl-LocalFileSystem-resolveLocalFileSystemURL
+ var nativeResolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL;
+ window.resolveLocalFileSystemURL = function (url, win, fail) {
+ /* If url starts with `cdvfile` then we need convert it to Chrome real url first:
+ cdvfile://localhost/persistent/path/to/file -> filesystem:file://persistent/path/to/file */
+ if (url.trim().substr(0, 7) === 'cdvfile') {
+ /* Quirk:
+ Plugin supports cdvfile://localhost (local resources) only.
+ I.e. external resources are not supported via cdvfile. */
+ if (url.indexOf('cdvfile://localhost') !== -1) {
+ // Browser supports temporary and persistent only
+ var indexPersistent = url.indexOf('persistent');
+ var indexTemporary = url.indexOf('temporary');
+
+ /* Chrome urls start with 'filesystem:' prefix. See quirk:
+ toURL function in Chrome returns filesystem:-prefixed path depending on application host.
+ For example, filesystem:file:///persistent/somefile.txt,
+ filesystem:http://localhost:8080/persistent/somefile.txt. */
+ var prefix = 'filesystem:file:///';
+ if (location.protocol !== 'file:') { // eslint-disable-line no-undef
+ prefix = 'filesystem:' + location.origin + '/'; // eslint-disable-line no-undef
+ }
+
+ var result;
+ if (indexPersistent !== -1) {
+ // cdvfile://localhost/persistent/path/to/file -> filesystem:file://persistent/path/to/file
+ // or filesystem:http://localhost:8080/persistent/path/to/file
+ result = prefix + 'persistent' + url.substr(indexPersistent + 10);
+ nativeResolveLocalFileSystemURL(result, win, fail);
+ return;
+ }
+
+ if (indexTemporary !== -1) {
+ // cdvfile://localhost/temporary/path/to/file -> filesystem:file://temporary/path/to/file
+ // or filesystem:http://localhost:8080/temporary/path/to/file
+ result = prefix + 'temporary' + url.substr(indexTemporary + 9);
+ nativeResolveLocalFileSystemURL(result, win, fail);
+ return;
+ }
+ }
+
+ // cdvfile other than local file resource is not supported
+ if (fail) {
+ fail(new FileError(FileError.ENCODING_ERR));
+ }
+ } else {
+ nativeResolveLocalFileSystemURL(url, win, fail);
+ }
+ };
+
+ function createFileEntryFunctions (fs) {
+ fs.root.getFile('todelete_658674_833_4_cdv', {create: true}, function (fileEntry) {
+ var fileEntryType = Object.getPrototypeOf(fileEntry);
+ var entryType = Object.getPrototypeOf(fileEntryType);
+
+ // Save the original method
+ var origToURL = entryType.toURL;
+ entryType.toURL = function () {
+ var origURL = origToURL.call(this);
+ if (this.isDirectory && origURL.substr(-1) !== '/') {
+ return origURL + '/';
+ }
+ return origURL;
+ };
+
+ entryType.toNativeURL = function () {
+ console.warn("DEPRECATED: Update your code to use 'toURL'");
+ return this.toURL();
+ };
+
+ entryType.toInternalURL = function () {
+ if (this.toURL().indexOf('persistent') > -1) {
+ return 'cdvfile://localhost/persistent' + this.fullPath;
+ }
+
+ if (this.toURL().indexOf('temporary') > -1) {
+ return 'cdvfile://localhost/temporary' + this.fullPath;
+ }
+ };
+
+ entryType.setMetadata = function (win, fail /*, metadata */) {
+ if (fail) {
+ fail('Not supported');
+ }
+ };
+
+ fileEntry.createWriter(function (writer) {
+ var originalWrite = writer.write;
+ var writerProto = Object.getPrototypeOf(writer);
+ writerProto.write = function (blob) {
+ if (blob instanceof Blob) { // eslint-disable-line no-undef
+ originalWrite.apply(this, [blob]);
+ } else {
+ var realBlob = new Blob([blob]); // eslint-disable-line no-undef
+ originalWrite.apply(this, [realBlob]);
+ }
+ };
+
+ fileEntry.remove(function () { entryFunctionsCreated = true; }, function () { /* empty callback */ });
+ });
+ });
+ }
+
+ window.initPersistentFileSystem = function (size, win, fail) {
+ if (navigator.webkitPersistentStorage) {
+ navigator.webkitPersistentStorage.requestQuota(size, win, fail);
+ return;
+ }
+
+ fail('This browser does not support this function');
+ };
+
+ window.isFilePluginReadyRaised = function () { return eventWasThrown; };
+
+ window.initPersistentFileSystem(PERSISTENT_FS_QUOTA, function () {
+ console.log('Persistent fs quota granted');
+ quotaWasRequested = true;
+ }, function (e) {
+ console.log('Error occured while trying to request Persistent fs quota: ' + JSON.stringify(e));
+ });
+
+ channel.onCordovaReady.subscribe(function () {
+ function dispatchEventIfReady () {
+ if (entryFunctionsCreated && quotaWasRequested) {
+ window.dispatchEvent(filePluginIsReadyEvent);
+ eventWasThrown = true;
+ } else {
+ setTimeout(dispatchEventIfReady, 100);
+ }
+ }
+
+ dispatchEventIfReady();
+ }, false);
+})();
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/browser/isChrome.js b/StoneIsland/plugins/cordova-plugin-file/www/browser/isChrome.js
new file mode 100644
index 00000000..853277d7
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/browser/isChrome.js
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+module.exports = function () {
+ // window.webkitRequestFileSystem and window.webkitResolveLocalFileSystemURL are available only in Chrome and
+ // possibly a good flag to indicate that we're running in Chrome
+ return window.webkitRequestFileSystem && window.webkitResolveLocalFileSystemURL;
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/fileSystemPaths.js b/StoneIsland/plugins/cordova-plugin-file/www/fileSystemPaths.js
new file mode 100644
index 00000000..f79794bb
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/fileSystemPaths.js
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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 exec = require('cordova/exec');
+var channel = require('cordova/channel');
+
+exports.file = {
+ // Read-only directory where the application is installed.
+ applicationDirectory: null,
+ // Root of app's private writable storage
+ applicationStorageDirectory: null,
+ // Where to put app-specific data files.
+ dataDirectory: null,
+ // Cached files that should survive app restarts.
+ // Apps should not rely on the OS to delete files in here.
+ cacheDirectory: null,
+ // Android: the application space on external storage.
+ externalApplicationStorageDirectory: null,
+ // Android: Where to put app-specific data files on external storage.
+ externalDataDirectory: null,
+ // Android: the application cache on external storage.
+ externalCacheDirectory: null,
+ // Android: the external storage (SD card) root.
+ externalRootDirectory: null,
+ // iOS: Temp directory that the OS can clear at will.
+ tempDirectory: null,
+ // iOS: Holds app-specific files that should be synced (e.g. to iCloud).
+ syncedDataDirectory: null,
+ // iOS: Files private to the app, but that are meaningful to other applications (e.g. Office files)
+ documentsDirectory: null,
+ // BlackBerry10: Files globally available to all apps
+ sharedDirectory: null
+};
+
+channel.waitForInitialization('onFileSystemPathsReady');
+channel.onCordovaReady.subscribe(function () {
+ function after (paths) {
+ for (var k in paths) {
+ exports.file[k] = paths[k];
+ }
+ channel.initializationComplete('onFileSystemPathsReady');
+ }
+ exec(after, null, 'File', 'requestAllPaths', []);
+});
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/fileSystems-roots.js b/StoneIsland/plugins/cordova-plugin-file/www/fileSystems-roots.js
new file mode 100644
index 00000000..b15adbd7
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/fileSystems-roots.js
@@ -0,0 +1,46 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+// Map of fsName -> FileSystem.
+var fsMap = null;
+var FileSystem = require('./FileSystem');
+var exec = require('cordova/exec');
+
+// Overridden by Android, BlackBerry 10 and iOS to populate fsMap.
+require('./fileSystems').getFs = function (name, callback) {
+ function success (response) {
+ fsMap = {};
+ for (var i = 0; i < response.length; ++i) {
+ var fsRoot = response[i];
+ if (fsRoot) {
+ var fs = new FileSystem(fsRoot.filesystemName, fsRoot);
+ fsMap[fs.name] = fs;
+ }
+ }
+ callback(fsMap[name]);
+ }
+
+ if (fsMap) {
+ callback(fsMap[name]);
+ } else {
+ exec(success, null, 'File', 'requestAllFileSystems', []);
+ }
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/fileSystems.js b/StoneIsland/plugins/cordova-plugin-file/www/fileSystems.js
new file mode 100644
index 00000000..40e55748
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/fileSystems.js
@@ -0,0 +1,25 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+// Overridden by Android, BlackBerry 10 and iOS to populate fsMap.
+module.exports.getFs = function (name, callback) {
+ callback(null);
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/ios/FileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/ios/FileSystem.js
new file mode 100644
index 00000000..cb14c783
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/ios/FileSystem.js
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.
+ *
+*/
+/* eslint no-undef : 0 */
+FILESYSTEM_PROTOCOL = 'cdvfile';
+
+module.exports = {
+ __format__: function (fullPath) {
+ var path = ('/' + this.name + (fullPath[0] === '/' ? '' : '/') + FileSystem.encodeURIPath(fullPath)).replace('//', '/');
+ return FILESYSTEM_PROTOCOL + '://localhost' + path;
+ }
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/osx/FileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/osx/FileSystem.js
new file mode 100644
index 00000000..cb14c783
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/osx/FileSystem.js
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.
+ *
+*/
+/* eslint no-undef : 0 */
+FILESYSTEM_PROTOCOL = 'cdvfile';
+
+module.exports = {
+ __format__: function (fullPath) {
+ var path = ('/' + this.name + (fullPath[0] === '/' ? '' : '/') + FileSystem.encodeURIPath(fullPath)).replace('//', '/');
+ return FILESYSTEM_PROTOCOL + '://localhost' + path;
+ }
+};
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/requestFileSystem.js b/StoneIsland/plugins/cordova-plugin-file/www/requestFileSystem.js
new file mode 100644
index 00000000..d9110cd2
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/requestFileSystem.js
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+(function () {
+ // For browser platform: not all browsers use this file.
+ function checkBrowser () {
+ if (cordova.platformId === 'browser' && require('./isChrome')()) { // eslint-disable-line no-undef
+ module.exports = window.requestFileSystem || window.webkitRequestFileSystem;
+ return true;
+ }
+ return false;
+ }
+ if (checkBrowser()) {
+ return;
+ }
+
+ var argscheck = require('cordova/argscheck');
+ var FileError = require('./FileError');
+ var FileSystem = require('./FileSystem');
+ var exec = require('cordova/exec');
+ var fileSystems = require('./fileSystems');
+
+ /**
+ * Request a file system in which to store application data.
+ * @param type local file system type
+ * @param size indicates how much storage space, in bytes, the application expects to need
+ * @param successCallback invoked with a FileSystem object
+ * @param errorCallback invoked if error occurs retrieving file system
+ */
+ var requestFileSystem = function (type, size, successCallback, errorCallback) {
+ argscheck.checkArgs('nnFF', 'requestFileSystem', arguments);
+ var fail = function (code) {
+ if (errorCallback) {
+ errorCallback(new FileError(code));
+ }
+ };
+
+ if (type < 0) {
+ fail(FileError.SYNTAX_ERR);
+ } else {
+ // if successful, return a FileSystem object
+ var success = function (file_system) {
+ if (file_system) {
+ if (successCallback) {
+ fileSystems.getFs(file_system.name, function (fs) {
+ // This should happen only on platforms that haven't implemented requestAllFileSystems (windows)
+ if (!fs) {
+ fs = new FileSystem(file_system.name, file_system.root);
+ }
+ successCallback(fs);
+ });
+ }
+ } else {
+ // no FileSystem object returned
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ };
+ exec(success, fail, 'File', 'requestFileSystem', [type, size]);
+ }
+ };
+
+ module.exports = requestFileSystem;
+})();
diff --git a/StoneIsland/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js b/StoneIsland/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js
new file mode 100644
index 00000000..764662c0
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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.
+ *
+*/
+(function () {
+ // For browser platform: not all browsers use overrided `resolveLocalFileSystemURL`.
+ function checkBrowser () {
+ if (cordova.platformId === 'browser' && require('./isChrome')()) { // eslint-disable-line no-undef
+ module.exports.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL;
+ return true;
+ }
+ return false;
+ }
+ if (checkBrowser()) {
+ return;
+ }
+
+ var argscheck = require('cordova/argscheck');
+ var DirectoryEntry = require('./DirectoryEntry');
+ var FileEntry = require('./FileEntry');
+ var FileError = require('./FileError');
+ var exec = require('cordova/exec');
+ var fileSystems = require('./fileSystems');
+
+ /**
+ * Look up file system Entry referred to by local URI.
+ * @param {DOMString} uri URI referring to a local file or directory
+ * @param successCallback invoked with Entry object corresponding to URI
+ * @param errorCallback invoked if error occurs retrieving file system entry
+ */
+ module.exports.resolveLocalFileSystemURL = module.exports.resolveLocalFileSystemURL || function (uri, successCallback, errorCallback) {
+ argscheck.checkArgs('sFF', 'resolveLocalFileSystemURI', arguments);
+ // error callback
+ var fail = function (error) {
+ if (errorCallback) {
+ errorCallback(new FileError(error));
+ }
+ };
+ // sanity check for 'not:valid:filename' or '/not:valid:filename'
+ // file.spec.12 window.resolveLocalFileSystemURI should error (ENCODING_ERR) when resolving invalid URI with leading /.
+ if (!uri || uri.split(':').length > 2) {
+ setTimeout(function () {
+ fail(FileError.ENCODING_ERR);
+ }, 0);
+ return;
+ }
+ // if successful, return either a file or directory entry
+ var success = function (entry) {
+ if (entry) {
+ if (successCallback) {
+ // create appropriate Entry object
+ var fsName = entry.filesystemName || (entry.filesystem && entry.filesystem.name) || (entry.filesystem === window.PERSISTENT ? 'persistent' : 'temporary'); // eslint-disable-line no-undef
+ fileSystems.getFs(fsName, function (fs) {
+ // This should happen only on platforms that haven't implemented requestAllFileSystems (windows)
+ if (!fs) {
+ fs = new FileSystem(fsName, {name: '', fullPath: '/'}); // eslint-disable-line no-undef
+ }
+ var result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath, fs, entry.nativeURL) : new FileEntry(entry.name, entry.fullPath, fs, entry.nativeURL);
+ successCallback(result);
+ });
+ }
+ } else {
+ // no Entry object returned
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ };
+
+ exec(success, fail, 'File', 'resolveLocalFileSystemURI', [uri]);
+ };
+
+ module.exports.resolveLocalFileSystemURI = function () {
+ console.log('resolveLocalFileSystemURI is deprecated. Please call resolveLocalFileSystemURL instead.');
+ module.exports.resolveLocalFileSystemURL.apply(this, arguments);
+ };
+})();