diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..587ae74 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +## Contributing + +Thank you for your interest in contributing to the Hello World PhoneGap Template. + +#### Issues + +One of the best contributions is filing issues in the [Issue Tracker][issue-tracker]. + +All contributions (feature or bug fix) should start with an issue first. + +#### Pull requests + +- Fork the repository +- Create a branch (`git checkout -b issue-nnn`) +- If possible, add a failing test +- Commit your fix/feature and tests (`git commit -am "fixes for issue #nnn"`) +- Push to the branch (`git push -u origin issue-nnn`) +- Open a [Pull Request][pull-request] + + +[issue-tracker]: https://github.com/phonegap/phonegap-template-hello-world/issues +[pull-request]: https://github.com/phonegap/phonegap-template-hello-world/pulls diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..b4d4b15 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,16 @@ +The following copyright message should appear at the top of all +source files. This file can be removed from your repository. + +Copyright (c) 2012-2014 Adobe Systems Incorporated. All rights reserved. + +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. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a571007 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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 2012-2014 Adobe Systems Incorporated. + + 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. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..fb15fee --- /dev/null +++ b/NOTICE @@ -0,0 +1,11 @@ +Adobe PhoneGap +Copyright 2012-2014 Adobe Systems Incorporated. + +This product includes software developed by +Apache Cordova (http://cordova.apache.org) + +This product includes software developed by +Apache Cordova Hello World (https://github.com/apache/cordova-app-hello-world) + +This product includes software developed by +Jasmine (https://github.com/pivotal/jasmine) diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..ec993f8 --- /dev/null +++ b/config.xml @@ -0,0 +1,117 @@ + + + Blank Project with Plugins + + Hello World sample application that responds to the deviceready event. + + + PhoneGap Team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hooks/README.md b/hooks/README.md new file mode 100644 index 0000000..574ad4c --- /dev/null +++ b/hooks/README.md @@ -0,0 +1,23 @@ + +# Cordova Hooks + +Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide. diff --git a/package.json b/package.json new file mode 100644 index 0000000..b3eeb8a --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "phonegap-template-hello-world", + "version": "4.0.3", + "description": "> A Hello World template built with PhoneGap", + "repository": { + "type": "git", + "url": "https://github.com/phonegap/phonegap-app-hello-world.git" + }, + "author": "PhoneGap Team", + "license": "Apache-2.0", + "keywords": [ + "ecosystem:phonegap", + "ecosystem:cordova", + "cordova:template", + "phonegap:template" + ], + "bugs": { + "url": "https://github.com/phonegap/phonegap-app-hello-world/issues" + }, + "homepage": "https://github.com/phonegap/phonegap-app-hello-world" +} diff --git a/platforms/browser/browser.json b/platforms/browser/browser.json new file mode 100644 index 0000000..a82acb7 --- /dev/null +++ b/platforms/browser/browser.json @@ -0,0 +1,718 @@ +{ + "prepare_queue": { + "installed": [], + "uninstalled": [] + }, + "config_munge": { + "files": { + "config.xml": { + "parents": { + "/*": [ + { + "xml": "", + "count": 1 + }, + { + "xml": "", + "count": 1 + } + ] + } + } + } + }, + "installed_plugins": { + "cordova-plugin-battery-status": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-camera": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-compat": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-file": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-media-capture": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-console": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-contacts": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-device": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-device-motion": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-device-orientation": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-dialogs": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-file-transfer": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-geolocation": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-globalization": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-inappbrowser": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-media": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-network-information": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-splashscreen": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-statusbar": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-vibration": { + "PACKAGE_NAME": "com.phonegap.helloworld" + }, + "cordova-plugin-whitelist": { + "PACKAGE_NAME": "com.phonegap.helloworld" + } + }, + "dependent_plugins": {}, + "modules": [ + { + "file": "plugins/cordova-plugin-battery-status/www/battery.js", + "id": "cordova-plugin-battery-status.battery", + "pluginId": "cordova-plugin-battery-status", + "clobbers": [ + "navigator.battery" + ] + }, + { + "file": "plugins/cordova-plugin-battery-status/src/browser/BatteryProxy.js", + "id": "cordova-plugin-battery-status.Battery", + "pluginId": "cordova-plugin-battery-status", + "runs": true + }, + { + "file": "plugins/cordova-plugin-camera/www/CameraConstants.js", + "id": "cordova-plugin-camera.Camera", + "pluginId": "cordova-plugin-camera", + "clobbers": [ + "Camera" + ] + }, + { + "file": "plugins/cordova-plugin-camera/www/CameraPopoverOptions.js", + "id": "cordova-plugin-camera.CameraPopoverOptions", + "pluginId": "cordova-plugin-camera", + "clobbers": [ + "CameraPopoverOptions" + ] + }, + { + "file": "plugins/cordova-plugin-camera/www/Camera.js", + "id": "cordova-plugin-camera.camera", + "pluginId": "cordova-plugin-camera", + "clobbers": [ + "navigator.camera" + ] + }, + { + "file": "plugins/cordova-plugin-camera/src/browser/CameraProxy.js", + "id": "cordova-plugin-camera.CameraProxy", + "pluginId": "cordova-plugin-camera", + "runs": true + }, + { + "file": "plugins/cordova-plugin-file/www/DirectoryEntry.js", + "id": "cordova-plugin-file.DirectoryEntry", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.DirectoryEntry" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/DirectoryReader.js", + "id": "cordova-plugin-file.DirectoryReader", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.DirectoryReader" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/Entry.js", + "id": "cordova-plugin-file.Entry", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.Entry" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/File.js", + "id": "cordova-plugin-file.File", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.File" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileEntry.js", + "id": "cordova-plugin-file.FileEntry", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileEntry" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileError.js", + "id": "cordova-plugin-file.FileError", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileError" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileReader.js", + "id": "cordova-plugin-file.FileReader", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileReader" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileSystem.js", + "id": "cordova-plugin-file.FileSystem", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileSystem" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileUploadOptions.js", + "id": "cordova-plugin-file.FileUploadOptions", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileUploadOptions" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileUploadResult.js", + "id": "cordova-plugin-file.FileUploadResult", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileUploadResult" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/FileWriter.js", + "id": "cordova-plugin-file.FileWriter", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.FileWriter" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/Flags.js", + "id": "cordova-plugin-file.Flags", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.Flags" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/LocalFileSystem.js", + "id": "cordova-plugin-file.LocalFileSystem", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.LocalFileSystem" + ], + "merges": [ + "window" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/Metadata.js", + "id": "cordova-plugin-file.Metadata", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.Metadata" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/ProgressEvent.js", + "id": "cordova-plugin-file.ProgressEvent", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.ProgressEvent" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/fileSystems.js", + "id": "cordova-plugin-file.fileSystems", + "pluginId": "cordova-plugin-file" + }, + { + "file": "plugins/cordova-plugin-file/www/requestFileSystem.js", + "id": "cordova-plugin-file.requestFileSystem", + "pluginId": "cordova-plugin-file", + "clobbers": [ + "window.requestFileSystem" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/resolveLocalFileSystemURI.js", + "id": "cordova-plugin-file.resolveLocalFileSystemURI", + "pluginId": "cordova-plugin-file", + "merges": [ + "window" + ] + }, + { + "file": "plugins/cordova-plugin-file/www/browser/isChrome.js", + "id": "cordova-plugin-file.isChrome", + "pluginId": "cordova-plugin-file", + "runs": true + }, + { + "file": "plugins/cordova-plugin-file/www/browser/Preparing.js", + "id": "cordova-plugin-file.Preparing", + "pluginId": "cordova-plugin-file", + "runs": true + }, + { + "file": "plugins/cordova-plugin-file/src/browser/FileProxy.js", + "id": "cordova-plugin-file.browserFileProxy", + "pluginId": "cordova-plugin-file", + "runs": true + }, + { + "file": "plugins/cordova-plugin-file/www/fileSystemPaths.js", + "id": "cordova-plugin-file.fileSystemPaths", + "pluginId": "cordova-plugin-file", + "merges": [ + "cordova" + ], + "runs": true + }, + { + "file": "plugins/cordova-plugin-file/www/browser/FileSystem.js", + "id": "cordova-plugin-file.firefoxFileSystem", + "pluginId": "cordova-plugin-file", + "merges": [ + "window.FileSystem" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/CaptureAudioOptions.js", + "id": "cordova-plugin-media-capture.CaptureAudioOptions", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "CaptureAudioOptions" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/CaptureImageOptions.js", + "id": "cordova-plugin-media-capture.CaptureImageOptions", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "CaptureImageOptions" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/CaptureVideoOptions.js", + "id": "cordova-plugin-media-capture.CaptureVideoOptions", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "CaptureVideoOptions" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/CaptureError.js", + "id": "cordova-plugin-media-capture.CaptureError", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "CaptureError" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/MediaFileData.js", + "id": "cordova-plugin-media-capture.MediaFileData", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "MediaFileData" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/MediaFile.js", + "id": "cordova-plugin-media-capture.MediaFile", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "MediaFile" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/www/capture.js", + "id": "cordova-plugin-media-capture.capture", + "pluginId": "cordova-plugin-media-capture", + "clobbers": [ + "navigator.device.capture" + ] + }, + { + "file": "plugins/cordova-plugin-media-capture/src/browser/CaptureProxy.js", + "id": "cordova-plugin-media-capture.CaptureProxy", + "pluginId": "cordova-plugin-media-capture", + "runs": true + }, + { + "file": "plugins/cordova-plugin-contacts/www/contacts.js", + "id": "cordova-plugin-contacts.contacts", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "navigator.contacts" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/Contact.js", + "id": "cordova-plugin-contacts.Contact", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "Contact" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactAddress.js", + "id": "cordova-plugin-contacts.ContactAddress", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "ContactAddress" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactError.js", + "id": "cordova-plugin-contacts.ContactError", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "ContactError" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactField.js", + "id": "cordova-plugin-contacts.ContactField", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "ContactField" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactFindOptions.js", + "id": "cordova-plugin-contacts.ContactFindOptions", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "ContactFindOptions" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactName.js", + "id": "cordova-plugin-contacts.ContactName", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "ContactName" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactOrganization.js", + "id": "cordova-plugin-contacts.ContactOrganization", + "pluginId": "cordova-plugin-contacts", + "clobbers": [ + "ContactOrganization" + ] + }, + { + "file": "plugins/cordova-plugin-contacts/www/ContactFieldType.js", + "id": "cordova-plugin-contacts.ContactFieldType", + "pluginId": "cordova-plugin-contacts", + "merges": [ + "" + ] + }, + { + "file": "plugins/cordova-plugin-device/www/device.js", + "id": "cordova-plugin-device.device", + "pluginId": "cordova-plugin-device", + "clobbers": [ + "device" + ] + }, + { + "file": "plugins/cordova-plugin-device/src/browser/DeviceProxy.js", + "id": "cordova-plugin-device.DeviceProxy", + "pluginId": "cordova-plugin-device", + "runs": true + }, + { + "file": "plugins/cordova-plugin-device-motion/www/Acceleration.js", + "id": "cordova-plugin-device-motion.Acceleration", + "pluginId": "cordova-plugin-device-motion", + "clobbers": [ + "Acceleration" + ] + }, + { + "file": "plugins/cordova-plugin-device-motion/www/accelerometer.js", + "id": "cordova-plugin-device-motion.accelerometer", + "pluginId": "cordova-plugin-device-motion", + "clobbers": [ + "navigator.accelerometer" + ] + }, + { + "file": "plugins/cordova-plugin-device-motion/src/browser/AccelerometerProxy.js", + "id": "cordova-plugin-device-motion.AccelerometerProxy", + "pluginId": "cordova-plugin-device-motion", + "runs": true + }, + { + "file": "plugins/cordova-plugin-device-orientation/www/CompassError.js", + "id": "cordova-plugin-device-orientation.CompassError", + "pluginId": "cordova-plugin-device-orientation", + "clobbers": [ + "CompassError" + ] + }, + { + "file": "plugins/cordova-plugin-device-orientation/www/CompassHeading.js", + "id": "cordova-plugin-device-orientation.CompassHeading", + "pluginId": "cordova-plugin-device-orientation", + "clobbers": [ + "CompassHeading" + ] + }, + { + "file": "plugins/cordova-plugin-device-orientation/www/compass.js", + "id": "cordova-plugin-device-orientation.compass", + "pluginId": "cordova-plugin-device-orientation", + "clobbers": [ + "navigator.compass" + ] + }, + { + "file": "plugins/cordova-plugin-device-orientation/src/browser/CompassProxy.js", + "id": "cordova-plugin-device-orientation.CompassProxy", + "pluginId": "cordova-plugin-device-orientation", + "runs": true + }, + { + "file": "plugins/cordova-plugin-dialogs/www/notification.js", + "id": "cordova-plugin-dialogs.notification", + "pluginId": "cordova-plugin-dialogs", + "merges": [ + "navigator.notification" + ] + }, + { + "file": "plugins/cordova-plugin-dialogs/www/browser/notification.js", + "id": "cordova-plugin-dialogs.notification_browser", + "pluginId": "cordova-plugin-dialogs", + "merges": [ + "navigator.notification" + ] + }, + { + "file": "plugins/cordova-plugin-file-transfer/www/FileTransferError.js", + "id": "cordova-plugin-file-transfer.FileTransferError", + "pluginId": "cordova-plugin-file-transfer", + "clobbers": [ + "window.FileTransferError" + ] + }, + { + "file": "plugins/cordova-plugin-file-transfer/www/FileTransfer.js", + "id": "cordova-plugin-file-transfer.FileTransfer", + "pluginId": "cordova-plugin-file-transfer", + "clobbers": [ + "window.FileTransfer" + ] + }, + { + "file": "plugins/cordova-plugin-file-transfer/www/browser/FileTransfer.js", + "id": "cordova-plugin-file-transfer.BrowserFileTransfer", + "pluginId": "cordova-plugin-file-transfer", + "clobbers": [ + "window.FileTransfer" + ] + }, + { + "file": "plugins/cordova-plugin-globalization/www/GlobalizationError.js", + "id": "cordova-plugin-globalization.GlobalizationError", + "pluginId": "cordova-plugin-globalization", + "clobbers": [ + "window.GlobalizationError" + ] + }, + { + "file": "plugins/cordova-plugin-globalization/www/globalization.js", + "id": "cordova-plugin-globalization.globalization", + "pluginId": "cordova-plugin-globalization", + "clobbers": [ + "navigator.globalization" + ] + }, + { + "file": "plugins/cordova-plugin-globalization/www/browser/moment.js", + "id": "cordova-plugin-globalization.moment", + "pluginId": "cordova-plugin-globalization", + "runs": true + }, + { + "file": "plugins/cordova-plugin-globalization/src/browser/GlobalizationProxy.js", + "id": "cordova-plugin-globalization.GlobalizationProxy", + "pluginId": "cordova-plugin-globalization", + "runs": true + }, + { + "file": "plugins/cordova-plugin-inappbrowser/www/inappbrowser.js", + "id": "cordova-plugin-inappbrowser.inappbrowser", + "pluginId": "cordova-plugin-inappbrowser", + "clobbers": [ + "cordova.InAppBrowser.open", + "window.open" + ] + }, + { + "file": "plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js", + "id": "cordova-plugin-inappbrowser.InAppBrowserProxy", + "pluginId": "cordova-plugin-inappbrowser", + "merges": [ + "" + ] + }, + { + "file": "plugins/cordova-plugin-media/www/MediaError.js", + "id": "cordova-plugin-media.MediaError", + "pluginId": "cordova-plugin-media", + "clobbers": [ + "window.MediaError" + ] + }, + { + "file": "plugins/cordova-plugin-media/www/Media.js", + "id": "cordova-plugin-media.Media", + "pluginId": "cordova-plugin-media", + "clobbers": [ + "window.Media" + ] + }, + { + "file": "plugins/cordova-plugin-media/www/browser/Media.js", + "id": "cordova-plugin-media.BrowserMedia", + "pluginId": "cordova-plugin-media", + "clobbers": [ + "window.Media" + ] + }, + { + "file": "plugins/cordova-plugin-network-information/www/network.js", + "id": "cordova-plugin-network-information.network", + "pluginId": "cordova-plugin-network-information", + "clobbers": [ + "navigator.connection", + "navigator.network.connection" + ] + }, + { + "file": "plugins/cordova-plugin-network-information/www/Connection.js", + "id": "cordova-plugin-network-information.Connection", + "pluginId": "cordova-plugin-network-information", + "clobbers": [ + "Connection" + ] + }, + { + "file": "plugins/cordova-plugin-network-information/src/browser/network.js", + "id": "cordova-plugin-network-information.NetworkInfoProxy", + "pluginId": "cordova-plugin-network-information", + "runs": true + }, + { + "file": "plugins/cordova-plugin-splashscreen/www/splashscreen.js", + "id": "cordova-plugin-splashscreen.SplashScreen", + "pluginId": "cordova-plugin-splashscreen", + "clobbers": [ + "navigator.splashscreen" + ] + }, + { + "file": "plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js", + "id": "cordova-plugin-splashscreen.SplashScreenProxy", + "pluginId": "cordova-plugin-splashscreen", + "runs": true + }, + { + "file": "plugins/cordova-plugin-statusbar/www/statusbar.js", + "id": "cordova-plugin-statusbar.statusbar", + "pluginId": "cordova-plugin-statusbar", + "clobbers": [ + "window.StatusBar" + ] + }, + { + "file": "plugins/cordova-plugin-statusbar/src/browser/statusbar.js", + "id": "cordova-plugin-statusbar.statusbar.Browser", + "pluginId": "cordova-plugin-statusbar", + "merges": [ + "window.StatusBar" + ] + }, + { + "file": "plugins/cordova-plugin-vibration/www/vibration.js", + "id": "cordova-plugin-vibration.notification", + "pluginId": "cordova-plugin-vibration", + "merges": [ + "navigator.notification", + "navigator" + ] + }, + { + "file": "plugins/cordova-plugin-vibration/src/browser/Vibration.js", + "id": "cordova-plugin-vibration.Vibration", + "pluginId": "cordova-plugin-vibration", + "merges": [ + "navigator.notification", + "navigator" + ] + } + ], + "plugin_metadata": { + "cordova-plugin-battery-status": "1.1.2", + "cordova-plugin-camera": "2.1.1", + "cordova-plugin-compat": "1.1.0", + "cordova-plugin-file": "4.3.1", + "cordova-plugin-media-capture": "1.2.0", + "cordova-plugin-console": "1.0.5", + "cordova-plugin-contacts": "2.0.1", + "cordova-plugin-device": "1.1.4", + "cordova-plugin-device-motion": "1.2.3", + "cordova-plugin-device-orientation": "1.0.5", + "cordova-plugin-dialogs": "1.2.1", + "cordova-plugin-file-transfer": "1.5.1", + "cordova-plugin-geolocation": "2.1.0", + "cordova-plugin-globalization": "1.0.5", + "cordova-plugin-inappbrowser": "1.3.0", + "cordova-plugin-media": "2.2.0", + "cordova-plugin-network-information": "1.2.1", + "cordova-plugin-splashscreen": "3.2.2", + "cordova-plugin-statusbar": "2.1.3", + "cordova-plugin-vibration": "2.1.3", + "cordova-plugin-whitelist": "1.2.2" + } +} \ No newline at end of file diff --git a/platforms/browser/config.xml b/platforms/browser/config.xml new file mode 100644 index 0000000..4f0ecb1 --- /dev/null +++ b/platforms/browser/config.xml @@ -0,0 +1,26 @@ + + + + + + + + + Blank Project with Plugins + + Hello World sample application that responds to the deviceready event. + + + PhoneGap Team + + + + + + + + + + + + diff --git a/platforms/browser/cordova/build b/platforms/browser/cordova/build new file mode 100755 index 0000000..e867ab1 --- /dev/null +++ b/platforms/browser/cordova/build @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + + +var build = require('./lib/build'), + args = process.argv; + +// provide help +if ( args[2] == '--help' || args[2] == '/?' || args[2] == '-h' || args[2] == '/h' || + args[2] == 'help' || args[2] == '-help' || args[2] == '/help') { + build.help(); + process.exit(0); +} else { + + build.run(); +} diff --git a/platforms/browser/cordova/build.bat b/platforms/browser/cordova/build.bat new file mode 100644 index 0000000..02641bc --- /dev/null +++ b/platforms/browser/cordova/build.bat @@ -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. + +@ECHO OFF +SET script_path="%~dp0build" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'build' script in 'cordova' folder, aborting...>&2 + EXIT /B 1 +) diff --git a/platforms/browser/cordova/clean b/platforms/browser/cordova/clean new file mode 100755 index 0000000..da21bc7 --- /dev/null +++ b/platforms/browser/cordova/clean @@ -0,0 +1,37 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + + +var path = require('path'), + clean = require('./lib/clean'), + reqs = require('./lib/check_reqs'), + args = process.argv; + +// Support basic help commands +if ( args.length > 2 + || args[2] == '--help' || args[2] == '/?' || args[2] == '-h' || + args[2] == 'help' || args[2] == '-help' || args[2] == '/help') { + console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'clean')) ); + process.exit(0); +} else { + clean.cleanProject(); +} + diff --git a/platforms/browser/cordova/defaults.xml b/platforms/browser/cordova/defaults.xml new file mode 100644 index 0000000..a7b31c0 --- /dev/null +++ b/platforms/browser/cordova/defaults.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/platforms/browser/cordova/lib/build.js b/platforms/browser/cordova/lib/build.js new file mode 100644 index 0000000..fde7519 --- /dev/null +++ b/platforms/browser/cordova/lib/build.js @@ -0,0 +1,65 @@ +#!/usr/bin/env node + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var path = require('path'), + fs = require('fs'), + shjs = require('shelljs'), + zip = require('adm-zip'), + Q = require('q'), + clean = require('./clean'), + check_reqs = require('./check_reqs'), + platformWwwDir = path.join('platforms', 'browser', 'www'), + platformBuildDir = path.join('platforms', 'browser', 'build'), + packageFile = path.join(platformBuildDir, 'package.zip'); + +/** + * run + * Creates a zip file int platform/build folder + */ +module.exports.run = function(){ + + return check_reqs.run() + .then(function(){ + return clean.cleanProject(); + }, + function checkReqsError(err){ + console.error('Please make sure you meet the software requirements in order to build a browser cordova project'); + }) + .then(function(){ + + if (!fs.existsSync(platformBuildDir)) { + fs.mkdirSync(platformBuildDir); + } + + // add the project to a zipfile + var zipFile = zip(); + zipFile.addLocalFolder(platformWwwDir, '.'); + zipFile.writeZip(packageFile); + + return Q.resolve(); + + }); +}; + +module.exports.help = function() { + console.log('Usage: cordova build browser'); + console.log('Build will create the packaged app in \''+platformBuildDir+'\'.'); +}; diff --git a/platforms/browser/cordova/lib/check_reqs.js b/platforms/browser/cordova/lib/check_reqs.js new file mode 100644 index 0000000..720d7aa --- /dev/null +++ b/platforms/browser/cordova/lib/check_reqs.js @@ -0,0 +1,28 @@ +#!/usr/bin/env node + +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +//add methods as we determine what are the requirements + +var Q = require('q'); + +module.exports.run = function() { + return Q.resolve(); +}; diff --git a/platforms/browser/cordova/lib/clean.js b/platforms/browser/cordova/lib/clean.js new file mode 100644 index 0000000..2e4367d --- /dev/null +++ b/platforms/browser/cordova/lib/clean.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var fs = require('fs'), + shjs = require('shelljs'), + path = require('path'), + check_reqs = require('./check_reqs'), + platformBuildDir = path.join('platforms', 'browser', 'build'); + +exports.cleanProject = function(){ + + // Check that requirements are (stil) met + if (!check_reqs.run()) { + console.error('Please make sure you meet the software requirements in order to clean an browser cordova project'); + process.exit(2); + } + + console.log('Cleaning Browser project'); + try { + if (fs.existsSync(platformBuildDir)) { + shjs.rm('-r', platformBuildDir); + } + } + catch(err) { + console.log('could not remove '+platformBuildDir+' : '+err.message); + } +}; + diff --git a/platforms/browser/cordova/node_modules/adm-zip/README.md b/platforms/browser/cordova/node_modules/adm-zip/README.md new file mode 100644 index 0000000..dd94d47 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/README.md @@ -0,0 +1,64 @@ +# ADM-ZIP for NodeJS + +ADM-ZIP is a pure JavaScript implementation for zip data compression for [NodeJS](http://nodejs.org/). + +# Installation + +With [npm](http://npmjs.org) do: + + $ npm install adm-zip + +## What is it good for? +The library allows you to: + +* decompress zip files directly to disk or in memory buffers +* compress files and store them to disk in .zip format or in compressed buffers +* update content of/add new/delete files from an existing .zip + +# Dependencies +There are no other nodeJS libraries that ADM-ZIP is dependent of + +# Examples + +## Basic usage +```javascript + + var AdmZip = require('adm-zip'); + + // reading archives + var zip = new AdmZip("./my_file.zip"); + var zipEntries = zip.getEntries(); // an array of ZipEntry records + + zipEntries.forEach(function(zipEntry) { + console.log(zipEntry.toString()); // outputs zip entries information + if (zipEntry.entryName == "my_file.txt") { + console.log(zipEntry.data.toString('utf8')); + } + }); + // outputs the content of some_folder/my_file.txt + console.log(zip.readAsText("some_folder/my_file.txt")); + // extracts the specified file to the specified location + zip.extractEntryTo(/*entry name*/"some_folder/my_file.txt", /*target path*/"/home/me/tempfolder", /*maintainEntryPath*/false, /*overwrite*/true); + // extracts everything + zip.extractAllTo(/*target path*/"/home/me/zipcontent/", /*overwrite*/true); + + + // creating archives + var zip = new AdmZip(); + + // add file directly + zip.addFile("test.txt", new Buffer("inner content of the file"), "entry comment goes here"); + // add local file + zip.addLocalFile("/home/me/some_picture.png"); + // get everything as a buffer + var willSendthis = zip.toBuffer(); + // or write everything to disk + zip.writeZip(/*target file name*/"/home/me/files.zip"); + + + // ... more examples in the wiki +``` + +For more detailed information please check out the [wiki](https://github.com/cthackers/adm-zip/wiki). + +[![build status](https://secure.travis-ci.org/cthackers/adm-zip.png)](http://travis-ci.org/cthackers/adm-zip) diff --git a/platforms/browser/cordova/node_modules/adm-zip/adm-zip.js b/platforms/browser/cordova/node_modules/adm-zip/adm-zip.js new file mode 100644 index 0000000..9ba4bd0 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/adm-zip.js @@ -0,0 +1,475 @@ +var fs = require("fs"), + pth = require("path"); + +fs.existsSync = fs.existsSync || pth.existsSync; + +var ZipEntry = require("./zipEntry"), + ZipFile = require("./zipFile"), + Utils = require("./util"); + +module.exports = function(/*String*/input) { + var _zip = undefined, + _filename = ""; + + if (input && typeof input === "string") { // load zip file + if (fs.existsSync(input)) { + _filename = input; + _zip = new ZipFile(input, Utils.Constants.FILE); + } else { + throw Utils.Errors.INVALID_FILENAME; + } + } else if(input && Buffer.isBuffer(input)) { // load buffer + _zip = new ZipFile(input, Utils.Constants.BUFFER); + } else { // create new zip file + _zip = new ZipFile(null, Utils.Constants.NONE); + } + + function getEntry(/*Object*/entry) { + if (entry && _zip) { + var item; + // If entry was given as a file name + if (typeof entry === "string") + item = _zip.getEntry(entry); + // if entry was given as a ZipEntry object + if (typeof entry === "object" && entry.entryName != undefined && entry.header != undefined) + item = _zip.getEntry(entry.entryName); + + if (item) { + return item; + } + } + return null; + } + + return { + /** + * Extracts the given entry from the archive and returns the content as a Buffer object + * @param entry ZipEntry object or String with the full path of the entry + * + * @return Buffer or Null in case of error + */ + readFile : function(/*Object*/entry) { + var item = getEntry(entry); + return item && item.getData() || null; + }, + + /** + * Asynchronous readFile + * @param entry ZipEntry object or String with the full path of the entry + * @param callback + * + * @return Buffer or Null in case of error + */ + readFileAsync : function(/*Object*/entry, /*Function*/callback) { + var item = getEntry(entry); + if (item) { + item.getDataAsync(callback); + } else { + callback(null,"getEntry failed for:" + entry) + } + }, + + /** + * Extracts the given entry from the archive and returns the content as plain text in the given encoding + * @param entry ZipEntry object or String with the full path of the entry + * @param encoding Optional. If no encoding is specified utf8 is used + * + * @return String + */ + readAsText : function(/*Object*/entry, /*String - Optional*/encoding) { + var item = getEntry(entry); + if (item) { + var data = item.getData(); + if (data && data.length) { + return data.toString(encoding || "utf8"); + } + } + return ""; + }, + + /** + * Asynchronous readAsText + * @param entry ZipEntry object or String with the full path of the entry + * @param callback + * @param encoding Optional. If no encoding is specified utf8 is used + * + * @return String + */ + readAsTextAsync : function(/*Object*/entry, /*Function*/callback, /*String - Optional*/encoding) { + var item = getEntry(entry); + if (item) { + item.getDataAsync(function(data) { + if (data && data.length) { + callback(data.toString(encoding || "utf8")); + } else { + callback(""); + } + }) + } else { + callback(""); + } + }, + + /** + * Remove the entry from the file or the entry and all it's nested directories and files if the given entry is a directory + * + * @param entry + */ + deleteFile : function(/*Object*/entry) { // @TODO: test deleteFile + var item = getEntry(entry); + if (item) { + _zip.deleteEntry(item.entryName); + } + }, + + /** + * Adds a comment to the zip. The zip must be rewritten after adding the comment. + * + * @param comment + */ + addZipComment : function(/*String*/comment) { // @TODO: test addZipComment + _zip.comment = comment; + }, + + /** + * Returns the zip comment + * + * @return String + */ + getZipComment : function() { + return _zip.comment || ''; + }, + + /** + * Adds a comment to a specified zipEntry. The zip must be rewritten after adding the comment + * The comment cannot exceed 65535 characters in length + * + * @param entry + * @param comment + */ + addZipEntryComment : function(/*Object*/entry,/*String*/comment) { + var item = getEntry(entry); + if (item) { + item.comment = comment; + } + }, + + /** + * Returns the comment of the specified entry + * + * @param entry + * @return String + */ + getZipEntryComment : function(/*Object*/entry) { + var item = getEntry(entry); + if (item) { + return item.comment || ''; + } + return '' + }, + + /** + * Updates the content of an existing entry inside the archive. The zip must be rewritten after updating the content + * + * @param entry + * @param content + */ + updateFile : function(/*Object*/entry, /*Buffer*/content) { + var item = getEntry(entry); + if (item) { + item.setData(content); + } + }, + + /** + * Adds a file from the disk to the archive + * + * @param localPath + */ + addLocalFile : function(/*String*/localPath, /*String*/zipPath, /*String*/zipName) { + if (fs.existsSync(localPath)) { + if(zipPath){ + zipPath=zipPath.split("\\").join("/"); + if(zipPath.charAt(zipPath.length - 1) != "/"){ + zipPath += "/"; + } + }else{ + zipPath=""; + } + var p = localPath.split("\\").join("/").split("/").pop(); + + if(zipName){ + this.addFile(zipPath+zipName, fs.readFileSync(localPath), "", 0) + }else{ + this.addFile(zipPath+p, fs.readFileSync(localPath), "", 0) + } + } else { + throw Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath); + } + }, + + /** + * Adds a local directory and all its nested files and directories to the archive + * + * @param localPath + * @param zipPath optional path inside zip + * @param filter optional RegExp or Function if files match will + * be included. + */ + addLocalFolder : function(/*String*/localPath, /*String*/zipPath, /*RegExp|Function*/filter) { + if (filter === undefined) { + filter = function() { return true; }; + } else if (filter instanceof RegExp) { + filter = function(filter) { + return function(filename) { + return filter.test(filename); + } + }(filter); + } + + if(zipPath){ + zipPath=zipPath.split("\\").join("/"); + if(zipPath.charAt(zipPath.length - 1) != "/"){ + zipPath += "/"; + } + }else{ + zipPath=""; + } + localPath = localPath.split("\\").join("/"); //windows fix + localPath = pth.normalize(localPath); + if (localPath.charAt(localPath.length - 1) != "/") + localPath += "/"; + + if (fs.existsSync(localPath)) { + + var items = Utils.findFiles(localPath), + self = this; + + if (items.length) { + items.forEach(function(path) { + var p = path.split("\\").join("/").replace( new RegExp(localPath, 'i'), ""); //windows fix + if (filter(p)) { + if (p.charAt(p.length - 1) !== "/") { + self.addFile(zipPath+p, fs.readFileSync(path), "", 0) + } else { + self.addFile(zipPath+p, new Buffer(0), "", 0) + } + } + }); + } + } else { + throw Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath); + } + }, + + /** + * Allows you to create a entry (file or directory) in the zip file. + * If you want to create a directory the entryName must end in / and a null buffer should be provided. + * Comment and attributes are optional + * + * @param entryName + * @param content + * @param comment + * @param attr + */ + addFile : function(/*String*/entryName, /*Buffer*/content, /*String*/comment, /*Number*/attr) { + var entry = new ZipEntry(); + entry.entryName = entryName; + entry.comment = comment || ""; + entry.attr = attr || 438; //0666; + if (entry.isDirectory && content.length) { + // throw Utils.Errors.DIRECTORY_CONTENT_ERROR; + } + entry.setData(content); + _zip.setEntry(entry); + }, + + /** + * Returns an array of ZipEntry objects representing the files and folders inside the archive + * + * @return Array + */ + getEntries : function() { + if (_zip) { + return _zip.entries; + } else { + return []; + } + }, + + /** + * Returns a ZipEntry object representing the file or folder specified by ``name``. + * + * @param name + * @return ZipEntry + */ + getEntry : function(/*String*/name) { + return getEntry(name); + }, + + /** + * Extracts the given entry to the given targetPath + * If the entry is a directory inside the archive, the entire directory and it's subdirectories will be extracted + * + * @param entry ZipEntry object or String with the full path of the entry + * @param targetPath Target folder where to write the file + * @param maintainEntryPath If maintainEntryPath is true and the entry is inside a folder, the entry folder + * will be created in targetPath as well. Default is TRUE + * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. + * Default is FALSE + * + * @return Boolean + */ + extractEntryTo : function(/*Object*/entry, /*String*/targetPath, /*Boolean*/maintainEntryPath, /*Boolean*/overwrite) { + overwrite = overwrite || false; + maintainEntryPath = typeof maintainEntryPath == "undefined" ? true : maintainEntryPath; + + var item = getEntry(entry); + if (!item) { + throw Utils.Errors.NO_ENTRY; + } + + var target = pth.resolve(targetPath, maintainEntryPath ? item.entryName : pth.basename(item.entryName)); + + if (item.isDirectory) { + target = pth.resolve(target, ".."); + var children = _zip.getEntryChildren(item); + children.forEach(function(child) { + if (child.isDirectory) return; + var content = child.getData(); + if (!content) { + throw Utils.Errors.CANT_EXTRACT_FILE; + } + Utils.writeFileTo(pth.resolve(targetPath, maintainEntryPath ? child.entryName : child.entryName.substr(item.entryName.length)), content, overwrite); + }); + return true; + } + + var content = item.getData(); + if (!content) throw Utils.Errors.CANT_EXTRACT_FILE; + + if (fs.existsSync(target) && !overwrite) { + throw Utils.Errors.CANT_OVERRIDE; + } + Utils.writeFileTo(target, content, overwrite); + + return true; + }, + + /** + * Extracts the entire archive to the given location + * + * @param targetPath Target location + * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. + * Default is FALSE + */ + extractAllTo : function(/*String*/targetPath, /*Boolean*/overwrite) { + overwrite = overwrite || false; + if (!_zip) { + throw Utils.Errors.NO_ZIP; + } + + _zip.entries.forEach(function(entry) { + if (entry.isDirectory) { + Utils.makeDir(pth.resolve(targetPath, entry.entryName.toString())); + return; + } + var content = entry.getData(); + if (!content) { + throw Utils.Errors.CANT_EXTRACT_FILE + "2"; + } + Utils.writeFileTo(pth.resolve(targetPath, entry.entryName.toString()), content, overwrite); + }) + }, + + /** + * Asynchronous extractAllTo + * + * @param targetPath Target location + * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. + * Default is FALSE + * @param callback + */ + extractAllToAsync : function(/*String*/targetPath, /*Boolean*/overwrite, /*Function*/callback) { + overwrite = overwrite || false; + if (!_zip) { + callback(new Error(Utils.Errors.NO_ZIP)); + return; + } + + var entries = _zip.entries; + var i = entries.length; + entries.forEach(function(entry) { + if(i <= 0) return; // Had an error already + + if (entry.isDirectory) { + Utils.makeDir(pth.resolve(targetPath, entry.entryName.toString())); + if(--i == 0) + callback(undefined); + return; + } + entry.getDataAsync(function(content) { + if(i <= 0) return; + if (!content) { + i = 0; + callback(new Error(Utils.Errors.CANT_EXTRACT_FILE + "2")); + return; + } + Utils.writeFileToAsync(pth.resolve(targetPath, entry.entryName.toString()), content, overwrite, function(succ) { + if(i <= 0) return; + + if(!succ) { + i = 0; + callback(new Error('Unable to write')); + return; + } + + if(--i == 0) + callback(undefined); + }); + + }); + }) + }, + + /** + * Writes the newly created zip file to disk at the specified location or if a zip was opened and no ``targetFileName`` is provided, it will overwrite the opened zip + * + * @param targetFileName + * @param callback + */ + writeZip : function(/*String*/targetFileName, /*Function*/callback) { + if (arguments.length == 1) { + if (typeof targetFileName == "function") { + callback = targetFileName; + targetFileName = ""; + } + } + + if (!targetFileName && _filename) { + targetFileName = _filename; + } + if (!targetFileName) return; + + var zipData = _zip.compressToBuffer(); + if (zipData) { + var ok = Utils.writeFileTo(targetFileName, zipData, true); + if (typeof callback == 'function') callback(!ok? new Error("failed"): null, ""); + } + }, + + /** + * Returns the content of the entire zip file as a Buffer object + * + * @return Buffer + */ + toBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) { + this.valueOf = 2; + if (typeof onSuccess == "function") { + _zip.toAsyncBuffer(onSuccess,onFail,onItemStart,onItemEnd); + return null; + } + return _zip.compressToBuffer() + } + } +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/headers/entryHeader.js b/platforms/browser/cordova/node_modules/adm-zip/headers/entryHeader.js new file mode 100644 index 0000000..9a0e1bd --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/headers/entryHeader.js @@ -0,0 +1,261 @@ +var Utils = require("../util"), + Constants = Utils.Constants; + +/* The central directory file header */ +module.exports = function () { + var _verMade = 0x0A, + _version = 0x0A, + _flags = 0, + _method = 0, + _time = 0, + _crc = 0, + _compressedSize = 0, + _size = 0, + _fnameLen = 0, + _extraLen = 0, + + _comLen = 0, + _diskStart = 0, + _inattr = 0, + _attr = 0, + _offset = 0; + + var _dataHeader = {}; + + function setTime(val) { + var val = new Date(val); + _time = (val.getFullYear() - 1980 & 0x7f) << 25 // b09-16 years from 1980 + | (val.getMonth() + 1) << 21 // b05-08 month + | val.getDay() << 16 // b00-04 hour + + // 2 bytes time + | val.getHours() << 11 // b11-15 hour + | val.getMinutes() << 5 // b05-10 minute + | val.getSeconds() >> 1; // b00-04 seconds divided by 2 + } + + setTime(+new Date()); + + return { + get made () { return _verMade; }, + set made (val) { _verMade = val; }, + + get version () { return _version; }, + set version (val) { _version = val }, + + get flags () { return _flags }, + set flags (val) { _flags = val; }, + + get method () { return _method; }, + set method (val) { _method = val; }, + + get time () { return new Date( + ((_time >> 25) & 0x7f) + 1980, + ((_time >> 21) & 0x0f) - 1, + (_time >> 16) & 0x1f, + (_time >> 11) & 0x1f, + (_time >> 5) & 0x3f, + (_time & 0x1f) << 1 + ); + }, + set time (val) { + setTime(val); + }, + + get crc () { return _crc; }, + set crc (val) { _crc = val; }, + + get compressedSize () { return _compressedSize; }, + set compressedSize (val) { _compressedSize = val; }, + + get size () { return _size; }, + set size (val) { _size = val; }, + + get fileNameLength () { return _fnameLen; }, + set fileNameLength (val) { _fnameLen = val; }, + + get extraLength () { return _extraLen }, + set extraLength (val) { _extraLen = val; }, + + get commentLength () { return _comLen }, + set commentLength (val) { _comLen = val }, + + get diskNumStart () { return _diskStart }, + set diskNumStart (val) { _diskStart = val }, + + get inAttr () { return _inattr }, + set inAttr (val) { _inattr = val }, + + get attr () { return _attr }, + set attr (val) { _attr = val }, + + get offset () { return _offset }, + set offset (val) { _offset = val }, + + get encripted () { return (_flags & 1) == 1 }, + + get entryHeaderSize () { + return Constants.CENHDR + _fnameLen + _extraLen + _comLen; + }, + + get realDataOffset () { + return _offset + Constants.LOCHDR + _dataHeader.fnameLen + _dataHeader.extraLen; + }, + + get dataHeader () { + return _dataHeader; + }, + + loadDataHeaderFromBinary : function(/*Buffer*/input) { + var data = input.slice(_offset, _offset + Constants.LOCHDR); + // 30 bytes and should start with "PK\003\004" + if (data.readUInt32LE(0) != Constants.LOCSIG) { + throw Utils.Errors.INVALID_LOC; + } + _dataHeader = { + // version needed to extract + version : data.readUInt16LE(Constants.LOCVER), + // general purpose bit flag + flags : data.readUInt16LE(Constants.LOCFLG), + // compression method + method : data.readUInt16LE(Constants.LOCHOW), + // modification time (2 bytes time, 2 bytes date) + time : data.readUInt32LE(Constants.LOCTIM), + // uncompressed file crc-32 value + crc : data.readUInt32LE(Constants.LOCCRC), + // compressed size + compressedSize : data.readUInt32LE(Constants.LOCSIZ), + // uncompressed size + size : data.readUInt32LE(Constants.LOCLEN), + // filename length + fnameLen : data.readUInt16LE(Constants.LOCNAM), + // extra field length + extraLen : data.readUInt16LE(Constants.LOCEXT) + } + }, + + loadFromBinary : function(/*Buffer*/data) { + // data should be 46 bytes and start with "PK 01 02" + if (data.length != Constants.CENHDR || data.readUInt32LE(0) != Constants.CENSIG) { + throw Utils.Errors.INVALID_CEN; + } + // version made by + _verMade = data.readUInt16LE(Constants.CENVEM); + // version needed to extract + _version = data.readUInt16LE(Constants.CENVER); + // encrypt, decrypt flags + _flags = data.readUInt16LE(Constants.CENFLG); + // compression method + _method = data.readUInt16LE(Constants.CENHOW); + // modification time (2 bytes time, 2 bytes date) + _time = data.readUInt32LE(Constants.CENTIM); + // uncompressed file crc-32 value + _crc = data.readUInt32LE(Constants.CENCRC); + // compressed size + _compressedSize = data.readUInt32LE(Constants.CENSIZ); + // uncompressed size + _size = data.readUInt32LE(Constants.CENLEN); + // filename length + _fnameLen = data.readUInt16LE(Constants.CENNAM); + // extra field length + _extraLen = data.readUInt16LE(Constants.CENEXT); + // file comment length + _comLen = data.readUInt16LE(Constants.CENCOM); + // volume number start + _diskStart = data.readUInt16LE(Constants.CENDSK); + // internal file attributes + _inattr = data.readUInt16LE(Constants.CENATT); + // external file attributes + _attr = data.readUInt32LE(Constants.CENATX); + // LOC header offset + _offset = data.readUInt32LE(Constants.CENOFF); + }, + + dataHeaderToBinary : function() { + // LOC header size (30 bytes) + var data = new Buffer(Constants.LOCHDR); + // "PK\003\004" + data.writeUInt32LE(Constants.LOCSIG, 0); + // version needed to extract + data.writeUInt16LE(_version, Constants.LOCVER); + // general purpose bit flag + data.writeUInt16LE(_flags, Constants.LOCFLG); + // compression method + data.writeUInt16LE(_method, Constants.LOCHOW); + // modification time (2 bytes time, 2 bytes date) + data.writeUInt32LE(_time, Constants.LOCTIM); + // uncompressed file crc-32 value + data.writeUInt32LE(_crc, Constants.LOCCRC); + // compressed size + data.writeUInt32LE(_compressedSize, Constants.LOCSIZ); + // uncompressed size + data.writeUInt32LE(_size, Constants.LOCLEN); + // filename length + data.writeUInt16LE(_fnameLen, Constants.LOCNAM); + // extra field length + data.writeUInt16LE(_extraLen, Constants.LOCEXT); + return data; + }, + + entryHeaderToBinary : function() { + // CEN header size (46 bytes) + var data = new Buffer(Constants.CENHDR + _fnameLen + _extraLen + _comLen); + // "PK\001\002" + data.writeUInt32LE(Constants.CENSIG, 0); + // version made by + data.writeUInt16LE(_verMade, Constants.CENVEM); + // version needed to extract + data.writeUInt16LE(_version, Constants.CENVER); + // encrypt, decrypt flags + data.writeUInt16LE(_flags, Constants.CENFLG); + // compression method + data.writeUInt16LE(_method, Constants.CENHOW); + // modification time (2 bytes time, 2 bytes date) + data.writeUInt32LE(_time, Constants.CENTIM); + // uncompressed file crc-32 value + data.writeInt32LE(_crc, Constants.CENCRC, true); + // compressed size + data.writeUInt32LE(_compressedSize, Constants.CENSIZ); + // uncompressed size + data.writeUInt32LE(_size, Constants.CENLEN); + // filename length + data.writeUInt16LE(_fnameLen, Constants.CENNAM); + // extra field length + data.writeUInt16LE(_extraLen, Constants.CENEXT); + // file comment length + data.writeUInt16LE(_comLen, Constants.CENCOM); + // volume number start + data.writeUInt16LE(_diskStart, Constants.CENDSK); + // internal file attributes + data.writeUInt16LE(_inattr, Constants.CENATT); + // external file attributes + data.writeUInt32LE(_attr, Constants.CENATX); + // LOC header offset + data.writeUInt32LE(_offset, Constants.CENOFF); + // fill all with + data.fill(0x00, Constants.CENHDR); + return data; + }, + + toString : function() { + return '{\n' + + '\t"made" : ' + _verMade + ",\n" + + '\t"version" : ' + _version + ",\n" + + '\t"flags" : ' + _flags + ",\n" + + '\t"method" : ' + Utils.methodToString(_method) + ",\n" + + '\t"time" : ' + _time + ",\n" + + '\t"crc" : 0x' + _crc.toString(16).toUpperCase() + ",\n" + + '\t"compressedSize" : ' + _compressedSize + " bytes,\n" + + '\t"size" : ' + _size + " bytes,\n" + + '\t"fileNameLength" : ' + _fnameLen + ",\n" + + '\t"extraLength" : ' + _extraLen + " bytes,\n" + + '\t"commentLength" : ' + _comLen + " bytes,\n" + + '\t"diskNumStart" : ' + _diskStart + ",\n" + + '\t"inAttr" : ' + _inattr + ",\n" + + '\t"attr" : ' + _attr + ",\n" + + '\t"offset" : ' + _offset + ",\n" + + '\t"entryHeaderSize" : ' + (Constants.CENHDR + _fnameLen + _extraLen + _comLen) + " bytes\n" + + '}'; + } + } +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/headers/index.js b/platforms/browser/cordova/node_modules/adm-zip/headers/index.js new file mode 100644 index 0000000..b54a722 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/headers/index.js @@ -0,0 +1,2 @@ +exports.EntryHeader = require("./entryHeader"); +exports.MainHeader = require("./mainHeader"); diff --git a/platforms/browser/cordova/node_modules/adm-zip/headers/mainHeader.js b/platforms/browser/cordova/node_modules/adm-zip/headers/mainHeader.js new file mode 100644 index 0000000..de8ae1a --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/headers/mainHeader.js @@ -0,0 +1,80 @@ +var Utils = require("../util"), + Constants = Utils.Constants; + +/* The entries in the end of central directory */ +module.exports = function () { + var _volumeEntries = 0, + _totalEntries = 0, + _size = 0, + _offset = 0, + _commentLength = 0; + + return { + get diskEntries () { return _volumeEntries }, + set diskEntries (/*Number*/val) { _volumeEntries = _totalEntries = val; }, + + get totalEntries () { return _totalEntries }, + set totalEntries (/*Number*/val) { _totalEntries = _volumeEntries = val; }, + + get size () { return _size }, + set size (/*Number*/val) { _size = val; }, + + get offset () { return _offset }, + set offset (/*Number*/val) { _offset = val; }, + + get commentLength () { return _commentLength }, + set commentLength (/*Number*/val) { _commentLength = val; }, + + get mainHeaderSize () { + return Constants.ENDHDR + _commentLength; + }, + + loadFromBinary : function(/*Buffer*/data) { + // data should be 22 bytes and start with "PK 05 06" + if (data.length != Constants.ENDHDR || data.readUInt32LE(0) != Constants.ENDSIG) + throw Utils.Errors.INVALID_END; + + // number of entries on this volume + _volumeEntries = data.readUInt16LE(Constants.ENDSUB); + // total number of entries + _totalEntries = data.readUInt16LE(Constants.ENDTOT); + // central directory size in bytes + _size = data.readUInt32LE(Constants.ENDSIZ); + // offset of first CEN header + _offset = data.readUInt32LE(Constants.ENDOFF); + // zip file comment length + _commentLength = data.readUInt16LE(Constants.ENDCOM); + }, + + toBinary : function() { + var b = new Buffer(Constants.ENDHDR + _commentLength); + // "PK 05 06" signature + b.writeUInt32LE(Constants.ENDSIG, 0); + b.writeUInt32LE(0, 4); + // number of entries on this volume + b.writeUInt16LE(_volumeEntries, Constants.ENDSUB); + // total number of entries + b.writeUInt16LE(_totalEntries, Constants.ENDTOT); + // central directory size in bytes + b.writeUInt32LE(_size, Constants.ENDSIZ); + // offset of first CEN header + b.writeUInt32LE(_offset, Constants.ENDOFF); + // zip file comment length + b.writeUInt16LE(_commentLength, Constants.ENDCOM); + // fill comment memory with spaces so no garbage is left there + b.fill(" ", Constants.ENDHDR); + + return b; + }, + + toString : function() { + return '{\n' + + '\t"diskEntries" : ' + _volumeEntries + ",\n" + + '\t"totalEntries" : ' + _totalEntries + ",\n" + + '\t"size" : ' + _size + " bytes,\n" + + '\t"offset" : 0x' + _offset.toString(16).toUpperCase() + ",\n" + + '\t"commentLength" : 0x' + _commentLength + "\n" + + '}'; + } + } +}; \ No newline at end of file diff --git a/platforms/browser/cordova/node_modules/adm-zip/methods/deflater.js b/platforms/browser/cordova/node_modules/adm-zip/methods/deflater.js new file mode 100644 index 0000000..34ef297 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/methods/deflater.js @@ -0,0 +1,1578 @@ +/* + * $Id: rawdeflate.js,v 0.5 2013/04/09 14:25:38 dankogai Exp dankogai $ + * + * GNU General Public License, version 2 (GPL-2.0) + * http://opensource.org/licenses/GPL-2.0 + * Original: + * http://www.onicos.com/staff/iz/amuse/javascript/expert/deflate.txt + */ +function JSDeflater(/*inbuff*/inbuf) { + + /* Copyright (C) 1999 Masanao Izumo + * Version: 1.0.1 + * LastModified: Dec 25 1999 + */ + + var WSIZE = 32768, // Sliding Window size + zip_STORED_BLOCK = 0, + zip_STATIC_TREES = 1, + zip_DYN_TREES = 2, + zip_DEFAULT_LEVEL = 6, + zip_FULL_SEARCH = true, + zip_INBUFSIZ = 32768, // Input buffer size + zip_INBUF_EXTRA = 64, // Extra buffer + zip_OUTBUFSIZ = 1024 * 8, + zip_window_size = 2 * WSIZE, + MIN_MATCH = 3, + MAX_MATCH = 258, + zip_BITS = 16, + LIT_BUFSIZE = 0x2000, + zip_HASH_BITS = 13, + zip_DIST_BUFSIZE = LIT_BUFSIZE, + zip_HASH_SIZE = 1 << zip_HASH_BITS, + zip_HASH_MASK = zip_HASH_SIZE - 1, + zip_WMASK = WSIZE - 1, + zip_NIL = 0, // Tail of hash chains + zip_TOO_FAR = 4096, + zip_MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1, + zip_MAX_DIST = WSIZE - zip_MIN_LOOKAHEAD, + zip_SMALLEST = 1, + zip_MAX_BITS = 15, + zip_MAX_BL_BITS = 7, + zip_LENGTH_CODES = 29, + zip_LITERALS = 256, + zip_END_BLOCK = 256, + zip_L_CODES = zip_LITERALS + 1 + zip_LENGTH_CODES, + zip_D_CODES = 30, + zip_BL_CODES = 19, + zip_REP_3_6 = 16, + zip_REPZ_3_10 = 17, + zip_REPZ_11_138 = 18, + zip_HEAP_SIZE = 2 * zip_L_CODES + 1, + zip_H_SHIFT = parseInt((zip_HASH_BITS + MIN_MATCH - 1) / MIN_MATCH); + + var zip_free_queue, zip_qhead, zip_qtail, zip_initflag, zip_outbuf = null, zip_outcnt, zip_outoff, zip_complete, + zip_window, zip_d_buf, zip_l_buf, zip_prev, zip_bi_buf, zip_bi_valid, zip_block_start, zip_ins_h, zip_hash_head, + zip_prev_match, zip_match_available, zip_match_length, zip_prev_length, zip_strstart, zip_match_start, zip_eofile, + zip_lookahead, zip_max_chain_length, zip_max_lazy_match, zip_compr_level, zip_good_match, zip_nice_match, + zip_dyn_ltree, zip_dyn_dtree, zip_static_ltree, zip_static_dtree, zip_bl_tree, zip_l_desc, zip_d_desc, zip_bl_desc, + zip_bl_count, zip_heap, zip_heap_len, zip_heap_max, zip_depth, zip_length_code, zip_dist_code, zip_base_length, + zip_base_dist, zip_flag_buf, zip_last_lit, zip_last_dist, zip_last_flags, zip_flags, zip_flag_bit, zip_opt_len, + zip_static_len, zip_deflate_data, zip_deflate_pos; + + var zip_DeflateCT = function () { + this.fc = 0; // frequency count or bit string + this.dl = 0; // father node in Huffman tree or length of bit string + }; + + var zip_DeflateTreeDesc = function () { + this.dyn_tree = null; // the dynamic tree + this.static_tree = null; // corresponding static tree or NULL + this.extra_bits = null; // extra bits for each code or NULL + this.extra_base = 0; // base index for extra_bits + this.elems = 0; // max number of elements in the tree + this.max_length = 0; // max bit length for the codes + this.max_code = 0; // largest code with non zero frequency + }; + + /* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + var zip_DeflateConfiguration = function (a, b, c, d) { + this.good_length = a; // reduce lazy search above this match length + this.max_lazy = b; // do not perform lazy search above this match length + this.nice_length = c; // quit search above this match length + this.max_chain = d; + }; + + var zip_DeflateBuffer = function () { + this.next = null; + this.len = 0; + this.ptr = new Array(zip_OUTBUFSIZ); + this.off = 0; + }; + + /* constant tables */ + var zip_extra_lbits = new Array( + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0); + var zip_extra_dbits = new Array( + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13); + var zip_extra_blbits = new Array( + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7); + var zip_bl_order = new Array( + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); + var zip_configuration_table = new Array( + new zip_DeflateConfiguration(0, 0, 0, 0), + new zip_DeflateConfiguration(4, 4, 8, 4), + new zip_DeflateConfiguration(4, 5, 16, 8), + new zip_DeflateConfiguration(4, 6, 32, 32), + new zip_DeflateConfiguration(4, 4, 16, 16), + new zip_DeflateConfiguration(8, 16, 32, 32), + new zip_DeflateConfiguration(8, 16, 128, 128), + new zip_DeflateConfiguration(8, 32, 128, 256), + new zip_DeflateConfiguration(32, 128, 258, 1024), + new zip_DeflateConfiguration(32, 258, 258, 4096)); + + + /* routines (deflate) */ + + var zip_deflate_start = function (level) { + var i; + + if (!level) + level = zip_DEFAULT_LEVEL; + else if (level < 1) + level = 1; + else if (level > 9) + level = 9; + + zip_compr_level = level; + zip_initflag = false; + zip_eofile = false; + if (zip_outbuf != null) + return; + + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = new Array(zip_OUTBUFSIZ); + zip_window = new Array(zip_window_size); + zip_d_buf = new Array(zip_DIST_BUFSIZE); + zip_l_buf = new Array(zip_INBUFSIZ + zip_INBUF_EXTRA); + zip_prev = new Array(1 << zip_BITS); + zip_dyn_ltree = new Array(zip_HEAP_SIZE); + for (i = 0; i < zip_HEAP_SIZE; i++) zip_dyn_ltree[i] = new zip_DeflateCT(); + zip_dyn_dtree = new Array(2 * zip_D_CODES + 1); + for (i = 0; i < 2 * zip_D_CODES + 1; i++) zip_dyn_dtree[i] = new zip_DeflateCT(); + zip_static_ltree = new Array(zip_L_CODES + 2); + for (i = 0; i < zip_L_CODES + 2; i++) zip_static_ltree[i] = new zip_DeflateCT(); + zip_static_dtree = new Array(zip_D_CODES); + for (i = 0; i < zip_D_CODES; i++) zip_static_dtree[i] = new zip_DeflateCT(); + zip_bl_tree = new Array(2 * zip_BL_CODES + 1); + for (i = 0; i < 2 * zip_BL_CODES + 1; i++) zip_bl_tree[i] = new zip_DeflateCT(); + zip_l_desc = new zip_DeflateTreeDesc(); + zip_d_desc = new zip_DeflateTreeDesc(); + zip_bl_desc = new zip_DeflateTreeDesc(); + zip_bl_count = new Array(zip_MAX_BITS + 1); + zip_heap = new Array(2 * zip_L_CODES + 1); + zip_depth = new Array(2 * zip_L_CODES + 1); + zip_length_code = new Array(MAX_MATCH - MIN_MATCH + 1); + zip_dist_code = new Array(512); + zip_base_length = new Array(zip_LENGTH_CODES); + zip_base_dist = new Array(zip_D_CODES); + zip_flag_buf = new Array(parseInt(LIT_BUFSIZE / 8)); + }; + + var zip_deflate_end = function () { + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = null; + zip_window = null; + zip_d_buf = null; + zip_l_buf = null; + zip_prev = null; + zip_dyn_ltree = null; + zip_dyn_dtree = null; + zip_static_ltree = null; + zip_static_dtree = null; + zip_bl_tree = null; + zip_l_desc = null; + zip_d_desc = null; + zip_bl_desc = null; + zip_bl_count = null; + zip_heap = null; + zip_depth = null; + zip_length_code = null; + zip_dist_code = null; + zip_base_length = null; + zip_base_dist = null; + zip_flag_buf = null; + }; + + var zip_reuse_queue = function (p) { + p.next = zip_free_queue; + zip_free_queue = p; + }; + + var zip_new_queue = function () { + var p; + + if (zip_free_queue != null) { + p = zip_free_queue; + zip_free_queue = zip_free_queue.next; + } + else + p = new zip_DeflateBuffer(); + p.next = null; + p.len = p.off = 0; + + return p; + }; + + var zip_head1 = function (i) { + return zip_prev[WSIZE + i]; + }; + + var zip_head2 = function (i, val) { + return zip_prev[WSIZE + i] = val; + }; + + /* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ + var zip_put_byte = function (c) { + zip_outbuf[zip_outoff + zip_outcnt++] = c; + if (zip_outoff + zip_outcnt == zip_OUTBUFSIZ) + zip_qoutbuf(); + }; + + /* Output a 16 bit value, lsb first */ + var zip_put_short = function (w) { + w &= 0xffff; + if (zip_outoff + zip_outcnt < zip_OUTBUFSIZ - 2) { + zip_outbuf[zip_outoff + zip_outcnt++] = (w & 0xff); + zip_outbuf[zip_outoff + zip_outcnt++] = (w >>> 8); + } else { + zip_put_byte(w & 0xff); + zip_put_byte(w >>> 8); + } + }; + + /* ========================================================================== + * Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ + var zip_INSERT_STRING = function () { + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) + ^ (zip_window[zip_strstart + MIN_MATCH - 1] & 0xff)) + & zip_HASH_MASK; + zip_hash_head = zip_head1(zip_ins_h); + zip_prev[zip_strstart & zip_WMASK] = zip_hash_head; + zip_head2(zip_ins_h, zip_strstart); + }; + + /* Send a code of the given tree. c and tree must not have side effects */ + var zip_SEND_CODE = function (c, tree) { + zip_send_bits(tree[c].fc, tree[c].dl); + }; + + /* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + var zip_D_CODE = function (dist) { + return (dist < 256 ? zip_dist_code[dist] + : zip_dist_code[256 + (dist >> 7)]) & 0xff; + }; + + /* ========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ + var zip_SMALLER = function (tree, n, m) { + return tree[n].fc < tree[m].fc || + (tree[n].fc == tree[m].fc && zip_depth[n] <= zip_depth[m]); + }; + + /* ========================================================================== + * read string data + */ + var zip_read_buff = function (buff, offset, n) { + var i; + for (i = 0; i < n && zip_deflate_pos < zip_deflate_data.length; i++) + buff[offset + i] = + zip_deflate_data[zip_deflate_pos++] & 0xff; + return i; + }; + + /* ========================================================================== + * Initialize the "longest match" routines for a new file + */ + var zip_lm_init = function () { + var j; + + /* Initialize the hash table. */ + for (j = 0; j < zip_HASH_SIZE; j++) + zip_prev[WSIZE + j] = 0; + zip_max_lazy_match = zip_configuration_table[zip_compr_level].max_lazy; + zip_good_match = zip_configuration_table[zip_compr_level].good_length; + if (!zip_FULL_SEARCH) + zip_nice_match = zip_configuration_table[zip_compr_level].nice_length; + zip_max_chain_length = zip_configuration_table[zip_compr_level].max_chain; + + zip_strstart = 0; + zip_block_start = 0; + + zip_lookahead = zip_read_buff(zip_window, 0, 2 * WSIZE); + if (zip_lookahead <= 0) { + zip_eofile = true; + zip_lookahead = 0; + return; + } + zip_eofile = false; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ + zip_ins_h = 0; + for (j = 0; j < MIN_MATCH - 1; j++) { + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[j] & 0xff)) & zip_HASH_MASK; + } + }; + + /* ========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ + var zip_longest_match = function (cur_match) { + var chain_length = zip_max_chain_length; // max hash chain length + var scanp = zip_strstart; // current string + var matchp; // matched string + var len; // length of current match + var best_len = zip_prev_length; // best match length so far + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + var limit = (zip_strstart > zip_MAX_DIST ? zip_strstart - zip_MAX_DIST : zip_NIL); + + var strendp = zip_strstart + MAX_MATCH; + var scan_end1 = zip_window[scanp + best_len - 1]; + var scan_end = zip_window[scanp + best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (zip_prev_length >= zip_good_match) + chain_length >>= 2; + + do { + matchp = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (zip_window[matchp + best_len] != scan_end || + zip_window[matchp + best_len - 1] != scan_end1 || + zip_window[matchp] != zip_window[scanp] || + zip_window[++matchp] != zip_window[scanp + 1]) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scanp += 2; + matchp++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + scanp < strendp); + + len = MAX_MATCH - (strendp - scanp); + scanp = strendp - MAX_MATCH; + + if (len > best_len) { + zip_match_start = cur_match; + best_len = len; + if (zip_FULL_SEARCH) { + if (len >= MAX_MATCH) break; + } else { + if (len >= zip_nice_match) break; + } + + scan_end1 = zip_window[scanp + best_len - 1]; + scan_end = zip_window[scanp + best_len]; + } + } while ((cur_match = zip_prev[cur_match & zip_WMASK]) > limit + && --chain_length != 0); + + return best_len; + }; + + /* ========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ + var zip_fill_window = function () { + var n, m; + + // Amount of free space at the end of the window. + var more = zip_window_size - zip_lookahead - zip_strstart; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == -1) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (zip_strstart >= WSIZE + zip_MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + for (n = 0; n < WSIZE; n++) + zip_window[n] = zip_window[n + WSIZE]; + + zip_match_start -= WSIZE; + zip_strstart -= WSIZE; + /* we now have strstart >= MAX_DIST: */ + zip_block_start -= WSIZE; + + for (n = 0; n < zip_HASH_SIZE; n++) { + m = zip_head1(n); + zip_head2(n, m >= WSIZE ? m - WSIZE : zip_NIL); + } + for (n = 0; n < WSIZE; n++) { + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + m = zip_prev[n]; + zip_prev[n] = (m >= WSIZE ? m - WSIZE : zip_NIL); + } + more += WSIZE; + } + // At this point, more >= 2 + if (!zip_eofile) { + n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more); + if (n <= 0) + zip_eofile = true; + else + zip_lookahead += n; + } + }; + + /* ========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluationof matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ + var zip_deflate_fast = function () { + while (zip_lookahead != 0 && zip_qhead == null) { + var flush; // set if current block must be flushed + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + zip_INSERT_STRING(); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (zip_hash_head != zip_NIL && + zip_strstart - zip_hash_head <= zip_MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + zip_match_length = zip_longest_match(zip_hash_head); + /* longest_match() sets match_start */ + if (zip_match_length > zip_lookahead) + zip_match_length = zip_lookahead; + } + if (zip_match_length >= MIN_MATCH) { + flush = zip_ct_tally(zip_strstart - zip_match_start, + zip_match_length - MIN_MATCH); + zip_lookahead -= zip_match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (zip_match_length <= zip_max_lazy_match) { + zip_match_length--; // string at strstart already in hash table + do { + zip_strstart++; + zip_INSERT_STRING(); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since + * the next lookahead bytes will be emitted as literals. + */ + } while (--zip_match_length != 0); + zip_strstart++; + } else { + zip_strstart += zip_match_length; + zip_match_length = 0; + zip_ins_h = zip_window[zip_strstart] & 0xff; + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[zip_strstart + 1] & 0xff)) & zip_HASH_MASK; + } + } else { + /* No match, output a literal byte */ + flush = zip_ct_tally(0, zip_window[zip_strstart] & 0xff); + zip_lookahead--; + zip_strstart++; + } + if (flush) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + } + }; + + var zip_deflate_better = function () { + /* Process the input block. */ + while (zip_lookahead != 0 && zip_qhead == null) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + zip_INSERT_STRING(); + + /* Find the longest match, discarding those <= prev_length. + */ + zip_prev_length = zip_match_length; + zip_prev_match = zip_match_start; + zip_match_length = MIN_MATCH - 1; + + if (zip_hash_head != zip_NIL && + zip_prev_length < zip_max_lazy_match && + zip_strstart - zip_hash_head <= zip_MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + zip_match_length = zip_longest_match(zip_hash_head); + /* longest_match() sets match_start */ + if (zip_match_length > zip_lookahead) + zip_match_length = zip_lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (zip_match_length == MIN_MATCH && + zip_strstart - zip_match_start > zip_TOO_FAR) { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + zip_match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (zip_prev_length >= MIN_MATCH && + zip_match_length <= zip_prev_length) { + var flush; // set if current block must be flushed + flush = zip_ct_tally(zip_strstart - 1 - zip_prev_match, + zip_prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + zip_lookahead -= zip_prev_length - 1; + zip_prev_length -= 2; + do { + zip_strstart++; + zip_INSERT_STRING(); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--zip_prev_length != 0); + zip_match_available = 0; + zip_match_length = MIN_MATCH - 1; + zip_strstart++; + if (flush) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + } else if (zip_match_available != 0) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff)) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + zip_strstart++; + zip_lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + zip_match_available = 1; + zip_strstart++; + zip_lookahead--; + } + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + } + }; + + var zip_init_deflate = function () { + if (zip_eofile) + return; + zip_bi_buf = 0; + zip_bi_valid = 0; + zip_ct_init(); + zip_lm_init(); + + zip_qhead = null; + zip_outcnt = 0; + zip_outoff = 0; + zip_match_available = 0; + + if (zip_compr_level <= 3) { + zip_prev_length = MIN_MATCH - 1; + zip_match_length = 0; + } + else { + zip_match_length = MIN_MATCH - 1; + zip_match_available = 0; + zip_match_available = 0; + } + + zip_complete = false; + }; + + /* ========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ + var zip_deflate_internal = function (buff, off, buff_size) { + var n; + + if (!zip_initflag) { + zip_init_deflate(); + zip_initflag = true; + if (zip_lookahead == 0) { // empty + zip_complete = true; + return 0; + } + } + + if ((n = zip_qcopy(buff, off, buff_size)) == buff_size) + return buff_size; + + if (zip_complete) + return n; + + if (zip_compr_level <= 3) // optimized for speed + zip_deflate_fast(); + else + zip_deflate_better(); + if (zip_lookahead == 0) { + if (zip_match_available != 0) + zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff); + zip_flush_block(1); + zip_complete = true; + } + return n + zip_qcopy(buff, n + off, buff_size - n); + }; + + var zip_qcopy = function (buff, off, buff_size) { + var n, i, j; + + n = 0; + while (zip_qhead != null && n < buff_size) { + i = buff_size - n; + if (i > zip_qhead.len) + i = zip_qhead.len; + for (j = 0; j < i; j++) + buff[off + n + j] = zip_qhead.ptr[zip_qhead.off + j]; + + zip_qhead.off += i; + zip_qhead.len -= i; + n += i; + if (zip_qhead.len == 0) { + var p; + p = zip_qhead; + zip_qhead = zip_qhead.next; + zip_reuse_queue(p); + } + } + + if (n == buff_size) + return n; + + if (zip_outoff < zip_outcnt) { + i = buff_size - n; + if (i > zip_outcnt - zip_outoff) + i = zip_outcnt - zip_outoff; + // System.arraycopy(outbuf, outoff, buff, off + n, i); + for (j = 0; j < i; j++) + buff[off + n + j] = zip_outbuf[zip_outoff + j]; + zip_outoff += i; + n += i; + if (zip_outcnt == zip_outoff) + zip_outcnt = zip_outoff = 0; + } + return n; + }; + + /* ========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ + var zip_ct_init = function () { + var n; // iterates over tree elements + var bits; // bit counter + var length; // length value + var code; // code value + var dist; // distance index + + if (zip_static_dtree[0].dl != 0) return; // ct_init already called + + zip_l_desc.dyn_tree = zip_dyn_ltree; + zip_l_desc.static_tree = zip_static_ltree; + zip_l_desc.extra_bits = zip_extra_lbits; + zip_l_desc.extra_base = zip_LITERALS + 1; + zip_l_desc.elems = zip_L_CODES; + zip_l_desc.max_length = zip_MAX_BITS; + zip_l_desc.max_code = 0; + + zip_d_desc.dyn_tree = zip_dyn_dtree; + zip_d_desc.static_tree = zip_static_dtree; + zip_d_desc.extra_bits = zip_extra_dbits; + zip_d_desc.extra_base = 0; + zip_d_desc.elems = zip_D_CODES; + zip_d_desc.max_length = zip_MAX_BITS; + zip_d_desc.max_code = 0; + + zip_bl_desc.dyn_tree = zip_bl_tree; + zip_bl_desc.static_tree = null; + zip_bl_desc.extra_bits = zip_extra_blbits; + zip_bl_desc.extra_base = 0; + zip_bl_desc.elems = zip_BL_CODES; + zip_bl_desc.max_length = zip_MAX_BL_BITS; + zip_bl_desc.max_code = 0; + + // Initialize the mapping length (0..255) -> length code (0..28) + length = 0; + for (code = 0; code < zip_LENGTH_CODES - 1; code++) { + zip_base_length[code] = length; + for (n = 0; n < (1 << zip_extra_lbits[code]); n++) + zip_length_code[length++] = code; + } + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + zip_length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + zip_base_dist[code] = dist; + for (n = 0; n < (1 << zip_extra_dbits[code]); n++) { + zip_dist_code[dist++] = code; + } + } + dist >>= 7; // from now on, all distances are divided by 128 + for (; code < zip_D_CODES; code++) { + zip_base_dist[code] = dist << 7; + for (n = 0; n < (1 << (zip_extra_dbits[code] - 7)); n++) + zip_dist_code[256 + dist++] = code; + } + // Construct the codes of the static literal tree + for (bits = 0; bits <= zip_MAX_BITS; bits++) + zip_bl_count[bits] = 0; + n = 0; + while (n <= 143) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++; + } + while (n <= 255) { + zip_static_ltree[n++].dl = 9; + zip_bl_count[9]++; + } + while (n <= 279) { + zip_static_ltree[n++].dl = 7; + zip_bl_count[7]++; + } + while (n <= 287) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + zip_gen_codes(zip_static_ltree, zip_L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < zip_D_CODES; n++) { + zip_static_dtree[n].dl = 5; + zip_static_dtree[n].fc = zip_bi_reverse(n, 5); + } + + // Initialize the first block of the first file: + zip_init_block(); + }; + + /* ========================================================================== + * Initialize a new block. + */ + var zip_init_block = function () { + var n; // iterates over tree elements + + // Initialize the trees. + for (n = 0; n < zip_L_CODES; n++) zip_dyn_ltree[n].fc = 0; + for (n = 0; n < zip_D_CODES; n++) zip_dyn_dtree[n].fc = 0; + for (n = 0; n < zip_BL_CODES; n++) zip_bl_tree[n].fc = 0; + + zip_dyn_ltree[zip_END_BLOCK].fc = 1; + zip_opt_len = zip_static_len = 0; + zip_last_lit = zip_last_dist = zip_last_flags = 0; + zip_flags = 0; + zip_flag_bit = 1; + }; + + /* ========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ + var zip_pqdownheap = function (tree, // the tree to restore + k) { // node to move down + var v = zip_heap[k]; + var j = k << 1; // left son of k + + while (j <= zip_heap_len) { + // Set j to the smallest of the two sons: + if (j < zip_heap_len && + zip_SMALLER(tree, zip_heap[j + 1], zip_heap[j])) + j++; + + // Exit if v is smaller than both sons + if (zip_SMALLER(tree, v, zip_heap[j])) + break; + + // Exchange v with the smallest son + zip_heap[k] = zip_heap[j]; + k = j; + + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + zip_heap[k] = v; + }; + + /* ========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ + var zip_gen_bitlen = function (desc) { // the tree descriptor + var tree = desc.dyn_tree; + var extra = desc.extra_bits; + var base = desc.extra_base; + var max_code = desc.max_code; + var max_length = desc.max_length; + var stree = desc.static_tree; + var h; // heap index + var n, m; // iterate over the tree elements + var bits; // bit length + var xbits; // extra bits + var f; // frequency + var overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= zip_MAX_BITS; bits++) + zip_bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[zip_heap[zip_heap_max]].dl = 0; // root of the heap + + for (h = zip_heap_max + 1; h < zip_HEAP_SIZE; h++) { + n = zip_heap[h]; + bits = tree[tree[n].dl].dl + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n].dl = bits; + // We overwrite tree[n].dl which is no longer needed + + if (n > max_code) + continue; // not a leaf node + + zip_bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n].fc; + zip_opt_len += f * (bits + xbits); + if (stree != null) + zip_static_len += f * (stree[n].dl + xbits); + } + if (overflow == 0) + return; + + // This happens for example on obj2 and pic of the Calgary corpus + + // Find the first bit length which could increase: + do { + bits = max_length - 1; + while (zip_bl_count[bits] == 0) + bits--; + zip_bl_count[bits]--; // move one leaf down the tree + zip_bl_count[bits + 1] += 2; // move one overflow item as its brother + zip_bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = zip_bl_count[bits]; + while (n != 0) { + m = zip_heap[--h]; + if (m > max_code) + continue; + if (tree[m].dl != bits) { + zip_opt_len += (bits - tree[m].dl) * tree[m].fc; + tree[m].fc = bits; + } + n--; + } + } + }; + + /* ========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ + var zip_gen_codes = function (tree, // the tree to decorate + max_code) { // largest code with non zero frequency + var next_code = new Array(zip_MAX_BITS + 1); // next code value for each bit length + var code = 0; // running code value + var bits; // bit index + var n; // code index + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= zip_MAX_BITS; bits++) { + code = ((code + zip_bl_count[bits - 1]) << 1); + next_code[bits] = code; + } + + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + for (n = 0; n <= max_code; n++) { + var len = tree[n].dl; + if (len == 0) + continue; + // Now reverse the bits + tree[n].fc = zip_bi_reverse(next_code[len]++, len); + } + }; + + /* ========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ + var zip_build_tree = function (desc) { // the tree descriptor + var tree = desc.dyn_tree; + var stree = desc.static_tree; + var elems = desc.elems; + var n, m; // iterate over heap elements + var max_code = -1; // largest code with non zero frequency + var node = elems; // next internal node of the tree + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + zip_heap_len = 0; + zip_heap_max = zip_HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc != 0) { + zip_heap[++zip_heap_len] = max_code = n; + zip_depth[n] = 0; + } else + tree[n].dl = 0; + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (zip_heap_len < 2) { + var xnew = zip_heap[++zip_heap_len] = (max_code < 2 ? ++max_code : 0); + tree[xnew].fc = 1; + zip_depth[xnew] = 0; + zip_opt_len--; + if (stree != null) + zip_static_len -= stree[xnew].dl; + // new is 0 or 1 so it does not have extra bits + } + desc.max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = zip_heap_len >> 1; n >= 1; n--) + zip_pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + n = zip_heap[zip_SMALLEST]; + zip_heap[zip_SMALLEST] = zip_heap[zip_heap_len--]; + zip_pqdownheap(tree, zip_SMALLEST); + + m = zip_heap[zip_SMALLEST]; // m = node of next least frequency + + // keep the nodes sorted by frequency + zip_heap[--zip_heap_max] = n; + zip_heap[--zip_heap_max] = m; + + // Create a new node father of n and m + tree[node].fc = tree[n].fc + tree[m].fc; + if (zip_depth[n] > zip_depth[m] + 1) + zip_depth[node] = zip_depth[n]; + else + zip_depth[node] = zip_depth[m] + 1; + tree[n].dl = tree[m].dl = node; + + // and insert the new node in the heap + zip_heap[zip_SMALLEST] = node++; + zip_pqdownheap(tree, zip_SMALLEST); + + } while (zip_heap_len >= 2); + + zip_heap[--zip_heap_max] = zip_heap[zip_SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + zip_gen_bitlen(desc); + + // The field len is now set, we can generate the bit codes + zip_gen_codes(tree, max_code); + }; + + /* ========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ + var zip_scan_tree = function (tree,// the tree to be scanned + max_code) { // and its largest code of non zero frequency + var n; // iterates over all tree elements + var prevlen = -1; // last emitted length + var curlen; // length of current code + var nextlen = tree[0].dl; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[max_code + 1].dl = 0xffff; // guard + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if (++count < max_count && curlen == nextlen) + continue; + else if (count < min_count) + zip_bl_tree[curlen].fc += count; + else if (curlen != 0) { + if (curlen != prevlen) + zip_bl_tree[curlen].fc++; + zip_bl_tree[zip_REP_3_6].fc++; + } else if (count <= 10) + zip_bl_tree[zip_REPZ_3_10].fc++; + else + zip_bl_tree[zip_REPZ_11_138].fc++; + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } + }; + + /* ========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ + var zip_send_tree = function (tree, // the tree to be scanned + max_code) { // and its largest code of non zero frequency + var n; // iterates over all tree elements + var prevlen = -1; // last emitted length + var curlen; // length of current code + var nextlen = tree[0].dl; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + /* tree[max_code+1].dl = -1; */ + /* guard already set */ + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + zip_SEND_CODE(curlen, zip_bl_tree); + } while (--count != 0); + } else if (curlen != 0) { + if (curlen != prevlen) { + zip_SEND_CODE(curlen, zip_bl_tree); + count--; + } + // Assert(count >= 3 && count <= 6, " 3_6?"); + zip_SEND_CODE(zip_REP_3_6, zip_bl_tree); + zip_send_bits(count - 3, 2); + } else if (count <= 10) { + zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree); + zip_send_bits(count - 3, 3); + } else { + zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree); + zip_send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } + }; + + /* ========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ + var zip_build_bl_tree = function () { + var max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + zip_scan_tree(zip_dyn_ltree, zip_l_desc.max_code); + zip_scan_tree(zip_dyn_dtree, zip_d_desc.max_code); + + // Build the bit length tree: + zip_build_tree(zip_bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = zip_BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (zip_bl_tree[zip_bl_order[max_blindex]].dl != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + zip_opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + return max_blindex; + }; + + /* ========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ + var zip_send_all_trees = function (lcodes, dcodes, blcodes) { // number of codes for each tree + var rank; // index in bl_order + zip_send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + zip_send_bits(dcodes - 1, 5); + zip_send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + zip_send_bits(zip_bl_tree[zip_bl_order[rank]].dl, 3); + } + + // send the literal tree + zip_send_tree(zip_dyn_ltree, lcodes - 1); + + // send the distance tree + zip_send_tree(zip_dyn_dtree, dcodes - 1); + }; + + /* ========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ + var zip_flush_block = function (eof) { // true if this is the last block for a file + var opt_lenb, static_lenb; // opt_len and static_len in bytes + var max_blindex; // index of last bit length code of non zero freq + var stored_len; // length of input block + + stored_len = zip_strstart - zip_block_start; + zip_flag_buf[zip_last_flags] = zip_flags; // Save the flags for the last 8 items + + // Construct the literal and distance trees + zip_build_tree(zip_l_desc); + zip_build_tree(zip_d_desc); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = zip_build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (zip_opt_len + 3 + 7) >> 3; + static_lenb = (zip_static_len + 3 + 7) >> 3; + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + if (stored_len + 4 <= opt_lenb // 4: two words for the lengths + && zip_block_start >= 0) { + var i; + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + zip_send_bits((zip_STORED_BLOCK << 1) + eof, 3); + /* send block type */ + zip_bi_windup(); + /* align on byte boundary */ + zip_put_short(stored_len); + zip_put_short(~stored_len); + + // copy block + for (i = 0; i < stored_len; i++) + zip_put_byte(zip_window[zip_block_start + i]); + + } else if (static_lenb == opt_lenb) { + zip_send_bits((zip_STATIC_TREES << 1) + eof, 3); + zip_compress_block(zip_static_ltree, zip_static_dtree); + } else { + zip_send_bits((zip_DYN_TREES << 1) + eof, 3); + zip_send_all_trees(zip_l_desc.max_code + 1, + zip_d_desc.max_code + 1, + max_blindex + 1); + zip_compress_block(zip_dyn_ltree, zip_dyn_dtree); + } + + zip_init_block(); + + if (eof != 0) + zip_bi_windup(); + }; + + /* ========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ + var zip_ct_tally = function (dist, // distance of matched string + lc) { // match length-MIN_MATCH or unmatched char (if dist==0) + zip_l_buf[zip_last_lit++] = lc; + if (dist == 0) { + // lc is the unmatched char + zip_dyn_ltree[lc].fc++; + } else { + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + zip_dyn_ltree[zip_length_code[lc] + zip_LITERALS + 1].fc++; + zip_dyn_dtree[zip_D_CODE(dist)].fc++; + + zip_d_buf[zip_last_dist++] = dist; + zip_flags |= zip_flag_bit; + } + zip_flag_bit <<= 1; + + // Output the flags if they fill a byte + if ((zip_last_lit & 7) == 0) { + zip_flag_buf[zip_last_flags++] = zip_flags; + zip_flags = 0; + zip_flag_bit = 1; + } + // Try to guess if it is profitable to stop the current block here + if (zip_compr_level > 2 && (zip_last_lit & 0xfff) == 0) { + // Compute an upper bound for the compressed length + var out_length = zip_last_lit * 8; + var in_length = zip_strstart - zip_block_start; + var dcode; + + for (dcode = 0; dcode < zip_D_CODES; dcode++) { + out_length += zip_dyn_dtree[dcode].fc * (5 + zip_extra_dbits[dcode]); + } + out_length >>= 3; + if (zip_last_dist < parseInt(zip_last_lit / 2) && + out_length < parseInt(in_length / 2)) + return true; + } + return (zip_last_lit == LIT_BUFSIZE - 1 || + zip_last_dist == zip_DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ + }; + + /* ========================================================================== + * Send the block data compressed using the given Huffman trees + */ + var zip_compress_block = function (ltree, // literal tree + dtree) { // distance tree + var dist; // distance of matched string + var lc; // match length or unmatched char (if dist == 0) + var lx = 0; // running index in l_buf + var dx = 0; // running index in d_buf + var fx = 0; // running index in flag_buf + var flag = 0; // current flags + var code; // the code to send + var extra; // number of extra bits to send + + if (zip_last_lit != 0) do { + if ((lx & 7) == 0) + flag = zip_flag_buf[fx++]; + lc = zip_l_buf[lx++] & 0xff; + if ((flag & 1) == 0) { + zip_SEND_CODE(lc, ltree); + /* send a literal byte */ + } else { + // Here, lc is the match length - MIN_MATCH + code = zip_length_code[lc]; + zip_SEND_CODE(code + zip_LITERALS + 1, ltree); // send the length code + extra = zip_extra_lbits[code]; + if (extra != 0) { + lc -= zip_base_length[code]; + zip_send_bits(lc, extra); // send the extra length bits + } + dist = zip_d_buf[dx++]; + // Here, dist is the match distance - 1 + code = zip_D_CODE(dist); + zip_SEND_CODE(code, dtree); // send the distance code + extra = zip_extra_dbits[code]; + if (extra != 0) { + dist -= zip_base_dist[code]; + zip_send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + flag >>= 1; + } while (lx < zip_last_lit); + + zip_SEND_CODE(zip_END_BLOCK, ltree); + }; + + /* ========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ + var zip_Buf_size = 16; // bit size of bi_buf + var zip_send_bits = function (value, // value to send + length) { // number of bits + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (zip_bi_valid > zip_Buf_size - length) { + zip_bi_buf |= (value << zip_bi_valid); + zip_put_short(zip_bi_buf); + zip_bi_buf = (value >> (zip_Buf_size - zip_bi_valid)); + zip_bi_valid += length - zip_Buf_size; + } else { + zip_bi_buf |= value << zip_bi_valid; + zip_bi_valid += length; + } + }; + + /* ========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ + var zip_bi_reverse = function (code, // the value to invert + len) { // its bit length + var res = 0; + do { + res |= code & 1; + code >>= 1; + res <<= 1; + } while (--len > 0); + return res >> 1; + }; + + /* ========================================================================== + * Write out any remaining bits in an incomplete byte. + */ + var zip_bi_windup = function () { + if (zip_bi_valid > 8) { + zip_put_short(zip_bi_buf); + } else if (zip_bi_valid > 0) { + zip_put_byte(zip_bi_buf); + } + zip_bi_buf = 0; + zip_bi_valid = 0; + }; + + var zip_qoutbuf = function () { + if (zip_outcnt != 0) { + var q, i; + q = zip_new_queue(); + if (zip_qhead == null) + zip_qhead = zip_qtail = q; + else + zip_qtail = zip_qtail.next = q; + q.len = zip_outcnt - zip_outoff; + for (i = 0; i < q.len; i++) + q.ptr[i] = zip_outbuf[zip_outoff + i]; + zip_outcnt = zip_outoff = 0; + } + }; + + function deflate(buffData, level) { + zip_deflate_data = buffData; + zip_deflate_pos = 0; + zip_deflate_start(level); + + var buff = new Array(1024), + pages = [], + totalSize = 0, + i; + + for (i = 0; i < 1024; i++) buff[i] = 0; + while ((i = zip_deflate_internal(buff, 0, buff.length)) > 0) { + var buf = new Buffer(buff.slice(0, i)); + pages.push(buf); + totalSize += buf.length; + } + + if (pages.length == 1) { + return pages[0]; + } + + var result = new Buffer(totalSize), + index = 0; + + for (i = 0; i < pages.length; i++) { + pages[i].copy(result, index); + index = index + pages[i].length + } + + return result; + } + + return { + deflate: function () { + return deflate(inbuf, 8); + } + } +} + +module.exports = function (/*Buffer*/inbuf) { + + var zlib = require("zlib"); + + return { + deflate: function () { + return new JSDeflater(inbuf).deflate(); + }, + + deflateAsync: function (/*Function*/callback) { + var tmp = zlib.createDeflateRaw({chunkSize:(parseInt(inbuf.length / 1024) + 1)*1024}), + parts = [], total = 0; + tmp.on('data', function(data) { + parts.push(data); + total += data.length; + }); + tmp.on('end', function() { + var buf = new Buffer(total), written = 0; + buf.fill(0); + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + part.copy(buf, written); + written += part.length; + } + callback && callback(buf); + }); + tmp.end(inbuf); + } + } +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/methods/index.js b/platforms/browser/cordova/node_modules/adm-zip/methods/index.js new file mode 100644 index 0000000..58c718d --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/methods/index.js @@ -0,0 +1,2 @@ +exports.Deflater = require("./deflater"); +exports.Inflater = require("./inflater"); \ No newline at end of file diff --git a/platforms/browser/cordova/node_modules/adm-zip/methods/inflater.js b/platforms/browser/cordova/node_modules/adm-zip/methods/inflater.js new file mode 100644 index 0000000..3739d98 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/methods/inflater.js @@ -0,0 +1,448 @@ +var Buffer = require("buffer").Buffer; + +function JSInflater(/*Buffer*/input) { + + var WSIZE = 0x8000, + slide = new Buffer(0x10000), + windowPos = 0, + fixedTableList = null, + fixedTableDist, + fixedLookup, + bitBuf = 0, + bitLen = 0, + method = -1, + eof = false, + copyLen = 0, + copyDist = 0, + tblList, tblDist, bitList, bitdist, + + inputPosition = 0, + + MASK_BITS = [0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff], + LENS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0], + LEXT = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99], + DISTS = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577], + DEXT = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13], + BITORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + + function HuffTable(clen, cnum, cval, blist, elist, lookupm) { + + this.status = 0; + this.root = null; + this.maxbit = 0; + + var el, f, tail, + offsets = [], + countTbl = [], + sTbl = [], + values = [], + tentry = {extra: 0, bitcnt: 0, lbase: 0, next: null}; + + tail = this.root = null; + for(var i = 0; i < 0x11; i++) { countTbl[i] = 0; sTbl[i] = 0; offsets[i] = 0; } + for(i = 0; i < 0x120; i++) values[i] = 0; + + el = cnum > 256 ? clen[256] : 16; + + var pidx = -1; + while (++pidx < cnum) countTbl[clen[pidx]]++; + + if(countTbl[0] == cnum) return; + + for(var j = 1; j <= 16; j++) if(countTbl[j] != 0) break; + var bitLen = j; + for(i = 16; i != 0; i--) if(countTbl[i] != 0) break; + var maxLen = i; + + lookupm < j && (lookupm = j); + + var dCodes = 1 << j; + for(; j < i; j++, dCodes <<= 1) + if((dCodes -= countTbl[j]) < 0) { + this.status = 2; + this.maxbit = lookupm; + return; + } + + if((dCodes -= countTbl[i]) < 0) { + this.status = 2; + this.maxbit = lookupm; + return; + } + + countTbl[i] += dCodes; + offsets[1] = j = 0; + pidx = 1; + var xp = 2; + while(--i > 0) offsets[xp++] = (j += countTbl[pidx++]); + pidx = 0; + i = 0; + do { + (j = clen[pidx++]) && (values[offsets[j]++] = i); + } while(++i < cnum); + cnum = offsets[maxLen]; + offsets[0] = i = 0; + pidx = 0; + + var level = -1, + w = sTbl[0] = 0, + cnode = null, + tblCnt = 0, + tblStack = []; + + for(; bitLen <= maxLen; bitLen++) { + var kccnt = countTbl[bitLen]; + while(kccnt-- > 0) { + while(bitLen > w + sTbl[1 + level]) { + w += sTbl[1 + level]; + level++; + tblCnt = (tblCnt = maxLen - w) > lookupm ? lookupm : tblCnt; + if((f = 1 << (j = bitLen - w)) > kccnt + 1) { + f -= kccnt + 1; + xp = bitLen; + while(++j < tblCnt) { + if((f <<= 1) <= countTbl[++xp]) break; + f -= countTbl[xp]; + } + } + if(w + j > el && w < el) j = el - w; + tblCnt = 1 << j; + sTbl[1 + level] = j; + cnode = []; + while (cnode.length < tblCnt) cnode.push({extra: 0, bitcnt: 0, lbase: 0, next: null}); + if (tail == null) { + tail = this.root = {next:null, list:null}; + } else { + tail = tail.next = {next:null, list:null} + } + tail.next = null; + tail.list = cnode; + + tblStack[level] = cnode; + + if(level > 0) { + offsets[level] = i; + tentry.bitcnt = sTbl[level]; + tentry.extra = 16 + j; + tentry.next = cnode; + j = (i & ((1 << w) - 1)) >> (w - sTbl[level]); + + tblStack[level-1][j].extra = tentry.extra; + tblStack[level-1][j].bitcnt = tentry.bitcnt; + tblStack[level-1][j].lbase = tentry.lbase; + tblStack[level-1][j].next = tentry.next; + } + } + tentry.bitcnt = bitLen - w; + if(pidx >= cnum) + tentry.extra = 99; + else if(values[pidx] < cval) { + tentry.extra = (values[pidx] < 256 ? 16 : 15); + tentry.lbase = values[pidx++]; + } else { + tentry.extra = elist[values[pidx] - cval]; + tentry.lbase = blist[values[pidx++] - cval]; + } + + f = 1 << (bitLen - w); + for(j = i >> w; j < tblCnt; j += f) { + cnode[j].extra = tentry.extra; + cnode[j].bitcnt = tentry.bitcnt; + cnode[j].lbase = tentry.lbase; + cnode[j].next = tentry.next; + } + for(j = 1 << (bitLen - 1); (i & j) != 0; j >>= 1) + i ^= j; + i ^= j; + while((i & ((1 << w) - 1)) != offsets[level]) { + w -= sTbl[level]; + level--; + } + } + } + + this.maxbit = sTbl[1]; + this.status = ((dCodes != 0 && maxLen != 1) ? 1 : 0); + } + + function addBits(n) { + while(bitLen < n) { + bitBuf |= input[inputPosition++] << bitLen; + bitLen += 8; + } + return bitBuf; + } + + function cutBits(n) { + bitLen -= n; + return bitBuf >>= n; + } + + function maskBits(n) { + while(bitLen < n) { + bitBuf |= input[inputPosition++] << bitLen; + bitLen += 8; + } + var res = bitBuf & MASK_BITS[n]; + bitBuf >>= n; + bitLen -= n; + return res; + } + + function codes(buff, off, size) { + var e, t; + if(size == 0) return 0; + + var n = 0; + for(;;) { + t = tblList.list[addBits(bitList) & MASK_BITS[bitList]]; + e = t.extra; + while(e > 16) { + if(e == 99) return -1; + cutBits(t.bitcnt); + e -= 16; + t = t.next[addBits(e) & MASK_BITS[e]]; + e = t.extra; + } + cutBits(t.bitcnt); + if(e == 16) { + windowPos &= WSIZE - 1; + buff[off + n++] = slide[windowPos++] = t.lbase; + if(n == size) return size; + continue; + } + if(e == 15) break; + + copyLen = t.lbase + maskBits(e); + t = tblDist.list[addBits(bitdist) & MASK_BITS[bitdist]]; + e = t.extra; + + while(e > 16) { + if(e == 99) return -1; + cutBits(t.bitcnt); + e -= 16; + t = t.next[addBits(e) & MASK_BITS[e]]; + e = t.extra + } + cutBits(t.bitcnt); + copyDist = windowPos - t.lbase - maskBits(e); + + while(copyLen > 0 && n < size) { + copyLen--; + copyDist &= WSIZE - 1; + windowPos &= WSIZE - 1; + buff[off + n++] = slide[windowPos++] = slide[copyDist++]; + } + + if(n == size) return size; + } + + method = -1; // done + return n; + } + + function stored(buff, off, size) { + cutBits(bitLen & 7); + var n = maskBits(0x10); + if(n != ((~maskBits(0x10)) & 0xffff)) return -1; + copyLen = n; + + n = 0; + while(copyLen > 0 && n < size) { + copyLen--; + windowPos &= WSIZE - 1; + buff[off + n++] = slide[windowPos++] = maskBits(8); + } + + if(copyLen == 0) method = -1; + return n; + } + + function fixed(buff, off, size) { + var fixed_bd = 0; + if(fixedTableList == null) { + var lengths = []; + + for(var symbol = 0; symbol < 144; symbol++) lengths[symbol] = 8; + for(; symbol < 256; symbol++) lengths[symbol] = 9; + for(; symbol < 280; symbol++) lengths[symbol] = 7; + for(; symbol < 288; symbol++) lengths[symbol] = 8; + + fixedLookup = 7; + + var htbl = new HuffTable(lengths, 288, 257, LENS, LEXT, fixedLookup); + + if(htbl.status != 0) return -1; + + fixedTableList = htbl.root; + fixedLookup = htbl.maxbit; + + for(symbol = 0; symbol < 30; symbol++) lengths[symbol] = 5; + fixed_bd = 5; + + htbl = new HuffTable(lengths, 30, 0, DISTS, DEXT, fixed_bd); + if(htbl.status > 1) { + fixedTableList = null; + return -1; + } + fixedTableDist = htbl.root; + fixed_bd = htbl.maxbit; + } + + tblList = fixedTableList; + tblDist = fixedTableDist; + bitList = fixedLookup; + bitdist = fixed_bd; + return codes(buff, off, size); + } + + function dynamic(buff, off, size) { + var ll = new Array(0x023C); + + for (var m = 0; m < 0x023C; m++) ll[m] = 0; + + var llencnt = 257 + maskBits(5), + dcodescnt = 1 + maskBits(5), + bitlencnt = 4 + maskBits(4); + + if(llencnt > 286 || dcodescnt > 30) return -1; + + for(var j = 0; j < bitlencnt; j++) ll[BITORDER[j]] = maskBits(3); + for(; j < 19; j++) ll[BITORDER[j]] = 0; + + // build decoding table for trees--single level, 7 bit lookup + bitList = 7; + var hufTable = new HuffTable(ll, 19, 19, null, null, bitList); + if(hufTable.status != 0) + return -1; // incomplete code set + + tblList = hufTable.root; + bitList = hufTable.maxbit; + var lencnt = llencnt + dcodescnt, + i = 0, + lastLen = 0; + while(i < lencnt) { + var hufLcode = tblList.list[addBits(bitList) & MASK_BITS[bitList]]; + j = hufLcode.bitcnt; + cutBits(j); + j = hufLcode.lbase; + if(j < 16) + ll[i++] = lastLen = j; + else if(j == 16) { + j = 3 + maskBits(2); + if(i + j > lencnt) return -1; + while(j-- > 0) ll[i++] = lastLen; + } else if(j == 17) { + j = 3 + maskBits(3); + if(i + j > lencnt) return -1; + while(j-- > 0) ll[i++] = 0; + lastLen = 0; + } else { + j = 11 + maskBits(7); + if(i + j > lencnt) return -1; + while(j-- > 0) ll[i++] = 0; + lastLen = 0; + } + } + bitList = 9; + hufTable = new HuffTable(ll, llencnt, 257, LENS, LEXT, bitList); + bitList == 0 && (hufTable.status = 1); + + if (hufTable.status != 0) return -1; + + tblList = hufTable.root; + bitList = hufTable.maxbit; + + for(i = 0; i < dcodescnt; i++) ll[i] = ll[i + llencnt]; + bitdist = 6; + hufTable = new HuffTable(ll, dcodescnt, 0, DISTS, DEXT, bitdist); + tblDist = hufTable.root; + bitdist = hufTable.maxbit; + + if((bitdist == 0 && llencnt > 257) || hufTable.status != 0) return -1; + + return codes(buff, off, size); + } + + return { + inflate : function(/*Buffer*/outputBuffer) { + tblList = null; + + var size = outputBuffer.length, + offset = 0, i; + + while(offset < size) { + if(eof && method == -1) return; + if(copyLen > 0) { + if(method != 0) { + while(copyLen > 0 && offset < size) { + copyLen--; + copyDist &= WSIZE - 1; + windowPos &= WSIZE - 1; + outputBuffer[offset++] = (slide[windowPos++] = slide[copyDist++]); + } + } else { + while(copyLen > 0 && offset < size) { + copyLen--; + windowPos &= WSIZE - 1; + outputBuffer[offset++] = (slide[windowPos++] = maskBits(8)); + } + copyLen == 0 && (method = -1); // done + } + if (offset == size) return; + } + + if(method == -1) { + if(eof) break; + eof = maskBits(1) != 0; + method = maskBits(2); + tblList = null; + copyLen = 0; + } + switch(method) { + case 0: i = stored(outputBuffer, offset, size - offset); break; + case 1: i = tblList != null ? codes(outputBuffer, offset, size - offset) : fixed(outputBuffer, offset, size - offset); break; + case 2: i = tblList != null ? codes(outputBuffer, offset, size - offset) : dynamic(outputBuffer, offset, size - offset); break; + default: i = -1; break; + } + + if(i == -1) return; + offset += i; + } + } + }; +} + +module.exports = function(/*Buffer*/inbuf) { + var zlib = require("zlib"); + return { + inflateAsync : function(/*Function*/callback) { + var tmp = zlib.createInflateRaw(), + parts = [], total = 0; + tmp.on('data', function(data) { + parts.push(data); + total += data.length; + }); + tmp.on('end', function() { + var buf = new Buffer(total), written = 0; + buf.fill(0); + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + part.copy(buf, written); + written += part.length; + } + callback && callback(buf); + }); + tmp.end(inbuf) + }, + + inflate : function(/*Buffer*/outputBuffer) { + var x = { + x: new JSInflater(inbuf) + }; + x.x.inflate(outputBuffer); + delete(x.x); + } + } +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/package.json b/platforms/browser/cordova/node_modules/adm-zip/package.json new file mode 100644 index 0000000..a5d8c91 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/package.json @@ -0,0 +1,66 @@ +{ + "name": "adm-zip", + "version": "0.4.7", + "description": "A Javascript implementation of zip for nodejs. Allows user to create or extract zip files both in memory or to/from disk", + "keywords": [ + "zip", + "methods", + "archive", + "unzip" + ], + "homepage": "http://github.com/cthackers/adm-zip", + "author": { + "name": "Nasca Iacob", + "email": "sy@another-d-mention.ro", + "url": "https://github.com/cthackers" + }, + "bugs": { + "url": "https://github.com/cthackers/adm-zip/issues", + "email": "sy@another-d-mention.ro" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/cthackers/adm-zip/master/MIT-LICENSE.txt" + } + ], + "files": [ + "adm-zip.js", + "headers", + "methods", + "util", + "zipEntry.js", + "zipFile.js" + ], + "main": "adm-zip.js", + "repository": { + "type": "git", + "url": "https://github.com/cthackers/adm-zip.git" + }, + "engines": { + "node": ">=0.3.0" + }, + "gitHead": "6708a3e5788ff9e67ddba288397f7788a5c02855", + "_id": "adm-zip@0.4.7", + "scripts": {}, + "_shasum": "8606c2cbf1c426ce8c8ec00174447fd49b6eafc1", + "_from": "adm-zip@>=0.4.7 <0.5.0", + "_resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", + "_npmVersion": "2.5.1", + "_nodeVersion": "0.12.0", + "_npmUser": { + "name": "cthackers", + "email": "iacob.campia@gmail.com" + }, + "maintainers": [ + { + "name": "cthackers", + "email": "sy@another-d-mention.ro" + } + ], + "dist": { + "shasum": "8606c2cbf1c426ce8c8ec00174447fd49b6eafc1", + "tarball": "http://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz" + }, + "directories": {} +} diff --git a/platforms/browser/cordova/node_modules/adm-zip/util/constants.js b/platforms/browser/cordova/node_modules/adm-zip/util/constants.js new file mode 100644 index 0000000..02de1e9 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/util/constants.js @@ -0,0 +1,115 @@ +module.exports = { + /* The local file header */ + LOCHDR : 30, // LOC header size + LOCSIG : 0x04034b50, // "PK\003\004" + LOCVER : 4, // version needed to extract + LOCFLG : 6, // general purpose bit flag + LOCHOW : 8, // compression method + LOCTIM : 10, // modification time (2 bytes time, 2 bytes date) + LOCCRC : 14, // uncompressed file crc-32 value + LOCSIZ : 18, // compressed size + LOCLEN : 22, // uncompressed size + LOCNAM : 26, // filename length + LOCEXT : 28, // extra field length + + /* The Data descriptor */ + EXTSIG : 0x08074b50, // "PK\007\008" + EXTHDR : 16, // EXT header size + EXTCRC : 4, // uncompressed file crc-32 value + EXTSIZ : 8, // compressed size + EXTLEN : 12, // uncompressed size + + /* The central directory file header */ + CENHDR : 46, // CEN header size + CENSIG : 0x02014b50, // "PK\001\002" + CENVEM : 4, // version made by + CENVER : 6, // version needed to extract + CENFLG : 8, // encrypt, decrypt flags + CENHOW : 10, // compression method + CENTIM : 12, // modification time (2 bytes time, 2 bytes date) + CENCRC : 16, // uncompressed file crc-32 value + CENSIZ : 20, // compressed size + CENLEN : 24, // uncompressed size + CENNAM : 28, // filename length + CENEXT : 30, // extra field length + CENCOM : 32, // file comment length + CENDSK : 34, // volume number start + CENATT : 36, // internal file attributes + CENATX : 38, // external file attributes (host system dependent) + CENOFF : 42, // LOC header offset + + /* The entries in the end of central directory */ + ENDHDR : 22, // END header size + ENDSIG : 0x06054b50, // "PK\005\006" + ENDSUB : 8, // number of entries on this disk + ENDTOT : 10, // total number of entries + ENDSIZ : 12, // central directory size in bytes + ENDOFF : 16, // offset of first CEN header + ENDCOM : 20, // zip file comment length + + /* Compression methods */ + STORED : 0, // no compression + SHRUNK : 1, // shrunk + REDUCED1 : 2, // reduced with compression factor 1 + REDUCED2 : 3, // reduced with compression factor 2 + REDUCED3 : 4, // reduced with compression factor 3 + REDUCED4 : 5, // reduced with compression factor 4 + IMPLODED : 6, // imploded + // 7 reserved + DEFLATED : 8, // deflated + ENHANCED_DEFLATED: 9, // enhanced deflated + PKWARE : 10,// PKWare DCL imploded + // 11 reserved + BZIP2 : 12, // compressed using BZIP2 + // 13 reserved + LZMA : 14, // LZMA + // 15-17 reserved + IBM_TERSE : 18, // compressed using IBM TERSE + IBM_LZ77 : 19, //IBM LZ77 z + + /* General purpose bit flag */ + FLG_ENC : 0, // encripted file + FLG_COMP1 : 1, // compression option + FLG_COMP2 : 2, // compression option + FLG_DESC : 4, // data descriptor + FLG_ENH : 8, // enhanced deflation + FLG_STR : 16, // strong encryption + FLG_LNG : 1024, // language encoding + FLG_MSK : 4096, // mask header values + + /* Load type */ + FILE : 0, + BUFFER : 1, + NONE : 2, + + /* 4.5 Extensible data fields */ + EF_ID : 0, + EF_SIZE : 2, + + /* Header IDs */ + ID_ZIP64 : 0x0001, + ID_AVINFO : 0x0007, + ID_PFS : 0x0008, + ID_OS2 : 0x0009, + ID_NTFS : 0x000a, + ID_OPENVMS : 0x000c, + ID_UNIX : 0x000d, + ID_FORK : 0x000e, + ID_PATCH : 0x000f, + ID_X509_PKCS7 : 0x0014, + ID_X509_CERTID_F : 0x0015, + ID_X509_CERTID_C : 0x0016, + ID_STRONGENC : 0x0017, + ID_RECORD_MGT : 0x0018, + ID_X509_PKCS7_RL : 0x0019, + ID_IBM1 : 0x0065, + ID_IBM2 : 0x0066, + ID_POSZIP : 0x4690, + + EF_ZIP64_OR_32 : 0xffffffff, + EF_ZIP64_OR_16 : 0xffff, + EF_ZIP64_SUNCOMP : 0, + EF_ZIP64_SCOMP : 8, + EF_ZIP64_RHO : 16, + EF_ZIP64_DSN : 24 +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/util/errors.js b/platforms/browser/cordova/node_modules/adm-zip/util/errors.js new file mode 100644 index 0000000..50931c3 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/util/errors.js @@ -0,0 +1,35 @@ +module.exports = { + /* Header error messages */ + "INVALID_LOC" : "Invalid LOC header (bad signature)", + "INVALID_CEN" : "Invalid CEN header (bad signature)", + "INVALID_END" : "Invalid END header (bad signature)", + + /* ZipEntry error messages*/ + "NO_DATA" : "Nothing to decompress", + "BAD_CRC" : "CRC32 checksum failed", + "FILE_IN_THE_WAY" : "There is a file in the way: %s", + "UNKNOWN_METHOD" : "Invalid/unsupported compression method", + + /* Inflater error messages */ + "AVAIL_DATA" : "inflate::Available inflate data did not terminate", + "INVALID_DISTANCE" : "inflate::Invalid literal/length or distance code in fixed or dynamic block", + "TO_MANY_CODES" : "inflate::Dynamic block code description: too many length or distance codes", + "INVALID_REPEAT_LEN" : "inflate::Dynamic block code description: repeat more than specified lengths", + "INVALID_REPEAT_FIRST" : "inflate::Dynamic block code description: repeat lengths with no first length", + "INCOMPLETE_CODES" : "inflate::Dynamic block code description: code lengths codes incomplete", + "INVALID_DYN_DISTANCE": "inflate::Dynamic block code description: invalid distance code lengths", + "INVALID_CODES_LEN": "inflate::Dynamic block code description: invalid literal/length code lengths", + "INVALID_STORE_BLOCK" : "inflate::Stored block length did not match one's complement", + "INVALID_BLOCK_TYPE" : "inflate::Invalid block type (type == 3)", + + /* ADM-ZIP error messages */ + "CANT_EXTRACT_FILE" : "Could not extract the file", + "CANT_OVERRIDE" : "Target file already exists", + "NO_ZIP" : "No zip file was loaded", + "NO_ENTRY" : "Entry doesn't exist", + "DIRECTORY_CONTENT_ERROR" : "A directory cannot have content", + "FILE_NOT_FOUND" : "File not found: %s", + "NOT_IMPLEMENTED" : "Not implemented", + "INVALID_FILENAME" : "Invalid filename", + "INVALID_FORMAT" : "Invalid or unsupported zip format. No END header found" +}; \ No newline at end of file diff --git a/platforms/browser/cordova/node_modules/adm-zip/util/fattr.js b/platforms/browser/cordova/node_modules/adm-zip/util/fattr.js new file mode 100644 index 0000000..4f247ea --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/util/fattr.js @@ -0,0 +1,84 @@ +var fs = require("fs"), + pth = require("path"); + +fs.existsSync = fs.existsSync || pth.existsSync; + +module.exports = function(/*String*/path) { + + var _path = path || "", + _permissions = 0, + _obj = newAttr(), + _stat = null; + + function newAttr() { + return { + directory : false, + readonly : false, + hidden : false, + executable : false, + mtime : 0, + atime : 0 + } + } + + if (_path && fs.existsSync(_path)) { + _stat = fs.statSync(_path); + _obj.directory = _stat.isDirectory(); + _obj.mtime = _stat.mtime; + _obj.atime = _stat.atime; + _obj.executable = !!(1 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0])); + _obj.readonly = !!(2 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0])); + _obj.hidden = pth.basename(_path)[0] === "."; + } else { + console.warn("Invalid path: " + _path) + } + + return { + + get directory () { + return _obj.directory; + }, + + get readOnly () { + return _obj.readonly; + }, + + get hidden () { + return _obj.hidden; + }, + + get mtime () { + return _obj.mtime; + }, + + get atime () { + return _obj.atime; + }, + + + get executable () { + return _obj.executable; + }, + + decodeAttributes : function(val) { + + }, + + encodeAttributes : function (val) { + + }, + + toString : function() { + return '{\n' + + '\t"path" : "' + _path + ",\n" + + '\t"isDirectory" : ' + _obj.directory + ",\n" + + '\t"isReadOnly" : ' + _obj.readonly + ",\n" + + '\t"isHidden" : ' + _obj.hidden + ",\n" + + '\t"isExecutable" : ' + _obj.executable + ",\n" + + '\t"mTime" : ' + _obj.mtime + "\n" + + '\t"aTime" : ' + _obj.atime + "\n" + + '}'; + } + } + +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/util/index.js b/platforms/browser/cordova/node_modules/adm-zip/util/index.js new file mode 100644 index 0000000..d77b980 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/util/index.js @@ -0,0 +1,4 @@ +module.exports = require("./utils"); +module.exports.Constants = require("./constants"); +module.exports.Errors = require("./errors"); +module.exports.FileAttr = require("./fattr"); \ No newline at end of file diff --git a/platforms/browser/cordova/node_modules/adm-zip/util/utils.js b/platforms/browser/cordova/node_modules/adm-zip/util/utils.js new file mode 100644 index 0000000..52a8ed9 --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/util/utils.js @@ -0,0 +1,199 @@ +var fs = require("fs"), + pth = require('path'); + +fs.existsSync = fs.existsSync || pth.existsSync; + +module.exports = (function() { + + var crcTable = [], + Constants = require('./constants'), + Errors = require('./errors'), + + PATH_SEPARATOR = pth.normalize("/"); + + + function mkdirSync(/*String*/path) { + var resolvedPath = path.split(PATH_SEPARATOR)[0]; + path.split(PATH_SEPARATOR).forEach(function(name) { + if (!name || name.substr(-1,1) == ":") return; + resolvedPath += PATH_SEPARATOR + name; + var stat; + try { + stat = fs.statSync(resolvedPath); + } catch (e) { + fs.mkdirSync(resolvedPath); + } + if (stat && stat.isFile()) + throw Errors.FILE_IN_THE_WAY.replace("%s", resolvedPath); + }); + } + + function findSync(/*String*/root, /*RegExp*/pattern, /*Boolean*/recoursive) { + if (typeof pattern === 'boolean') { + recoursive = pattern; + pattern = undefined; + } + var files = []; + fs.readdirSync(root).forEach(function(file) { + var path = pth.join(root, file); + + if (fs.statSync(path).isDirectory() && recoursive) + files = files.concat(findSync(path, pattern, recoursive)); + + if (!pattern || pattern.test(path)) { + files.push(pth.normalize(path) + (fs.statSync(path).isDirectory() ? PATH_SEPARATOR : "")); + } + + }); + return files; + } + + return { + makeDir : function(/*String*/path) { + mkdirSync(path); + }, + + crc32 : function(buf) { + var b = new Buffer(4); + if (!crcTable.length) { + for (var n = 0; n < 256; n++) { + var c = n; + for (var k = 8; --k >= 0;) // + if ((c & 1) != 0) { c = 0xedb88320 ^ (c >>> 1); } else { c = c >>> 1; } + if (c < 0) { + b.writeInt32LE(c, 0); + c = b.readUInt32LE(0); + } + crcTable[n] = c; + } + } + var crc = 0, off = 0, len = buf.length, c1 = ~crc; + while(--len >= 0) c1 = crcTable[(c1 ^ buf[off++]) & 0xff] ^ (c1 >>> 8); + crc = ~c1; + b.writeInt32LE(crc & 0xffffffff, 0); + return b.readUInt32LE(0); + }, + + methodToString : function(/*Number*/method) { + switch (method) { + case Constants.STORED: + return 'STORED (' + method + ')'; + case Constants.DEFLATED: + return 'DEFLATED (' + method + ')'; + default: + return 'UNSUPPORTED (' + method + ')'; + } + + }, + + writeFileTo : function(/*String*/path, /*Buffer*/content, /*Boolean*/overwrite, /*Number*/attr) { + if (fs.existsSync(path)) { + if (!overwrite) + return false; // cannot overwite + + var stat = fs.statSync(path); + if (stat.isDirectory()) { + return false; + } + } + var folder = pth.dirname(path); + if (!fs.existsSync(folder)) { + mkdirSync(folder); + } + + var fd; + try { + fd = fs.openSync(path, 'w', 438); // 0666 + } catch(e) { + fs.chmodSync(path, 438); + fd = fs.openSync(path, 'w', 438); + } + if (fd) { + fs.writeSync(fd, content, 0, content.length, 0); + fs.closeSync(fd); + } + fs.chmodSync(path, attr || 438); + return true; + }, + + writeFileToAsync : function(/*String*/path, /*Buffer*/content, /*Boolean*/overwrite, /*Number*/attr, /*Function*/callback) { + if(typeof attr === 'function') { + callback = attr; + attr = undefined; + } + + fs.exists(path, function(exists) { + if(exists && !overwrite) + return callback(false); + + fs.stat(path, function(err, stat) { + if(exists &&stat.isDirectory()) { + return callback(false); + } + + var folder = pth.dirname(path); + fs.exists(folder, function(exists) { + if(!exists) + mkdirSync(folder); + + fs.open(path, 'w', 438, function(err, fd) { + if(err) { + fs.chmod(path, 438, function(err) { + fs.open(path, 'w', 438, function(err, fd) { + fs.write(fd, content, 0, content.length, 0, function(err, written, buffer) { + fs.close(fd, function(err) { + fs.chmod(path, attr || 438, function() { + callback(true); + }) + }); + }); + }); + }) + } else { + if(fd) { + fs.write(fd, content, 0, content.length, 0, function(err, written, buffer) { + fs.close(fd, function(err) { + fs.chmod(path, attr || 438, function() { + callback(true); + }) + }); + }); + } else { + fs.chmod(path, attr || 438, function() { + callback(true); + }) + } + } + }); + }) + }) + }) + }, + + findFiles : function(/*String*/path) { + return findSync(path, true); + }, + + getAttributes : function(/*String*/path) { + + }, + + setAttributes : function(/*String*/path) { + + }, + + toBuffer : function(input) { + if (Buffer.isBuffer(input)) { + return input; + } else { + if (input.length == 0) { + return new Buffer(0) + } + return new Buffer(input, 'utf8'); + } + }, + + Constants : Constants, + Errors : Errors + } +})(); diff --git a/platforms/browser/cordova/node_modules/adm-zip/zipEntry.js b/platforms/browser/cordova/node_modules/adm-zip/zipEntry.js new file mode 100644 index 0000000..6b1309b --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/zipEntry.js @@ -0,0 +1,284 @@ +var Utils = require("./util"), + Headers = require("./headers"), + Constants = Utils.Constants, + Methods = require("./methods"); + +module.exports = function (/*Buffer*/input) { + + var _entryHeader = new Headers.EntryHeader(), + _entryName = new Buffer(0), + _comment = new Buffer(0), + _isDirectory = false, + uncompressedData = null, + _extra = new Buffer(0); + + function getCompressedDataFromZip() { + if (!input || !Buffer.isBuffer(input)) { + return new Buffer(0); + } + _entryHeader.loadDataHeaderFromBinary(input); + return input.slice(_entryHeader.realDataOffset, _entryHeader.realDataOffset + _entryHeader.compressedSize) + } + + function crc32OK(data) { + // if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written + if (_entryHeader.flags & 0x8 != 0x8) { + if (Utils.crc32(data) != _entryHeader.crc) { + return false; + } + } else { + // @TODO: load and check data descriptor header + // The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure + // (optionally preceded by a 4-byte signature) immediately after the compressed data: + } + return true; + } + + function decompress(/*Boolean*/async, /*Function*/callback, /*String*/pass) { + if(typeof callback === 'undefined' && typeof async === 'string') { + pass=async; + async=void 0; + } + if (_isDirectory) { + if (async && callback) { + callback(new Buffer(0), Utils.Errors.DIRECTORY_CONTENT_ERROR); //si added error. + } + return new Buffer(0); + } + + var compressedData = getCompressedDataFromZip(); + + if (compressedData.length == 0) { + if (async && callback) callback(compressedData, Utils.Errors.NO_DATA);//si added error. + return compressedData; + } + + var data = new Buffer(_entryHeader.size); + data.fill(0); + + switch (_entryHeader.method) { + case Utils.Constants.STORED: + compressedData.copy(data); + if (!crc32OK(data)) { + if (async && callback) callback(data, Utils.Errors.BAD_CRC);//si added error + return Utils.Errors.BAD_CRC; + } else {//si added otherwise did not seem to return data. + if (async && callback) callback(data); + return data; + } + break; + case Utils.Constants.DEFLATED: + var inflater = new Methods.Inflater(compressedData); + if (!async) { + inflater.inflate(data); + if (!crc32OK(data)) { + console.warn(Utils.Errors.BAD_CRC + " " + _entryName.toString()) + } + return data; + } else { + inflater.inflateAsync(function(result) { + result.copy(data, 0); + if (!crc32OK(data)) { + if (callback) callback(data, Utils.Errors.BAD_CRC); //si added error + } else { //si added otherwise did not seem to return data. + if (callback) callback(data); + } + }) + } + break; + default: + if (async && callback) callback(new Buffer(0), Utils.Errors.UNKNOWN_METHOD); + return Utils.Errors.UNKNOWN_METHOD; + } + } + + function compress(/*Boolean*/async, /*Function*/callback) { + if ((!uncompressedData || !uncompressedData.length) && Buffer.isBuffer(input)) { + // no data set or the data wasn't changed to require recompression + if (async && callback) callback(getCompressedDataFromZip()); + return getCompressedDataFromZip(); + } + + if (uncompressedData.length && !_isDirectory) { + var compressedData; + // Local file header + switch (_entryHeader.method) { + case Utils.Constants.STORED: + _entryHeader.compressedSize = _entryHeader.size; + + compressedData = new Buffer(uncompressedData.length); + uncompressedData.copy(compressedData); + + if (async && callback) callback(compressedData); + return compressedData; + + break; + default: + case Utils.Constants.DEFLATED: + + var deflater = new Methods.Deflater(uncompressedData); + if (!async) { + var deflated = deflater.deflate(); + _entryHeader.compressedSize = deflated.length; + return deflated; + } else { + deflater.deflateAsync(function(data) { + compressedData = new Buffer(data.length); + _entryHeader.compressedSize = data.length; + data.copy(compressedData); + callback && callback(compressedData); + }) + } + deflater = null; + break; + } + } else { + if (async && callback) { + callback(new Buffer(0)); + } else { + return new Buffer(0); + } + } + } + + function readUInt64LE(buffer, offset) { + return (buffer.readUInt32LE(offset + 4) << 4) + buffer.readUInt32LE(offset); + } + + function parseExtra(data) { + var offset = 0; + var signature, size, part; + while(offset= Constants.EF_ZIP64_SCOMP) { + size = readUInt64LE(data, Constants.EF_ZIP64_SUNCOMP); + if(_entryHeader.size === Constants.EF_ZIP64_OR_32) { + _entryHeader.size = size; + } + } + if(data.length >= Constants.EF_ZIP64_RHO) { + compressedSize = readUInt64LE(data, Constants.EF_ZIP64_SCOMP); + if(_entryHeader.compressedSize === Constants.EF_ZIP64_OR_32) { + _entryHeader.compressedSize = compressedSize; + } + } + if(data.length >= Constants.EF_ZIP64_DSN) { + offset = readUInt64LE(data, Constants.EF_ZIP64_RHO); + if(_entryHeader.offset === Constants.EF_ZIP64_OR_32) { + _entryHeader.offset = offset; + } + } + if(data.length >= Constants.EF_ZIP64_DSN+4) { + diskNumStart = data.readUInt32LE(Constants.EF_ZIP64_DSN); + if(_entryHeader.diskNumStart === Constants.EF_ZIP64_OR_16) { + _entryHeader.diskNumStart = diskNumStart; + } + } + } + + + return { + get entryName () { return _entryName.toString(); }, + get rawEntryName() { return _entryName; }, + set entryName (val) { + _entryName = Utils.toBuffer(val); + var lastChar = _entryName[_entryName.length - 1]; + _isDirectory = (lastChar == 47) || (lastChar == 92); + _entryHeader.fileNameLength = _entryName.length; + }, + + get extra () { return _extra; }, + set extra (val) { + _extra = val; + _entryHeader.extraLength = val.length; + parseExtra(val); + }, + + get comment () { return _comment.toString(); }, + set comment (val) { + _comment = Utils.toBuffer(val); + _entryHeader.commentLength = _comment.length; + }, + + get name () { var n = _entryName.toString(); return _isDirectory ? n.substr(n.length - 1).split("/").pop() : n.split("/").pop(); }, + get isDirectory () { return _isDirectory }, + + getCompressedData : function() { + return compress(false, null) + }, + + getCompressedDataAsync : function(/*Function*/callback) { + compress(true, callback) + }, + + setData : function(value) { + uncompressedData = Utils.toBuffer(value); + if (!_isDirectory && uncompressedData.length) { + _entryHeader.size = uncompressedData.length; + _entryHeader.method = Utils.Constants.DEFLATED; + _entryHeader.crc = Utils.crc32(value); + } else { // folders and blank files should be stored + _entryHeader.method = Utils.Constants.STORED; + } + }, + + getData : function(pass) { + return decompress(false, null, pass); + }, + + getDataAsync : function(/*Function*/callback, pass) { + decompress(true, callback, pass) + }, + + set attr(attr) { _entryHeader.attr = attr; }, + get attr() { return _entryHeader.attr; }, + + set header(/*Buffer*/data) { + _entryHeader.loadFromBinary(data); + }, + + get header() { + return _entryHeader; + }, + + packHeader : function() { + var header = _entryHeader.entryHeaderToBinary(); + // add + _entryName.copy(header, Utils.Constants.CENHDR); + if (_entryHeader.extraLength) { + _extra.copy(header, Utils.Constants.CENHDR + _entryName.length) + } + if (_entryHeader.commentLength) { + _comment.copy(header, Utils.Constants.CENHDR + _entryName.length + _entryHeader.extraLength, _comment.length); + } + return header; + }, + + toString : function() { + return '{\n' + + '\t"entryName" : "' + _entryName.toString() + "\",\n" + + '\t"name" : "' + _entryName.toString().split("/").pop() + "\",\n" + + '\t"comment" : "' + _comment.toString() + "\",\n" + + '\t"isDirectory" : ' + _isDirectory + ",\n" + + '\t"header" : ' + _entryHeader.toString().replace(/\t/mg, "\t\t") + ",\n" + + '\t"compressedData" : <' + (input && input.length + " bytes buffer" || "null") + ">\n" + + '\t"data" : <' + (uncompressedData && uncompressedData.length + " bytes buffer" || "null") + ">\n" + + '}'; + } + } +}; diff --git a/platforms/browser/cordova/node_modules/adm-zip/zipFile.js b/platforms/browser/cordova/node_modules/adm-zip/zipFile.js new file mode 100644 index 0000000..794afdb --- /dev/null +++ b/platforms/browser/cordova/node_modules/adm-zip/zipFile.js @@ -0,0 +1,311 @@ +var ZipEntry = require("./zipEntry"), + Headers = require("./headers"), + Utils = require("./util"); + +module.exports = function(/*String|Buffer*/input, /*Number*/inputType) { + var entryList = [], + entryTable = {}, + _comment = new Buffer(0), + filename = "", + fs = require("fs"), + inBuffer = null, + mainHeader = new Headers.MainHeader(); + + if (inputType == Utils.Constants.FILE) { + // is a filename + filename = input; + inBuffer = fs.readFileSync(filename); + readMainHeader(); + } else if (inputType == Utils.Constants.BUFFER) { + // is a memory buffer + inBuffer = input; + readMainHeader(); + } else { + // none. is a new file + } + + function readEntries() { + entryTable = {}; + entryList = new Array(mainHeader.diskEntries); // total number of entries + var index = mainHeader.offset; // offset of first CEN header + for(var i = 0; i < entryList.length; i++) { + + var tmp = index, + entry = new ZipEntry(inBuffer); + entry.header = inBuffer.slice(tmp, tmp += Utils.Constants.CENHDR); + + entry.entryName = inBuffer.slice(tmp, tmp += entry.header.fileNameLength); + + if (entry.header.extraLength) { + entry.extra = inBuffer.slice(tmp, tmp += entry.header.extraLength); + } + + if (entry.header.commentLength) + entry.comment = inBuffer.slice(tmp, tmp + entry.header.commentLength); + + index += entry.header.entryHeaderSize; + + entryList[i] = entry; + entryTable[entry.entryName] = entry; + } + } + + function readMainHeader() { + var i = inBuffer.length - Utils.Constants.ENDHDR, // END header size + n = Math.max(0, i - 0xFFFF), // 0xFFFF is the max zip file comment length + endOffset = -1; // Start offset of the END header + + for (i; i >= n; i--) { + if (inBuffer[i] != 0x50) continue; // quick check that the byte is 'P' + if (inBuffer.readUInt32LE(i) == Utils.Constants.ENDSIG) { // "PK\005\006" + endOffset = i; + break; + } + } + if (!~endOffset) + throw Utils.Errors.INVALID_FORMAT; + + mainHeader.loadFromBinary(inBuffer.slice(endOffset, endOffset + Utils.Constants.ENDHDR)); + if (mainHeader.commentLength) { + _comment = inBuffer.slice(endOffset + Utils.Constants.ENDHDR); + } + readEntries(); + } + + return { + /** + * Returns an array of ZipEntry objects existent in the current opened archive + * @return Array + */ + get entries () { + return entryList; + }, + + /** + * Archive comment + * @return {String} + */ + get comment () { return _comment.toString(); }, + set comment(val) { + mainHeader.commentLength = val.length; + _comment = val; + }, + + /** + * Returns a reference to the entry with the given name or null if entry is inexistent + * + * @param entryName + * @return ZipEntry + */ + getEntry : function(/*String*/entryName) { + return entryTable[entryName] || null; + }, + + /** + * Adds the given entry to the entry list + * + * @param entry + */ + setEntry : function(/*ZipEntry*/entry) { + entryList.push(entry); + entryTable[entry.entryName] = entry; + mainHeader.totalEntries = entryList.length; + }, + + /** + * Removes the entry with the given name from the entry list. + * + * If the entry is a directory, then all nested files and directories will be removed + * @param entryName + */ + deleteEntry : function(/*String*/entryName) { + var entry = entryTable[entryName]; + if (entry && entry.isDirectory) { + var _self = this; + this.getEntryChildren(entry).forEach(function(child) { + if (child.entryName != entryName) { + _self.deleteEntry(child.entryName) + } + }) + } + entryList.splice(entryList.indexOf(entry), 1); + delete(entryTable[entryName]); + mainHeader.totalEntries = entryList.length; + }, + + /** + * Iterates and returns all nested files and directories of the given entry + * + * @param entry + * @return Array + */ + getEntryChildren : function(/*ZipEntry*/entry) { + if (entry.isDirectory) { + var list = [], + name = entry.entryName, + len = name.length; + + entryList.forEach(function(zipEntry) { + if (zipEntry.entryName.substr(0, len) == name) { + list.push(zipEntry); + } + }); + return list; + } + return [] + }, + + /** + * Returns the zip file + * + * @return Buffer + */ + compressToBuffer : function() { + if (entryList.length > 1) { + entryList.sort(function(a, b) { + var nameA = a.entryName.toLowerCase(); + var nameB = b.entryName.toLowerCase(); + if (nameA < nameB) {return -1} + if (nameA > nameB) {return 1} + return 0; + }); + } + + var totalSize = 0, + dataBlock = [], + entryHeaders = [], + dindex = 0; + + mainHeader.size = 0; + mainHeader.offset = 0; + + entryList.forEach(function(entry) { + entry.header.offset = dindex; + + // compress data and set local and entry header accordingly. Reason why is called first + var compressedData = entry.getCompressedData(); + // data header + var dataHeader = entry.header.dataHeaderToBinary(); + var postHeader = new Buffer(entry.entryName + entry.extra.toString()); + var dataLength = dataHeader.length + postHeader.length + compressedData.length; + + dindex += dataLength; + + dataBlock.push(dataHeader); + dataBlock.push(postHeader); + dataBlock.push(compressedData); + + var entryHeader = entry.packHeader(); + entryHeaders.push(entryHeader); + mainHeader.size += entryHeader.length; + totalSize += (dataLength + entryHeader.length); + }); + + totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length + // point to end of data and begining of central directory first record + mainHeader.offset = dindex; + + dindex = 0; + var outBuffer = new Buffer(totalSize); + dataBlock.forEach(function(content) { + content.copy(outBuffer, dindex); // write data blocks + dindex += content.length; + }); + entryHeaders.forEach(function(content) { + content.copy(outBuffer, dindex); // write central directory entries + dindex += content.length; + }); + + var mh = mainHeader.toBinary(); + if (_comment) { + _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment + } + + mh.copy(outBuffer, dindex); // write main header + + return outBuffer + }, + + toAsyncBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) { + if (entryList.length > 1) { + entryList.sort(function(a, b) { + var nameA = a.entryName.toLowerCase(); + var nameB = b.entryName.toLowerCase(); + if (nameA > nameB) {return -1} + if (nameA < nameB) {return 1} + return 0; + }); + } + + var totalSize = 0, + dataBlock = [], + entryHeaders = [], + dindex = 0; + + mainHeader.size = 0; + mainHeader.offset = 0; + + var compress=function(entryList){ + var self=arguments.callee; + var entry; + if(entryList.length){ + var entry=entryList.pop(); + var name=entry.entryName + entry.extra.toString(); + if(onItemStart)onItemStart(name); + entry.getCompressedDataAsync(function(compressedData){ + if(onItemEnd)onItemEnd(name); + + entry.header.offset = dindex; + // data header + var dataHeader = entry.header.dataHeaderToBinary(); + var postHeader = new Buffer(name); + var dataLength = dataHeader.length + postHeader.length + compressedData.length; + + dindex += dataLength; + + dataBlock.push(dataHeader); + dataBlock.push(postHeader); + dataBlock.push(compressedData); + + var entryHeader = entry.packHeader(); + entryHeaders.push(entryHeader); + mainHeader.size += entryHeader.length; + totalSize += (dataLength + entryHeader.length); + + if(entryList.length){ + self(entryList); + }else{ + + + totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length + // point to end of data and begining of central directory first record + mainHeader.offset = dindex; + + dindex = 0; + var outBuffer = new Buffer(totalSize); + dataBlock.forEach(function(content) { + content.copy(outBuffer, dindex); // write data blocks + dindex += content.length; + }); + entryHeaders.forEach(function(content) { + content.copy(outBuffer, dindex); // write central directory entries + dindex += content.length; + }); + + var mh = mainHeader.toBinary(); + if (_comment) { + _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment + } + + mh.copy(outBuffer, dindex); // write main header + + onSuccess(outBuffer); + } + }); + } + }; + + compress(entryList); + } + } +}; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/.jshintrc b/platforms/browser/cordova/node_modules/cordova-serve/.jshintrc new file mode 100644 index 0000000..6997763 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/.jshintrc @@ -0,0 +1,11 @@ +{ + "node": true, + "bitwise": true, + "undef": true, + "trailing": true, + "quotmark": true, + "indent": 4, + "unused": "vars", + "latedef": "nofunc", + "-W030": false +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/README.md b/platforms/browser/cordova/node_modules/cordova-serve/README.md new file mode 100644 index 0000000..4d45447 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/README.md @@ -0,0 +1,80 @@ + + +# cordova-serve +This module can be used to serve up a Cordova application in the browser. It has no command-line, but rather is intended +to be called using the following API: + +``` js +var serve = require('cordova-serve'); +serve.launchServer(opts); +serve.servePlatform(platform, opts); +serve.launchBrowser(ops); +``` + +## launchServer() + +``` js +launchServer(opts); +``` + +Launches a server with the specified options. Parameters: + +* **opts**: Options, as described below. + +## servePlatform() + +``` js +servePlatform(platform, opts); +``` + +Launches a server that serves up any Cordova platform (e.g. `browser`, `android` etc) from the current project. +Parameters: + +* **opts**: Options, as described below. Note that for `servePlatform()`, the `root` value should be a Cordova project's + root folder, or any folder within it - `servePlatform()` will replace it with the platform's `www_dir` folder. If this + value is not specified, the *cwd* will be used. + +## launchBrowser() + +``` js +launchBrowser(opts); +``` + +Launches a browser window pointing to the specified URL. The single parameter is an options object that supports the +following values (both optional): + +* **url**: The URL to open in the browser. +* **target**: The name of the browser to launch. Can be any of the following: `chrome`, `chromium`, `firefox`, `ie`, + `opera`, `safari`. If no browser is specified, + +## The *opts* Options Object +The opts object passed to `launchServer()` and `servePlatform()` supports the following values (all optional): + +* **root**: The file path on the local file system that is used as the root for the server, for default mapping of URL + path to local file system path. +* **port**: The port for the server. Note that if this port is already in use, it will be incremented until a free port + is found. +* **router**: An `ExpressJS` router. If provided, this will be attached *before* default static handling. +* **noLogOutput**: If `true`, turns off all log output. +* **noServerInfo**: If `true`, cordova-serve won't output `Static file server running on...` message. +* **events**: An `EventEmitter` to use for logging. If provided, logging will be output using `events.emit('log', msg)`. + If not provided, `console.log()` will be used. Note that nothing will be output in either case if `noLogOutput` is `true`. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/RELEASENOTES.md b/platforms/browser/cordova/node_modules/cordova-serve/RELEASENOTES.md new file mode 100644 index 0000000..18ddd81 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/RELEASENOTES.md @@ -0,0 +1,33 @@ + +# Cordova-serve Release Notes + +### 1.0.0 (Oct 05, 2015) +* Refactor cordova-serve to use Express. + +### 0.1.3 (Aug 22, 2015) +* Clean up cordova-serve console output. +* CB-9546 cordova-serve.servePlatform() should provide project folders +* CB-9545 Cordova-serve's 'noCache' option does not work in IE. +* Add support for --target=edge to launch app in Edge browser. + +### 0.1.2 (June 15, 2015) +Initial release diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/index.js new file mode 100644 index 0000000..2d85a91 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/index.js @@ -0,0 +1,116 @@ +'use strict'; +var escapeStringRegexp = require('escape-string-regexp'); +var ansiStyles = require('ansi-styles'); +var stripAnsi = require('strip-ansi'); +var hasAnsi = require('has-ansi'); +var supportsColor = require('supports-color'); +var defineProps = Object.defineProperties; +var isSimpleWindowsTerm = process.platform === 'win32' && !/^xterm/i.test(process.env.TERM); + +function Chalk(options) { + // detect mode if not set manually + this.enabled = !options || options.enabled === undefined ? supportsColor : options.enabled; +} + +// use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001b[94m'; +} + +var styles = (function () { + var ret = {}; + + Object.keys(ansiStyles).forEach(function (key) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); + + ret[key] = { + get: function () { + return build.call(this, this._styles.concat(key)); + } + }; + }); + + return ret; +})(); + +var proto = defineProps(function chalk() {}, styles); + +function build(_styles) { + var builder = function () { + return applyStyle.apply(builder, arguments); + }; + + builder._styles = _styles; + builder.enabled = this.enabled; + // __proto__ is used because we must return a function, but there is + // no way to create a function with a different prototype. + /* eslint-disable no-proto */ + builder.__proto__ = proto; + + return builder; +} + +function applyStyle() { + // support varags, but simply cast to string in case there's only one arg + var args = arguments; + var argsLen = args.length; + var str = argsLen !== 0 && String(arguments[0]); + + if (argsLen > 1) { + // don't slice `arguments`, it prevents v8 optimizations + for (var a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } + + if (!this.enabled || !str) { + return str; + } + + var nestedStyles = this._styles; + var i = nestedStyles.length; + + // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, + // see https://github.com/chalk/chalk/issues/58 + // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. + var originalDim = ansiStyles.dim.open; + if (isSimpleWindowsTerm && (nestedStyles.indexOf('gray') !== -1 || nestedStyles.indexOf('grey') !== -1)) { + ansiStyles.dim.open = ''; + } + + while (i--) { + var code = ansiStyles[nestedStyles[i]]; + + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + str = code.open + str.replace(code.closeRe, code.open) + code.close; + } + + // Reset the original 'dim' if we changed it to work around the Windows dimmed gray issue. + ansiStyles.dim.open = originalDim; + + return str; +} + +function init() { + var ret = {}; + + Object.keys(styles).forEach(function (name) { + ret[name] = { + get: function () { + return build.call(this, [name]); + } + }; + }); + + return ret; +} + +defineProps(Chalk.prototype, init()); + +module.exports = new Chalk(); +module.exports.styles = ansiStyles; +module.exports.hasColor = hasAnsi; +module.exports.stripColor = stripAnsi; +module.exports.supportsColor = supportsColor; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/index.js new file mode 100644 index 0000000..e917d61 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/index.js @@ -0,0 +1,131 @@ +'use strict'; +var colorConvert = require('color-convert'); + +function wrapAnsi16(fn, offset) { + return function () { + var code = fn.apply(colorConvert, arguments); + return '\u001b[' + (code + offset) + 'm'; + }; +} + +function wrapAnsi256(fn, offset) { + return function () { + var code = fn.apply(colorConvert, arguments); + return '\u001b[' + (38 + offset) + ';5;' + code + 'm'; + }; +} + +function wrapAnsi16m(fn, offset) { + return function () { + var rgb = fn.apply(colorConvert, arguments); + return '\u001b[' + (38 + offset) + ';2;' + + rgb[0] + ';' + rgb[1] + ';' + rgb[2] + 'm'; + }; +} + +function assembleStyles() { + var styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + gray: [90, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49] + } + }; + + // fix humans + styles.color.grey = styles.color.gray; + + Object.keys(styles).forEach(function (groupName) { + var group = styles[groupName]; + + Object.keys(group).forEach(function (styleName) { + var style = group[styleName]; + + styles[styleName] = group[styleName] = { + open: '\u001b[' + style[0] + 'm', + close: '\u001b[' + style[1] + 'm' + }; + }); + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + }); + + function rgb2rgb(r, g, b) { + return [r, g, b]; + } + + styles.color.close = '\u001b[39m'; + styles.bgColor.close = '\u001b[49m'; + + styles.color.ansi = {}; + styles.color.ansi256 = {}; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; + + styles.bgColor.ansi = {}; + styles.bgColor.ansi256 = {}; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; + + for (var key in colorConvert) { + if (!colorConvert.hasOwnProperty(key) || typeof colorConvert[key] !== 'object') { + continue; + } + + var suite = colorConvert[key]; + + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } + + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } + + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } + } + + return styles; +} + +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/CHANGELOG.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/CHANGELOG.md new file mode 100644 index 0000000..0a7bce4 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/CHANGELOG.md @@ -0,0 +1,54 @@ +# 1.0.0 - 2016-01-07 + +- Removed: unused speed test +- Added: Automatic routing between previously unsupported conversions +([#27](https://github.com/Qix-/color-convert/pull/27)) +- Removed: `xxx2xxx()` and `xxx2xxxRaw()` functions +([#27](https://github.com/Qix-/color-convert/pull/27)) +- Removed: `convert()` class +([#27](https://github.com/Qix-/color-convert/pull/27)) +- Changed: all functions to lookup dictionary +([#27](https://github.com/Qix-/color-convert/pull/27)) +- Changed: `ansi` to `ansi256` +([#27](https://github.com/Qix-/color-convert/pull/27)) +- Fixed: argument grouping for functions requiring only one argument +([#27](https://github.com/Qix-/color-convert/pull/27)) + +# 0.6.0 - 2015-07-23 + +- Added: methods to handle +[ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) 16/256 colors: + - rgb2ansi16 + - rgb2ansi + - hsl2ansi16 + - hsl2ansi + - hsv2ansi16 + - hsv2ansi + - hwb2ansi16 + - hwb2ansi + - cmyk2ansi16 + - cmyk2ansi + - keyword2ansi16 + - keyword2ansi + - ansi162rgb + - ansi162hsl + - ansi162hsv + - ansi162hwb + - ansi162cmyk + - ansi162keyword + - ansi2rgb + - ansi2hsl + - ansi2hsv + - ansi2hwb + - ansi2cmyk + - ansi2keyword +([#18](https://github.com/harthur/color-convert/pull/18)) + +# 0.5.3 - 2015-06-02 + +- Fixed: hsl2hsv does not return `NaN` anymore when using `[0,0,0]` +([#15](https://github.com/harthur/color-convert/issues/15)) + +--- + +Check out commit logs for older releases diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/LICENSE b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/LICENSE new file mode 100644 index 0000000..a8b08d4 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2011 Heather Arthur + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/README.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/README.md new file mode 100644 index 0000000..effe3f0 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/README.md @@ -0,0 +1,62 @@ +# color-convert + +[![Build Status](https://travis-ci.org/MoOx/color-convert.svg?branch=master)](https://travis-ci.org/MoOx/color-convert) + +Color-convert is a color conversion library for JavaScript and node. +It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s: + +```js +var convert = require('color-convert'); + +convert.rgb.hsl(140, 200, 100); // [96, 48, 59] +convert.keyword.rgb('blue'); // [0, 0, 255] +``` + +# Install + +```console +$ npm install color-convert +``` + +# API + +Simply get the property of the _from_ and _to_ conversion that you're looking for. + +All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function. + +```js +var convert = require('color-convert'); + +// Hex to LAB +convert.hex.lab('DEADBF'); // [ 76, 21, -2 ] +convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ] + +// RGB to CMYK +convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ] +convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ] +``` + +### Arrays +All functions that accept multiple arguments also support passing an array. + +Not that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.) + +```js +var convert = require('color-convert'); + +convert.rgb.hex(123, 45, 67); // '7B2D43' +convert.rgb.hex([123, 45, 67]); // '7B2D43' +``` + +## Routing + +Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex). + +Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js). + +# Contribute + +If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request. + +# License +Copyright © 2011-2016, Heather Arthur and Josh Junon. Licensed under the [MIT License](LICENSE). diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/conversions.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/conversions.js new file mode 100644 index 0000000..d0aa959 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/conversions.js @@ -0,0 +1,594 @@ +/* MIT license */ +var cssKeywords = require('./css-keywords'); + +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) + +var reverseKeywords = {}; +for (var key in cssKeywords) { + if (cssKeywords.hasOwnProperty(key)) { + reverseKeywords[cssKeywords[key].join()] = key; + } +} + +var convert = module.exports = { + rgb: {}, + hsl: {}, + hsv: {}, + hwb: {}, + cmyk: {}, + xyz: {}, + lab: {}, + lch: {}, + hex: {}, + keyword: {}, + ansi16: {}, + ansi256: {} +}; + +convert.rgb.hsl = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; +}; + +convert.rgb.hsv = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var v; + + if (max === 0) { + s = 0; + } else { + s = (delta / max * 1000) / 10; + } + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + v = ((max / 255) * 1000) / 10; + + return [h, s, v]; +}; + +convert.rgb.hwb = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); + + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +}; + +convert.rgb.cmyk = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; +}; + +convert.rgb.keyword = function (rgb) { + return reverseKeywords[rgb.join()]; +}; + +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; + +convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y * 100, z * 100]; +}; + +convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.hsl.rgb = function (hsl) { + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var val; + + if (s === 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + if (t3 > 1) { + t3--; + } + + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } + + rgb[i] = val * 255; + } + + return rgb; +}; + +convert.hsl.hsv = function (hsl) { + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var sv; + var v; + + if (l === 0) { + // no need to do calc on black + // also avoids divide by 0 error + return [0, 0, 0]; + } + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + v = (l + s) / 2; + sv = (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; +}; + +convert.hsv.rgb = function (hsv) { + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; + + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; + +convert.hsv.hsl = function (hsv) { + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var sl; + var l; + + l = (2 - s) * v; + sl = s * v; + sl /= (l <= 1) ? l : 2 - l; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; +}; + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + + if ((i & 0x01) !== 0) { + f = 1 - f; + } + + n = wh + f * (v - wh); // linear interpolation + + var r; + var g; + var b; + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +}; + +convert.cmyk.rgb = function (cmyk) { + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.rgb = function (xyz) { + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r *= 12.92; + + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g *= 12.92; + + b = b > 0.0031308 + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b *= 12.92; + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.lab.xyz = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; + var y2; + + if (l <= 8) { + y = (l * 100) / 903.3; + y2 = (7.787 * (y / 100)) + (16 / 116); + } else { + y = 100 * Math.pow((l + 16) / 116, 3); + y2 = Math.pow(y / 100, 1 / 3); + } + + x = x / 95.047 <= 0.008856 + ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 + : 95.047 * Math.pow((a / 500) + y2, 3); + z = z / 108.883 <= 0.008859 + ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 + : 108.883 * Math.pow(y2 - (b / 200), 3); + + return [x, y, z]; +}; + +convert.lab.lch = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + + if (h < 0) { + h += 360; + } + + c = Math.sqrt(a * a + b * b); + + return [l, c, h]; +}; + +convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + + return [l, a, b]; +}; + +convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; +}; + +convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + + // we use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; +}; + +convert.ansi16.rgb = function (args) { + var color = args % 10; + + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; +}; + +convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; + + return [r, g, b]; +}; + +convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.hex.rgb = function (args) { + var match = args.toString(16).match(/[a-f0-9]{6}/i); + if (!match) { + return [0, 0, 0]; + } + + var integer = parseInt(match[0], 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + + return [r, g, b]; +}; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/css-keywords.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/css-keywords.js new file mode 100644 index 0000000..495ca56 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/css-keywords.js @@ -0,0 +1,151 @@ +module.exports = { + aliceblue: [240, 248, 255], + antiquewhite: [250, 235, 215], + aqua: [0, 255, 255], + aquamarine: [127, 255, 212], + azure: [240, 255, 255], + beige: [245, 245, 220], + bisque: [255, 228, 196], + black: [0, 0, 0], + blanchedalmond: [255, 235, 205], + blue: [0, 0, 255], + blueviolet: [138, 43, 226], + brown: [165, 42, 42], + burlywood: [222, 184, 135], + cadetblue: [95, 158, 160], + chartreuse: [127, 255, 0], + chocolate: [210, 105, 30], + coral: [255, 127, 80], + cornflowerblue: [100, 149, 237], + cornsilk: [255, 248, 220], + crimson: [220, 20, 60], + cyan: [0, 255, 255], + darkblue: [0, 0, 139], + darkcyan: [0, 139, 139], + darkgoldenrod: [184, 134, 11], + darkgray: [169, 169, 169], + darkgreen: [0, 100, 0], + darkgrey: [169, 169, 169], + darkkhaki: [189, 183, 107], + darkmagenta: [139, 0, 139], + darkolivegreen: [85, 107, 47], + darkorange: [255, 140, 0], + darkorchid: [153, 50, 204], + darkred: [139, 0, 0], + darksalmon: [233, 150, 122], + darkseagreen: [143, 188, 143], + darkslateblue: [72, 61, 139], + darkslategray: [47, 79, 79], + darkslategrey: [47, 79, 79], + darkturquoise: [0, 206, 209], + darkviolet: [148, 0, 211], + deeppink: [255, 20, 147], + deepskyblue: [0, 191, 255], + dimgray: [105, 105, 105], + dimgrey: [105, 105, 105], + dodgerblue: [30, 144, 255], + firebrick: [178, 34, 34], + floralwhite: [255, 250, 240], + forestgreen: [34, 139, 34], + fuchsia: [255, 0, 255], + gainsboro: [220, 220, 220], + ghostwhite: [248, 248, 255], + gold: [255, 215, 0], + goldenrod: [218, 165, 32], + gray: [128, 128, 128], + green: [0, 128, 0], + greenyellow: [173, 255, 47], + grey: [128, 128, 128], + honeydew: [240, 255, 240], + hotpink: [255, 105, 180], + indianred: [205, 92, 92], + indigo: [75, 0, 130], + ivory: [255, 255, 240], + khaki: [240, 230, 140], + lavender: [230, 230, 250], + lavenderblush: [255, 240, 245], + lawngreen: [124, 252, 0], + lemonchiffon: [255, 250, 205], + lightblue: [173, 216, 230], + lightcoral: [240, 128, 128], + lightcyan: [224, 255, 255], + lightgoldenrodyellow: [250, 250, 210], + lightgray: [211, 211, 211], + lightgreen: [144, 238, 144], + lightgrey: [211, 211, 211], + lightpink: [255, 182, 193], + lightsalmon: [255, 160, 122], + lightseagreen: [32, 178, 170], + lightskyblue: [135, 206, 250], + lightslategray: [119, 136, 153], + lightslategrey: [119, 136, 153], + lightsteelblue: [176, 196, 222], + lightyellow: [255, 255, 224], + lime: [0, 255, 0], + limegreen: [50, 205, 50], + linen: [250, 240, 230], + magenta: [255, 0, 255], + maroon: [128, 0, 0], + mediumaquamarine: [102, 205, 170], + mediumblue: [0, 0, 205], + mediumorchid: [186, 85, 211], + mediumpurple: [147, 112, 219], + mediumseagreen: [60, 179, 113], + mediumslateblue: [123, 104, 238], + mediumspringgreen: [0, 250, 154], + mediumturquoise: [72, 209, 204], + mediumvioletred: [199, 21, 133], + midnightblue: [25, 25, 112], + mintcream: [245, 255, 250], + mistyrose: [255, 228, 225], + moccasin: [255, 228, 181], + navajowhite: [255, 222, 173], + navy: [0, 0, 128], + oldlace: [253, 245, 230], + olive: [128, 128, 0], + olivedrab: [107, 142, 35], + orange: [255, 165, 0], + orangered: [255, 69, 0], + orchid: [218, 112, 214], + palegoldenrod: [238, 232, 170], + palegreen: [152, 251, 152], + paleturquoise: [175, 238, 238], + palevioletred: [219, 112, 147], + papayawhip: [255, 239, 213], + peachpuff: [255, 218, 185], + peru: [205, 133, 63], + pink: [255, 192, 203], + plum: [221, 160, 221], + powderblue: [176, 224, 230], + purple: [128, 0, 128], + rebeccapurple: [102, 51, 153], + red: [255, 0, 0], + rosybrown: [188, 143, 143], + royalblue: [65, 105, 225], + saddlebrown: [139, 69, 19], + salmon: [250, 128, 114], + sandybrown: [244, 164, 96], + seagreen: [46, 139, 87], + seashell: [255, 245, 238], + sienna: [160, 82, 45], + silver: [192, 192, 192], + skyblue: [135, 206, 235], + slateblue: [106, 90, 205], + slategray: [112, 128, 144], + slategrey: [112, 128, 144], + snow: [255, 250, 250], + springgreen: [0, 255, 127], + steelblue: [70, 130, 180], + tan: [210, 180, 140], + teal: [0, 128, 128], + thistle: [216, 191, 216], + tomato: [255, 99, 71], + turquoise: [64, 224, 208], + violet: [238, 130, 238], + wheat: [245, 222, 179], + white: [255, 255, 255], + whitesmoke: [245, 245, 245], + yellow: [255, 255, 0], + yellowgreen: [154, 205, 50] +}; + diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/index.js new file mode 100644 index 0000000..b3418f6 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/index.js @@ -0,0 +1,75 @@ +var conversions = require('./conversions'); +var route = require('./route'); + +var convert = {}; + +var models = Object.keys(conversions); + +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + return fn(args); + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + var result = fn(args); + + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(function (fromModel) { + convert[fromModel] = {}; + + var routes = route(fromModel); + var routeModels = Object.keys(routes); + + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +module.exports = convert; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/package.json new file mode 100644 index 0000000..fab11d7 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/package.json @@ -0,0 +1,84 @@ +{ + "name": "color-convert", + "description": "Plain color conversion functions", + "version": "1.0.0", + "author": { + "name": "Heather Arthur", + "email": "fayearthur@gmail.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/qix-/color-convert.git" + }, + "scripts": { + "pretest": "xo", + "test": "node test/basic.js" + }, + "keywords": [ + "color", + "colour", + "convert", + "converter", + "conversion", + "rgb", + "hsl", + "hsv", + "hwb", + "cmyk", + "ansi", + "ansi16" + ], + "files": [ + "index.js", + "conversions.js", + "css-keywords.js", + "route.js" + ], + "xo": { + "rules": { + "default-case": 0, + "no-inline-comments": 0, + "operator-linebreak": 0 + } + }, + "devDependencies": { + "chalk": "^1.1.1", + "xo": "^0.11.2" + }, + "gitHead": "31cd56dc3d34ae332cc83d90bd4f925baf5bd982", + "bugs": { + "url": "https://github.com/qix-/color-convert/issues" + }, + "homepage": "https://github.com/qix-/color-convert#readme", + "_id": "color-convert@1.0.0", + "_shasum": "3c26fcd885d272d45beacf6e41baba75c89a8579", + "_from": "color-convert@>=1.0.0 <2.0.0", + "_npmVersion": "3.3.6", + "_nodeVersion": "4.1.1", + "_npmUser": { + "name": "qix", + "email": "i.am.qix@gmail.com" + }, + "dist": { + "shasum": "3c26fcd885d272d45beacf6e41baba75c89a8579", + "tarball": "http://registry.npmjs.org/color-convert/-/color-convert-1.0.0.tgz" + }, + "maintainers": [ + { + "name": "harth", + "email": "fayearthur@gmail.com" + }, + { + "name": "moox", + "email": "m@moox.io" + }, + { + "name": "qix", + "email": "i.am.qix@gmail.com" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/route.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/route.js new file mode 100644 index 0000000..c365e1e --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/route.js @@ -0,0 +1,98 @@ +var conversions = require('./conversions'); + +/* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +// https://jsperf.com/object-keys-vs-for-in-with-closure/3 +var models = Object.keys(conversions); + +function buildGraph() { + var graph = {}; + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); + + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +module.exports = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/package.json new file mode 100644 index 0000000..3367a23 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/package.json @@ -0,0 +1,88 @@ +{ + "name": "ansi-styles", + "version": "2.2.0", + "description": "ANSI escape codes for styling strings in the terminal", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-styles.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "color-convert": "^1.0.0" + }, + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "gitHead": "74502955deaf0eb977507757e33c52cad5a9aefa", + "bugs": { + "url": "https://github.com/chalk/ansi-styles/issues" + }, + "homepage": "https://github.com/chalk/ansi-styles", + "_id": "ansi-styles@2.2.0", + "_shasum": "c59191936e6ed1c1315a4b6b6b97f3acfbfa68b0", + "_from": "ansi-styles@>=2.1.0 <3.0.0", + "_npmVersion": "2.14.12", + "_nodeVersion": "4.2.6", + "_npmUser": { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + }, + "dist": { + "shasum": "c59191936e6ed1c1315a4b6b6b97f3acfbfa68b0", + "tarball": "http://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.0.tgz" + }, + "_npmOperationalInternal": { + "host": "packages-5-east.internal.npmjs.com", + "tmp": "tmp/ansi-styles-2.2.0.tgz_1456057673117_0.8365559694357216" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/readme.md new file mode 100644 index 0000000..b87b124 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/ansi-styles/readme.md @@ -0,0 +1,114 @@ +# ansi-styles [![Build Status](https://travis-ci.org/chalk/ansi-styles.svg?branch=master)](https://travis-ci.org/chalk/ansi-styles) + +> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal + +You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings. + +![](screenshot.png) + + +## Install + +``` +$ npm install --save ansi-styles +``` + + +## Usage + +```js +const style = require('ansi-styles'); + +console.log(style.green.open + 'Hello world!' + style.green.close); + +// color conversion between 16/256/truecolor +// NOTE: if conversion goes to 16 colors or 256 colors, the original color +// may be degraded to fit that color palette. This means terminals +// that do not support 16 million colors will best-match the +// original color. +console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close); +console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close); +console.log(style.color.ansi16m.hex('#ABCDEF') + 'Hello world!' + style.color.close); +``` + +## API + +Each style has an `open` and `close` property. + + +## Styles + +### Modifiers + +- `reset` +- `bold` +- `dim` +- `italic` *(not widely supported)* +- `underline` +- `inverse` +- `hidden` +- `strikethrough` *(not widely supported)* + +### Colors + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `magenta` +- `cyan` +- `white` +- `gray` + +### Background colors + +- `bgBlack` +- `bgRed` +- `bgGreen` +- `bgYellow` +- `bgBlue` +- `bgMagenta` +- `bgCyan` +- `bgWhite` + + +## Advanced usage + +By default you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module. + +- `style.modifier` +- `style.color` +- `style.bgColor` + + +###### Example + +```js +console.log(style.color.green.open); +``` + +## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728) +`ansi-styles` uses the [`color-convert`](https://github.com/MoOx/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors. + +To use these, call the associated conversion function with the intended output, e.g.: + +```js +style.color.ansi.rgb(100, 200, 15); // RGB to 16 color ansi foreground code +style.bgColor.ansi.rgb(100, 200, 15); // RGB to 16 color ansi background code + +style.color.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code +style.bgColor.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code + +style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground code +style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code +``` + +## Related + +- [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/index.js new file mode 100644 index 0000000..7834bf9 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/index.js @@ -0,0 +1,11 @@ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/package.json new file mode 100644 index 0000000..7a8a3f7 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/package.json @@ -0,0 +1,75 @@ +{ + "name": "escape-string-regexp", + "version": "1.0.5", + "description": "Escape RegExp special characters", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/escape-string-regexp.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.8.0" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "escape", + "regex", + "regexp", + "re", + "regular", + "expression", + "string", + "str", + "special", + "characters" + ], + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "gitHead": "db124a3e1aae9d692c4899e42a5c6c3e329eaa20", + "bugs": { + "url": "https://github.com/sindresorhus/escape-string-regexp/issues" + }, + "homepage": "https://github.com/sindresorhus/escape-string-regexp", + "_id": "escape-string-regexp@1.0.5", + "_shasum": "1b61c0562190a8dff6ae3bb2cf0200ca130b86d4", + "_from": "escape-string-regexp@>=1.0.2 <2.0.0", + "_npmVersion": "2.14.12", + "_nodeVersion": "4.2.6", + "_npmUser": { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + }, + "dist": { + "shasum": "1b61c0562190a8dff6ae3bb2cf0200ca130b86d4", + "tarball": "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "_npmOperationalInternal": { + "host": "packages-9-west.internal.npmjs.com", + "tmp": "tmp/escape-string-regexp-1.0.5.tgz_1456059312074_0.7245344955008477" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/readme.md new file mode 100644 index 0000000..87ac82d --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/escape-string-regexp/readme.md @@ -0,0 +1,27 @@ +# escape-string-regexp [![Build Status](https://travis-ci.org/sindresorhus/escape-string-regexp.svg?branch=master)](https://travis-ci.org/sindresorhus/escape-string-regexp) + +> Escape RegExp special characters + + +## Install + +``` +$ npm install --save escape-string-regexp +``` + + +## Usage + +```js +const escapeStringRegexp = require('escape-string-regexp'); + +const escapedString = escapeStringRegexp('how much $ for a unicorn?'); +//=> 'how much \$ for a unicorn\?' + +new RegExp(escapedString); +``` + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/index.js new file mode 100644 index 0000000..98fae06 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/index.js @@ -0,0 +1,4 @@ +'use strict'; +var ansiRegex = require('ansi-regex'); +var re = new RegExp(ansiRegex().source); // remove the `g` flag +module.exports = re.test.bind(re); diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/index.js new file mode 100644 index 0000000..4906755 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/index.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports = function () { + return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; +}; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/package.json new file mode 100644 index 0000000..7fc0767 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/package.json @@ -0,0 +1,86 @@ +{ + "name": "ansi-regex", + "version": "2.0.0", + "description": "Regular expression for matching ANSI escape codes", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/ansi-regex.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha test/test.js", + "view-supported": "node test/viewCodes.js" + }, + "files": [ + "index.js" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "devDependencies": { + "mocha": "*" + }, + "gitHead": "57c3f2941a73079fa8b081e02a522e3d29913e2f", + "bugs": { + "url": "https://github.com/sindresorhus/ansi-regex/issues" + }, + "homepage": "https://github.com/sindresorhus/ansi-regex", + "_id": "ansi-regex@2.0.0", + "_shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107", + "_from": "ansi-regex@>=2.0.0 <3.0.0", + "_npmVersion": "2.11.2", + "_nodeVersion": "0.12.5", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "dist": { + "shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107", + "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/readme.md new file mode 100644 index 0000000..1a4894e --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/readme.md @@ -0,0 +1,31 @@ +# ansi-regex [![Build Status](https://travis-ci.org/sindresorhus/ansi-regex.svg?branch=master)](https://travis-ci.org/sindresorhus/ansi-regex) + +> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code) + + +## Install + +``` +$ npm install --save ansi-regex +``` + + +## Usage + +```js +var ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001b[4mcake\u001b[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001b[4mcake\u001b[0m'.match(ansiRegex()); +//=> ['\u001b[4m', '\u001b[0m'] +``` + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/package.json new file mode 100644 index 0000000..d39a62e --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/package.json @@ -0,0 +1,85 @@ +{ + "name": "has-ansi", + "version": "2.0.0", + "description": "Check if a string has ANSI escape codes", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/has-ansi.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "node test.js" + }, + "files": [ + "index.js" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern", + "has" + ], + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "devDependencies": { + "ava": "0.0.4" + }, + "gitHead": "0722275e1bef139fcd09137da6e5550c3cd368b9", + "bugs": { + "url": "https://github.com/sindresorhus/has-ansi/issues" + }, + "homepage": "https://github.com/sindresorhus/has-ansi", + "_id": "has-ansi@2.0.0", + "_shasum": "34f5049ce1ecdf2b0649af3ef24e45ed35416d91", + "_from": "has-ansi@>=2.0.0 <3.0.0", + "_npmVersion": "2.11.2", + "_nodeVersion": "0.12.5", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "dist": { + "shasum": "34f5049ce1ecdf2b0649af3ef24e45ed35416d91", + "tarball": "http://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/readme.md new file mode 100644 index 0000000..02bc7c2 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/has-ansi/readme.md @@ -0,0 +1,36 @@ +# has-ansi [![Build Status](https://travis-ci.org/sindresorhus/has-ansi.svg?branch=master)](https://travis-ci.org/sindresorhus/has-ansi) + +> Check if a string has [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code) + + +## Install + +``` +$ npm install --save has-ansi +``` + + +## Usage + +```js +var hasAnsi = require('has-ansi'); + +hasAnsi('\u001b[4mcake\u001b[0m'); +//=> true + +hasAnsi('cake'); +//=> false +``` + + +## Related + +- [has-ansi-cli](https://github.com/sindresorhus/has-ansi-cli) - CLI for this module +- [strip-ansi](https://github.com/sindresorhus/strip-ansi) - Strip ANSI escape codes +- [ansi-regex](https://github.com/sindresorhus/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/sindresorhus/chalk) - Terminal string styling done right + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/index.js new file mode 100644 index 0000000..099480f --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/index.js @@ -0,0 +1,6 @@ +'use strict'; +var ansiRegex = require('ansi-regex')(); + +module.exports = function (str) { + return typeof str === 'string' ? str.replace(ansiRegex, '') : str; +}; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/index.js new file mode 100644 index 0000000..4906755 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/index.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports = function () { + return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; +}; diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/package.json new file mode 100644 index 0000000..7fc0767 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/package.json @@ -0,0 +1,86 @@ +{ + "name": "ansi-regex", + "version": "2.0.0", + "description": "Regular expression for matching ANSI escape codes", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/ansi-regex.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha test/test.js", + "view-supported": "node test/viewCodes.js" + }, + "files": [ + "index.js" + ], + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "devDependencies": { + "mocha": "*" + }, + "gitHead": "57c3f2941a73079fa8b081e02a522e3d29913e2f", + "bugs": { + "url": "https://github.com/sindresorhus/ansi-regex/issues" + }, + "homepage": "https://github.com/sindresorhus/ansi-regex", + "_id": "ansi-regex@2.0.0", + "_shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107", + "_from": "ansi-regex@>=2.0.0 <3.0.0", + "_npmVersion": "2.11.2", + "_nodeVersion": "0.12.5", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "dist": { + "shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107", + "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/readme.md new file mode 100644 index 0000000..1a4894e --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/readme.md @@ -0,0 +1,31 @@ +# ansi-regex [![Build Status](https://travis-ci.org/sindresorhus/ansi-regex.svg?branch=master)](https://travis-ci.org/sindresorhus/ansi-regex) + +> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code) + + +## Install + +``` +$ npm install --save ansi-regex +``` + + +## Usage + +```js +var ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001b[4mcake\u001b[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001b[4mcake\u001b[0m'.match(ansiRegex()); +//=> ['\u001b[4m', '\u001b[0m'] +``` + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/package.json new file mode 100644 index 0000000..a6bde1e --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/package.json @@ -0,0 +1,90 @@ +{ + "name": "strip-ansi", + "version": "3.0.1", + "description": "Strip ANSI escape codes", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "gitHead": "8270705c704956da865623e564eba4875c3ea17f", + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "homepage": "https://github.com/chalk/strip-ansi", + "_id": "strip-ansi@3.0.1", + "_shasum": "6a385fb8853d952d5ff05d0e8aaf94278dc63dcf", + "_from": "strip-ansi@>=3.0.0 <4.0.0", + "_npmVersion": "2.11.3", + "_nodeVersion": "0.12.7", + "_npmUser": { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + }, + "dist": { + "shasum": "6a385fb8853d952d5ff05d0e8aaf94278dc63dcf", + "tarball": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + }, + "_npmOperationalInternal": { + "host": "packages-9-west.internal.npmjs.com", + "tmp": "tmp/strip-ansi-3.0.1.tgz_1456057278183_0.28958667791448534" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/readme.md new file mode 100644 index 0000000..cb7d9ff --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/strip-ansi/readme.md @@ -0,0 +1,33 @@ +# strip-ansi [![Build Status](https://travis-ci.org/chalk/strip-ansi.svg?branch=master)](https://travis-ci.org/chalk/strip-ansi) + +> Strip [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code) + + +## Install + +``` +$ npm install --save strip-ansi +``` + + +## Usage + +```js +var stripAnsi = require('strip-ansi'); + +stripAnsi('\u001b[4mcake\u001b[0m'); +//=> 'cake' +``` + + +## Related + +- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/index.js b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/index.js new file mode 100644 index 0000000..4346e27 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/index.js @@ -0,0 +1,50 @@ +'use strict'; +var argv = process.argv; + +var terminator = argv.indexOf('--'); +var hasFlag = function (flag) { + flag = '--' + flag; + var pos = argv.indexOf(flag); + return pos !== -1 && (terminator !== -1 ? pos < terminator : true); +}; + +module.exports = (function () { + if ('FORCE_COLOR' in process.env) { + return true; + } + + if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + return false; + } + + if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + return true; + } + + if (process.stdout && !process.stdout.isTTY) { + return false; + } + + if (process.platform === 'win32') { + return true; + } + + if ('COLORTERM' in process.env) { + return true; + } + + if (process.env.TERM === 'dumb') { + return false; + } + + if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { + return true; + } + + return false; +})(); diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/license b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/package.json new file mode 100644 index 0000000..38a1ecb --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/package.json @@ -0,0 +1,79 @@ +{ + "name": "supports-color", + "version": "2.0.0", + "description": "Detect whether a terminal supports color", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/supports-color.git" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + } + ], + "engines": { + "node": ">=0.8.0" + }, + "scripts": { + "test": "mocha" + }, + "files": [ + "index.js" + ], + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect" + ], + "devDependencies": { + "mocha": "*", + "require-uncached": "^1.0.2" + }, + "gitHead": "8400d98ade32b2adffd50902c06d9e725a5c6588", + "bugs": { + "url": "https://github.com/chalk/supports-color/issues" + }, + "homepage": "https://github.com/chalk/supports-color", + "_id": "supports-color@2.0.0", + "_shasum": "535d045ce6b6363fa40117084629995e9df324c7", + "_from": "supports-color@>=2.0.0 <3.0.0", + "_npmVersion": "2.11.2", + "_nodeVersion": "0.12.5", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "dist": { + "shasum": "535d045ce6b6363fa40117084629995e9df324c7", + "tarball": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/readme.md new file mode 100644 index 0000000..b4761f1 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/node_modules/supports-color/readme.md @@ -0,0 +1,36 @@ +# supports-color [![Build Status](https://travis-ci.org/chalk/supports-color.svg?branch=master)](https://travis-ci.org/chalk/supports-color) + +> Detect whether a terminal supports color + + +## Install + +``` +$ npm install --save supports-color +``` + + +## Usage + +```js +var supportsColor = require('supports-color'); + +if (supportsColor) { + console.log('Terminal supports color'); +} +``` + +It obeys the `--color` and `--no-color` CLI flags. + +For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`. + + +## Related + +- [supports-color-cli](https://github.com/chalk/supports-color-cli) - CLI for this module +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## License + +MIT © [Sindre Sorhus](http://sindresorhus.com) diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/package.json b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/package.json new file mode 100644 index 0000000..dc5e754 --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/package.json @@ -0,0 +1,103 @@ +{ + "name": "chalk", + "version": "1.1.1", + "description": "Terminal string styling done right. Much color.", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/chalk.git" + }, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "jbnicolai", + "email": "jappelman@xebia.com" + }, + { + "name": "unicorn", + "email": "sindresorhus+unicorn@gmail.com" + } + ], + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "xo && mocha", + "bench": "matcha benchmark.js", + "coverage": "nyc npm test && nyc report", + "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls" + }, + "files": [ + "index.js" + ], + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "str", + "ansi", + "style", + "styles", + "tty", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "dependencies": { + "ansi-styles": "^2.1.0", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "devDependencies": { + "coveralls": "^2.11.2", + "matcha": "^0.6.0", + "mocha": "*", + "nyc": "^3.0.0", + "require-uncached": "^1.0.2", + "resolve-from": "^1.0.0", + "semver": "^4.3.3", + "xo": "*" + }, + "xo": { + "envs": [ + "node", + "mocha" + ] + }, + "gitHead": "8b554e254e89c85c1fd04dcc444beeb15824e1a5", + "bugs": { + "url": "https://github.com/chalk/chalk/issues" + }, + "homepage": "https://github.com/chalk/chalk#readme", + "_id": "chalk@1.1.1", + "_shasum": "509afb67066e7499f7eb3535c77445772ae2d019", + "_from": "chalk@>=1.1.1 <2.0.0", + "_npmVersion": "2.13.5", + "_nodeVersion": "0.12.7", + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "dist": { + "shasum": "509afb67066e7499f7eb3535c77445772ae2d019", + "tarball": "http://registry.npmjs.org/chalk/-/chalk-1.1.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/readme.md b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/readme.md new file mode 100644 index 0000000..5cf111e --- /dev/null +++ b/platforms/browser/cordova/node_modules/cordova-serve/node_modules/chalk/readme.md @@ -0,0 +1,213 @@ +

+
+
+ chalk +
+
+
+

+ +> Terminal string styling done right + +[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk) +[![Coverage Status](https://coveralls.io/repos/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/r/chalk/chalk?branch=master) +[![](http://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4) + + +[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough. + +**Chalk is a clean and focused alternative.** + +![](https://github.com/chalk/ansi-styles/raw/master/screenshot.png) + + +## Why + +- Highly performant +- Doesn't extend `String.prototype` +- Expressive API +- Ability to nest styles +- Clean and focused +- Auto-detects color support +- Actively maintained +- [Used by ~4500 modules](https://www.npmjs.com/browse/depended/chalk) as of July 15, 2015 + + +## Install + +``` +$ npm install --save chalk +``` + + +## Usage + +Chalk comes with an easy to use composable API where you just chain and nest the styles you want. + +```js +var chalk = require('chalk'); + +// style a string +chalk.blue('Hello world!'); + +// combine styled and normal strings +chalk.blue('Hello') + 'World' + chalk.red('!'); + +// compose multiple styles using the chainable API +chalk.blue.bgRed.bold('Hello world!'); + +// pass in multiple arguments +chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz'); + +// nest styles +chalk.red('Hello', chalk.underline.bgBlue('world') + '!'); + +// nest styles of the same type even (color, underline, background) +chalk.green( + 'I am a green line ' + + chalk.blue.underline.bold('with a blue substring') + + ' that becomes green again!' +); +``` + +Easily define your own themes. + +```js +var chalk = require('chalk'); +var error = chalk.bold.red; +console.log(error('Error!')); +``` + +Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data). + +```js +var name = 'Sindre'; +console.log(chalk.green('Hello %s'), name); +//=> Hello Sindre +``` + + +## API + +### chalk.` + +
+ +
+
+ +
+
+
+
+
+
+ + diff --git a/plugins/cordova-plugin-camera/www/blackberry10/assets/camera.js b/plugins/cordova-plugin-camera/www/blackberry10/assets/camera.js new file mode 100644 index 0000000..8a245e5 --- /dev/null +++ b/plugins/cordova-plugin-camera/www/blackberry10/assets/camera.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. + * + */ + +document.addEventListener('DOMContentLoaded', function () { + document.getElementById('back').onclick = function () { + window.qnx.callExtensionMethod('cordova-plugin-camera', 'cancel'); + }; + window.navigator.webkitGetUserMedia( + { video: true }, + function (stream) { + var video = document.getElementById('v'), + canvas = document.getElementById('c'), + camera = document.getElementById('camera'); + video.autoplay = true; + video.width = window.innerWidth; + video.height = window.innerHeight - 100; + video.src = window.webkitURL.createObjectURL(stream); + camera.onclick = function () { + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + canvas.getContext('2d').drawImage(video, 0, 0, video.videoWidth, video.videoHeight); + window.qnx.callExtensionMethod('cordova-plugin-camera', canvas.toDataURL('img/png')); + }; + }, + function () { + window.qnx.callExtensionMethod('cordova-plugin-camera', 'error', 'getUserMedia failed'); + } + ); +}); diff --git a/plugins/cordova-plugin-camera/www/ios/CameraPopoverHandle.js b/plugins/cordova-plugin-camera/www/ios/CameraPopoverHandle.js new file mode 100644 index 0000000..3990e18 --- /dev/null +++ b/plugins/cordova-plugin-camera/www/ios/CameraPopoverHandle.js @@ -0,0 +1,60 @@ +/* + * + * 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'); + +/** + * @namespace navigator + */ + +/** + * A handle to an image picker popover. + * + * __Supported Platforms__ + * + * - iOS + * + * @example + * var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail, + * { + * destinationType: Camera.DestinationType.FILE_URI, + * sourceType: Camera.PictureSourceType.PHOTOLIBRARY, + * popoverOptions: new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY) + * }); + * + * // Reposition the popover if the orientation changes. + * window.onorientationchange = function() { + * var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY); + * cameraPopoverHandle.setPosition(cameraPopoverOptions); + * } + * @module CameraPopoverHandle + */ +var CameraPopoverHandle = function() { + /** Set the position of the popover. + * @param {module:CameraPopoverOptions} popoverOptions + */ + this.setPosition = function(popoverOptions) { + var args = [ popoverOptions ]; + exec(null, null, "Camera", "repositionPopover", args); + }; +}; + +module.exports = CameraPopoverHandle; diff --git a/plugins/cordova-plugin-compat/README.md b/plugins/cordova-plugin-compat/README.md new file mode 100644 index 0000000..095c384 --- /dev/null +++ b/plugins/cordova-plugin-compat/README.md @@ -0,0 +1,31 @@ + + +cordova-plugin-compat +------------------------ + +This repo is for remaining backwards compatible with previous versions of Cordova. + +## USAGE + +Your plugin can depend on this plugin and use it to handle the new run time permissions Android 6.0.0 (cordova-android 5.0.0) introduced. + +View [this commit](https://github.com/apache/cordova-plugin-camera/commit/a9c18710f23e86f5b7f8918dfab7c87a85064870) to see how to depend on `cordova-plugin-compat`. View [this file](https://github.com/apache/cordova-plugin-camera/blob/master/src/android/CameraLauncher.java) to see how `PermissionHelper` is being used to request and store permissions. Read more about Android permissions at http://cordova.apache.org/docs/en/latest/guide/platforms/android/plugin.html#android-permissions. diff --git a/plugins/cordova-plugin-compat/RELEASENOTES.md b/plugins/cordova-plugin-compat/RELEASENOTES.md new file mode 100644 index 0000000..b7aa129 --- /dev/null +++ b/plugins/cordova-plugin-compat/RELEASENOTES.md @@ -0,0 +1,29 @@ + +# Release Notes + +### 1.1.0 (Nov 02, 2016) +* [CB-11625](https://issues.apache.org/jira/browse/CB-11625) Adding the `BuildConfig` fetching code as a backup to using a new preference +* Add github pull request template + +### 1.0.0 (Apr 15, 2016) +* Initial release +* Moved `PermissionHelper.java` into `src` diff --git a/plugins/cordova-plugin-compat/package.json b/plugins/cordova-plugin-compat/package.json new file mode 100644 index 0000000..f31174d --- /dev/null +++ b/plugins/cordova-plugin-compat/package.json @@ -0,0 +1,32 @@ +{ + "name": "cordova-plugin-compat", + "description": "This repo is for remaining backwards compatible with previous versions of Cordova.", + "version": "1.1.0", + "homepage": "http://github.com/apache/cordova-plugin-compat#readme", + "repository": { + "type": "git", + "url": "git://github.com/apache/cordova-plugin-compat.git" + }, + "bugs": { + "url": "https://github.com/apache/cordova-plugin-compat/issues" + }, + "cordova": { + "id": "cordova-plugin-compat", + "platforms": [ + "android" + ] + }, + "keywords": [ + "ecosystem:cordova", + "ecosystem:phonegap", + "cordova-android" + ], + "engines": [ + { + "name": "cordova", + "version": ">=5.0.0" + } + ], + "author": "Apache Software Foundation", + "license": "Apache-2.0" +} diff --git a/plugins/cordova-plugin-compat/plugin.xml b/plugins/cordova-plugin-compat/plugin.xml new file mode 100644 index 0000000..40b45cc --- /dev/null +++ b/plugins/cordova-plugin-compat/plugin.xml @@ -0,0 +1,37 @@ + + + + + + Compat + Cordova Compatibility Plugin + Apache 2.0 + cordova,compat + https://git-wip-us.apache.org/repos/asf/cordova-plugin-compat.git + + + + + + + + diff --git a/plugins/cordova-plugin-compat/src/android/BuildHelper.java b/plugins/cordova-plugin-compat/src/android/BuildHelper.java new file mode 100644 index 0000000..d9b18aa --- /dev/null +++ b/plugins/cordova-plugin-compat/src/android/BuildHelper.java @@ -0,0 +1,70 @@ +/* + 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; + +/* + * This is a utility class that allows us to get the BuildConfig variable, which is required + * for the use of different providers. This is not guaranteed to work, and it's better for this + * to be set in the build step in config.xml + * + */ + +import android.app.Activity; +import android.content.Context; + +import java.lang.reflect.Field; + + +public class BuildHelper { + + + private static String TAG="BuildHelper"; + + /* + * This needs to be implemented if you wish to use the Camera Plugin or other plugins + * that read the Build Configuration. + * + * Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to + * StackOverflow. This is annoying as hell! However, this method does not work with + * ProGuard, and you should use the config.xml to define the application_id + * + */ + + public static Object getBuildConfigValue(Context ctx, String key) + { + try + { + Class clazz = Class.forName(ctx.getPackageName() + ".BuildConfig"); + Field field = clazz.getField(key); + return field.get(null); + } catch (ClassNotFoundException e) { + LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?"); + e.printStackTrace(); + } catch (NoSuchFieldException e) { + LOG.d(TAG, key + " is not a valid field. Check your build.gradle"); + } catch (IllegalAccessException e) { + LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace."); + e.printStackTrace(); + } + + return null; + } + +} diff --git a/plugins/cordova-plugin-compat/src/android/PermissionHelper.java b/plugins/cordova-plugin-compat/src/android/PermissionHelper.java new file mode 100644 index 0000000..bbd7911 --- /dev/null +++ b/plugins/cordova-plugin-compat/src/android/PermissionHelper.java @@ -0,0 +1,138 @@ +/* + 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; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; + +import android.content.pm.PackageManager; + +/** + * This class provides reflective methods for permission requesting and checking so that plugins + * written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions. + */ +public class PermissionHelper { + private static final String LOG_TAG = "CordovaPermissionHelper"; + + /** + * Requests a "dangerous" permission for the application at runtime. This is a helper method + * alternative to cordovaInterface.requestPermission() that does not require the project to be + * built with cordova-android 5.0.0+ + * + * @param plugin The plugin the permission is being requested for + * @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult() + * along with the result of the permission request + * @param permission The permission to be requested + */ + public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) { + PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission}); + } + + /** + * Requests "dangerous" permissions for the application at runtime. This is a helper method + * alternative to cordovaInterface.requestPermissions() that does not require the project to be + * built with cordova-android 5.0.0+ + * + * @param plugin The plugin the permissions are being requested for + * @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult() + * along with the result of the permissions request + * @param permissions The permissions to be requested + */ + public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) { + try { + Method requestPermission = CordovaInterface.class.getDeclaredMethod( + "requestPermissions", CordovaPlugin.class, int.class, String[].class); + + // If there is no exception, then this is cordova-android 5.0.0+ + requestPermission.invoke(plugin.cordova, plugin, requestCode, permissions); + } catch (NoSuchMethodException noSuchMethodException) { + // cordova-android version is less than 5.0.0, so permission is implicitly granted + LOG.d(LOG_TAG, "No need to request permissions " + Arrays.toString(permissions)); + + // Notify the plugin that all were granted by using more reflection + deliverPermissionResult(plugin, requestCode, permissions); + } catch (IllegalAccessException illegalAccessException) { + // Should never be caught; this is a public method + LOG.e(LOG_TAG, "IllegalAccessException when requesting permissions " + Arrays.toString(permissions), illegalAccessException); + } catch(InvocationTargetException invocationTargetException) { + // This method does not throw any exceptions, so this should never be caught + LOG.e(LOG_TAG, "invocationTargetException when requesting permissions " + Arrays.toString(permissions), invocationTargetException); + } + } + + /** + * Checks at runtime to see if the application has been granted a permission. This is a helper + * method alternative to cordovaInterface.hasPermission() that does not require the project to + * be built with cordova-android 5.0.0+ + * + * @param plugin The plugin the permission is being checked against + * @param permission The permission to be checked + * + * @return True if the permission has already been granted and false otherwise + */ + public static boolean hasPermission(CordovaPlugin plugin, String permission) { + try { + Method hasPermission = CordovaInterface.class.getDeclaredMethod("hasPermission", String.class); + + // If there is no exception, then this is cordova-android 5.0.0+ + return (Boolean) hasPermission.invoke(plugin.cordova, permission); + } catch (NoSuchMethodException noSuchMethodException) { + // cordova-android version is less than 5.0.0, so permission is implicitly granted + LOG.d(LOG_TAG, "No need to check for permission " + permission); + return true; + } catch (IllegalAccessException illegalAccessException) { + // Should never be caught; this is a public method + LOG.e(LOG_TAG, "IllegalAccessException when checking permission " + permission, illegalAccessException); + } catch(InvocationTargetException invocationTargetException) { + // This method does not throw any exceptions, so this should never be caught + LOG.e(LOG_TAG, "invocationTargetException when checking permission " + permission, invocationTargetException); + } + return false; + } + + private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) { + // Generate the request results + int[] requestResults = new int[permissions.length]; + Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED); + + try { + Method onRequestPermissionResult = CordovaPlugin.class.getDeclaredMethod( + "onRequestPermissionResult", int.class, String[].class, int[].class); + + onRequestPermissionResult.invoke(plugin, requestCode, permissions, requestResults); + } catch (NoSuchMethodException noSuchMethodException) { + // Should never be caught since the plugin must be written for cordova-android 5.0.0+ if it + // made it to this point + LOG.e(LOG_TAG, "NoSuchMethodException when delivering permissions results", noSuchMethodException); + } catch (IllegalAccessException illegalAccessException) { + // Should never be caught; this is a public method + LOG.e(LOG_TAG, "IllegalAccessException when delivering permissions results", illegalAccessException); + } catch(InvocationTargetException invocationTargetException) { + // This method may throw a JSONException. We are just duplicating cordova-android's + // exception handling behavior here; all it does is log the exception in CordovaActivity, + // print the stacktrace, and ignore it + LOG.e(LOG_TAG, "InvocationTargetException when delivering permissions results", invocationTargetException); + } + } +} diff --git a/plugins/cordova-plugin-console/CONTRIBUTING.md b/plugins/cordova-plugin-console/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-console/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-console/LICENSE b/plugins/cordova-plugin-console/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-console/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/plugins/cordova-plugin-console/NOTICE b/plugins/cordova-plugin-console/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-console/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/plugins/cordova-plugin-console/README.md b/plugins/cordova-plugin-console/README.md new file mode 100644 index 0000000..ebd0939 --- /dev/null +++ b/plugins/cordova-plugin-console/README.md @@ -0,0 +1,109 @@ +--- +title: Console +description: Get JavaScript logs in your native logs. +--- + + +|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI | +|:-:|:-:|:-:|:-:|:-:|:-:| +|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-console)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-console/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-console)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-console/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-console)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-console/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-console)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-console/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-console)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-console/)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-console) + +# cordova-plugin-console + +This plugin is meant to ensure that console.log() is as useful as it can be. +It adds additional function for iOS, Ubuntu, Windows Phone 8, and Windows. If +you are happy with how console.log() works for you, then you probably +don't need this plugin. + +This plugin defines a global `console` object. + +Although the object is in the global scope, features provided by this plugin +are not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + +:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Console%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + + +## Installation + + cordova plugin add cordova-plugin-console + +### Android Quirks + +On some platforms other than Android, console.log() will act on multiple +arguments, such as console.log("1", "2", "3"). However, Android will act only +on the first argument. Subsequent arguments to console.log() will be ignored. +This plugin is not the cause of that, it is a limitation of Android itself. + +## Supported Methods + +The plugin support following methods of the `console` object: + +- `console.log` +- `console.error` +- `console.exception` +- `console.warn` +- `console.info` +- `console.debug` +- `console.assert` +- `console.dir` +- `console.dirxml` +- `console.time` +- `console.timeEnd` +- `console.table` + +## Partially supported Methods + +Methods of the `console` object which implemented, but behave different from browser implementation: + +- `console.group` +- `console.groupCollapsed` + +The grouping methods are just log name of the group and don't actually indicate grouping for later +calls to `console` object methods. + +## Not supported Methods + +Methods of the `console` object which are implemented, but do nothing: + +- `console.clear` +- `console.trace` +- `console.groupEnd` +- `console.timeStamp` +- `console.profile` +- `console.profileEnd` +- `console.count` + +## Supported formatting + +The following formatting options available: + +Format chars: + +* `%j` - format arg as JSON +* `%o` - format arg as JSON +* `%c` - format arg as `''`. No color formatting could be done. +* `%%` - replace with `'%'` + +Any other char following `%` will format its arg via `toString()`. diff --git a/plugins/cordova-plugin-console/RELEASENOTES.md b/plugins/cordova-plugin-console/RELEASENOTES.md new file mode 100644 index 0000000..582b739 --- /dev/null +++ b/plugins/cordova-plugin-console/RELEASENOTES.md @@ -0,0 +1,207 @@ + +# Release Notes + +### 1.0.5 (Dec 07, 2016) +* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Updated version and RELEASENOTES.md for release 1.0.4 +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* add JIRA issue tracker link +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md +* [CB-11091](https://issues.apache.org/jira/browse/CB-11091) Incremented plugin version. +* Updated version and RELEASENOTES.md for release 1.0.3 +* [CB-10720](https://issues.apache.org/jira/browse/CB-10720) Minor spelling/formatting changes. +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins +* chore: edit package.json license to match SPDX id +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Incremented plugin version. +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) linked issues in RELEASENOTES.md +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated version and RELEASENOTES.md for release 1.0.2 +* removed r prefix from tags +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated RELEASENOTES to be newest to oldest +* Actually fixing the contribute link. +* Fixing contribute link. +* Document formatting options for the console object +* [CB-5089](https://issues.apache.org/jira/browse/CB-5089) Document supported methods for console object +* reverted d58f218b9149d362ebb0b8ce697cf403569d14cd because logger is not needed on android +* remove travi-ci integration +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Incremented plugin version. +* [CB-9202](https://issues.apache.org/jira/browse/CB-9202) updated repo url to github mirror in package.json +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Updated version and RELEASENOTES.md for release 1.0.1 +* move logger.js and console-via-logger.js to common modules, instead of the numerous repeats that were there. +* clean up tests, info is below log level so it does not exist by default. +* add a couple tests +* [CB-9191](https://issues.apache.org/jira/browse/CB-9191) Add basic test +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-console documentation translation: cordova-plugin-console +* attempt to fix npm markdown issue +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version in package.json for release 1.0.0 +* Revert "CB-8858 Incremented plugin version." +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version and RELEASENOTES.md for release 1.0.0 +* [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) changed plugin-id to pacakge-name +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) 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: renamed Windows8 to Windows +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* [CB-8560](https://issues.apache.org/jira/browse/CB-8560) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-console documentation translation: cordova-plugin-console +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8362](https://issues.apache.org/jira/browse/CB-8362) Add Windows platform section to Console plugin +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Incremented plugin version. +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.2.13 +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) Use argumentForIndex rather than NSArray extension +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Incremented plugin version. +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Updated version and RELEASENOTES.md for release 0.2.12 +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention deviceready in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-console documentation translation: cordova-plugin-console +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Incremented plugin version. +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Updated version and RELEASENOTES.md for release 0.2.11 +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-console documentation translation: cordova-plugin-console +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Incremented plugin version. +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Updated version and RELEASENOTES.md for release 0.2.10 +* CB-7249cordova-plugin-console documentation translation: cordova-plugin-console +* CB-6127lisa6cordova-plugin-console documentation translation: cordova-plugin-console +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Incremented plugin version. +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Updated version and RELEASENOTES.md for release 0.2.9 +* [CB-6848](https://issues.apache.org/jira/browse/CB-6848) Add Android quirk, list applicable platforms +* [CB-6796](https://issues.apache.org/jira/browse/CB-6796) Add license +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Incremented plugin version on dev branch. +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Updated version and RELEASENOTES.md for release 0.2.8 +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460) Update license headers +* Add NOTICE file +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Incremented plugin version on dev branch. +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 0.2.7 +* Native console needs to be called DebugConsole to avoid ambiguous reference. This commit requires the 3.4.0 version of the native class factory +* [CB-4718](https://issues.apache.org/jira/browse/CB-4718) fixed Console plugin not work on wp +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Incremented plugin version on dev branch. +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Updated version and RELEASENOTES.md for release 0.2.6 +* [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 Console plugin +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Incremented plugin version on dev branch. +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Updated version and RELEASENOTES.md for release 0.2.5 +* add ubuntu platform +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) Updated version and RELEASENOTES.md for release 0.2.4 +* [CB-5154](https://issues.apache.org/jira/browse/CB-5154) log formatting incorrectly to native +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128) added repo + issue tag to plugin.xml for console plugin +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Updated version and RELEASENOTES.md for release 0.2.3 +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.console to org.apache.cordova.console +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. +* Add empty CHANGELOG.md +* [CB-4595](https://issues.apache.org/jira/browse/CB-4595) updated version +* reverted name element to be human readable +* updated name tag, namespace and readme +* [plugin.xml] standardizing license + meta +* [license] adding apache license file +* [Windows8] added support for windows8 [CB-4436](https://issues.apache.org/jira/browse/CB-4436) +* updating plugin.xml with registry data + +### 1.0.4 (Sep 08, 2016) +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to `cordovaDependencies` +* add `JIRA` issue tracker link +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md + +### 1.0.3 (Apr 15, 2016) +* [CB-10720](https://issues.apache.org/jira/browse/CB-10720) Minor spelling/formatting changes. +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins + +### 1.0.2 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* Fixing contribute link. +* Document formatting options for the console object +* [CB-5089](https://issues.apache.org/jira/browse/CB-5089) Document supported methods for console object +* reverted `d58f218b9149d362ebb0b8ce697cf403569d14cd` because `logger` is not needed on **Android** + +### 1.0.1 (Jun 17, 2015) +* move logger.js and console-via-logger.js to common modules, instead of the numerous repeats that were there. +* clean up tests, info is below log level so it does not exist by default. +* add a couple tests +* [CB-9191](https://issues.apache.org/jira/browse/CB-9191) Add basic test +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-console documentation translation: cordova-plugin-console +* attempt to fix npm markdown issue + +### 1.0.0 (Apr 15, 2015) +* [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) changed plugin-id to pacakge-name +* [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: renamed Windows8 to Windows +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* [CB-8560](https://issues.apache.org/jira/browse/CB-8560) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-console documentation translation: cordova-plugin-console +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8362](https://issues.apache.org/jira/browse/CB-8362) Add Windows platform section to Console plugin + +### 0.2.13 (Feb 04, 2015) +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension + +### 0.2.12 (Dec 02, 2014) +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-console documentation translation: cordova-plugin-console + +### 0.2.11 (Sep 17, 2014) +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-console documentation translation + +### 0.2.10 (Aug 06, 2014) +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs + +### 0.2.9 (Jun 05, 2014) +* [CB-6848](https://issues.apache.org/jira/browse/CB-6848) Add Android quirk, list applicable platforms +* [CB-6796](https://issues.apache.org/jira/browse/CB-6796) Add license +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md + +### 0.2.8 (Apr 17, 2014) +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* Add NOTICE file + +### 0.2.7 (Feb 05, 2014) +* Native console needs to be called DebugConsole to avoid ambiguous reference. This commit requires the 3.4.0 version of the native class factory +* [CB-4718](https://issues.apache.org/jira/browse/CB-4718) fixed Console plugin not working on wp + +### 0.2.6 (Jan 02, 2014) +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Console plugin + +### 0.2.5 (Dec 4, 2013) +* add ubuntu platform + +### 0.2.4 (Oct 28, 2013) +* [CB-5154](https://issues.apache.org/jira/browse/CB-5154) log formatting incorrectly to native +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for console plugin +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.2.3 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.console to org.apache.cordova.console +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. + + diff --git a/plugins/cordova-plugin-console/doc/de/README.md b/plugins/cordova-plugin-console/doc/de/README.md new file mode 100644 index 0000000..933c1b7 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/de/README.md @@ -0,0 +1,43 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +Dieses Plugin stellt sicher, dass der Befehl console.log() so hilfreich ist, wie er sein kann. Es fügt zusätzliche Funktion für iOS, Ubuntu, Windows Phone 8 und Windows. Teilweise kann es vorkommen, dass der Befehl console.log() nicht korrekt erkannt wird, und es zu Fehlern bzw. zu nicht angezeigten Logs in der Console kommt. Wenn Sie mit der derzeitigen Funktionsweise zufrieden sind, kann es sein, dass Sie dieses Plugin nicht benötigen. + +Dieses Plugin wird ein global-`console`-Objekt definiert. + +Obwohl das Objekt im globalen Gültigkeitsbereich ist, stehen Features von diesem Plugin nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## Installation + + cordova plugin add cordova-plugin-console + + +### Android Eigenarten + +Auf einigen Plattformen als Android fungieren console.log() auf mehrere Argumente wie console.log ("1", "2", "3"). Android wird jedoch nur auf das erste Argument fungieren. Nachfolgende Argumente zu console.log() werden ignoriert. Dieses Plugin ist nicht die Verantwortung dafür, es ist eine Einschränkung von Android selbst. \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/de/index.md b/plugins/cordova-plugin-console/doc/de/index.md new file mode 100644 index 0000000..9551782 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/de/index.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +Dieses Plugin stellt sicher, dass der Befehl console.log() so hilfreich ist, wie er sein kann. Es fügt zusätzliche Funktion für iOS, Ubuntu, Windows Phone 8 und Windows 8 hinzu. Teilweise kann es vorkommen, dass der Befehl console.log() nicht korrekt erkannt wird, und es zu Fehlern bzw. zu nicht angezeigten Logs in der Console kommt. Wenn Sie mit der derzeitigen Funktionsweise zufrieden sind, kann es sein, dass Sie dieses Plugin nicht benötigen. + +Dieses Plugin wird ein global-`console`-Objekt definiert. + +Obwohl das Objekt im globalen Gültigkeitsbereich ist, stehen Features von diesem Plugin nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## Installation + + cordova plugin add cordova-plugin-console + + +### Android Eigenarten + +Auf einigen Plattformen als Android fungieren console.log() auf mehrere Argumente wie console.log ("1", "2", "3"). Android wird jedoch nur auf das erste Argument fungieren. Nachfolgende Argumente zu console.log() werden ignoriert. Dieses Plugin ist nicht die Verantwortung dafür, es ist eine Einschränkung von Android selbst. diff --git a/plugins/cordova-plugin-console/doc/es/README.md b/plugins/cordova-plugin-console/doc/es/README.md new file mode 100644 index 0000000..b089d63 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/es/README.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +Este plugin es para asegurarse de que console.log() es tan útil como puede ser. Agrega la función adicional para iOS, Ubuntu, Windows Phone 8 y Windows. Si estás contento con cómo funciona console.log() para ti, entonces probablemente no necesitas este plugin. + +Este plugin define un global `console` objeto. + +Aunque el objeto está en el ámbito global, características proporcionadas por este plugin no están disponibles hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log ("console.log funciona bien");} + + +## Instalación + + cordova plugin add cordova-plugin-console + + +### Rarezas Android + +En algunas plataformas que no sean Android, console.log() actuará en varios argumentos, como console.log ("1", "2", "3"). Sin embargo, Android actuará sólo en el primer argumento. Se omitirá posteriores argumentos para console.log(). Este plugin no es la causa de eso, es una limitación propia de Android. \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/es/index.md b/plugins/cordova-plugin-console/doc/es/index.md new file mode 100644 index 0000000..e6b8e68 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/es/index.md @@ -0,0 +1,39 @@ + + +# cordova-plugin-console + +Este plugin es para asegurarse de que console.log() es tan útil como puede ser. Añade función adicional para iOS, Windows Phone 8, Ubuntu y Windows 8. Si estás contento con cómo funciona console.log() para ti, entonces probablemente no necesitas este plugin. + +Este plugin define un global `console` objeto. + +Aunque el objeto está en el ámbito global, características proporcionadas por este plugin no están disponibles hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log ("console.log funciona bien");} + + +## Instalación + + Cordova plugin agregar cordova-plugin-console + + +### Rarezas Android + +En algunas plataformas que no sean Android, console.log() actuará en varios argumentos, como console.log ("1", "2", "3"). Sin embargo, Android actuará sólo en el primer argumento. Se omitirá posteriores argumentos para console.log(). Este plugin no es la causa de eso, es una limitación propia de Android. diff --git a/plugins/cordova-plugin-console/doc/fr/README.md b/plugins/cordova-plugin-console/doc/fr/README.md new file mode 100644 index 0000000..74207ac --- /dev/null +++ b/plugins/cordova-plugin-console/doc/fr/README.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +Ce plugin est destiné à faire en sorte que console.log() est aussi utile que possible. Il ajoute une fonction supplémentaire pour iOS, Ubuntu, Windows Phone 8 et Windows. Si vous êtes satisfait du fonctionnement de console.log() pour vous, alors vous avez probablement pas besoin ce plugin. + +Ce plugin définit un global `console` objet. + +Bien que l'objet est dans la portée globale, les fonctions offertes par ce plugin ne sont pas disponibles jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log ("console.log fonctionne bien");} + + +## Installation + + cordova plugin add cordova-plugin-console + + +### Quirks Android + +Sur certaines plateformes autres que Android, console.log() va agir sur plusieurs arguments, tels que console.log ("1", "2", "3"). Toutefois, Android doit agir uniquement sur le premier argument. Les arguments suivants à console.log() seront ignorées. Ce plugin n'est pas la cause de cela, il s'agit d'une limitation d'Android lui-même. \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/fr/index.md b/plugins/cordova-plugin-console/doc/fr/index.md new file mode 100644 index 0000000..d0dbba5 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/fr/index.md @@ -0,0 +1,39 @@ + + +# cordova-plugin-console + +Ce plugin est destiné à faire en sorte que console.log() est aussi utile que possible. Il ajoute une fonction supplémentaire pour iOS, Ubuntu, Windows Phone 8 et Windows 8. Si vous êtes satisfait du fonctionnement de console.log() pour vous, alors vous avez probablement pas besoin ce plugin. + +Ce plugin définit un global `console` objet. + +Bien que l'objet est dans la portée globale, les fonctions offertes par ce plugin ne sont pas disponibles jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log ("console.log fonctionne bien");} + + +## Installation + + Cordova plugin ajouter cordova-plugin-console + + +### Quirks Android + +Sur certaines plateformes autres que Android, console.log() va agir sur plusieurs arguments, tels que console.log ("1", "2", "3"). Toutefois, Android doit agir uniquement sur le premier argument. Les arguments suivants à console.log() seront ignorées. Ce plugin n'est pas la cause de cela, il s'agit d'une limitation d'Android lui-même. diff --git a/plugins/cordova-plugin-console/doc/it/README.md b/plugins/cordova-plugin-console/doc/it/README.md new file mode 100644 index 0000000..5394d55 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/it/README.md @@ -0,0 +1,43 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +Questo plugin è intesa a garantire che console.log() è tanto utile quanto può essere. Aggiunge una funzione aggiuntiva per iOS, Ubuntu, Windows Phone 8 e Windows. Se sei soddisfatto di come console.log() funziona per voi, quindi probabilmente non è necessario questo plugin. + +Questo plugin definisce un oggetto globale `console`. + +Sebbene l'oggetto sia in ambito globale, funzionalità fornite da questo plugin non sono disponibili fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## Installazione + + cordova plugin add cordova-plugin-console + + +### Stranezze Android + +Su alcune piattaforme diverse da Android, console.log() agirà su più argomenti, come ad esempio console ("1", "2", "3"). Tuttavia, Android agirà solo sul primo argomento. Argomenti successivi a console.log() verranno ignorati. Questo plugin non è la causa di ciò, è una limitazione di Android stesso. \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/it/index.md b/plugins/cordova-plugin-console/doc/it/index.md new file mode 100644 index 0000000..f0625b3 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/it/index.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +Questo plugin è intesa a garantire che console.log() è tanto utile quanto può essere. Aggiunge una funzione aggiuntiva per iOS, Ubuntu, Windows 8 e Windows Phone 8. Se sei soddisfatto di come console.log() funziona per voi, quindi probabilmente non è necessario questo plugin. + +Questo plugin definisce un oggetto globale `console`. + +Sebbene l'oggetto sia in ambito globale, funzionalità fornite da questo plugin non sono disponibili fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## Installazione + + cordova plugin add cordova-plugin-console + + +### Stranezze Android + +Su alcune piattaforme diverse da Android, console.log() agirà su più argomenti, come ad esempio console ("1", "2", "3"). Tuttavia, Android agirà solo sul primo argomento. Argomenti successivi a console.log() verranno ignorati. Questo plugin non è la causa di ciò, è una limitazione di Android stesso. diff --git a/plugins/cordova-plugin-console/doc/ja/README.md b/plugins/cordova-plugin-console/doc/ja/README.md new file mode 100644 index 0000000..059c373 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/ja/README.md @@ -0,0 +1,43 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +ã“ã®ãƒ—ラグインã¯ã€ãã® console.log() ãŒã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ä¾¿åˆ©ãªã“ã¨ã‚’確èªã™ã‚‹ã‚‚ã®ã§ã™ã€‚ ãれ㯠iOSã€Ubuntuã€Windows Phone 8 ã¯ã€Windows ã«è¿½åŠ ã®é–¢æ•°ã‚’追加ã—ã¾ã™ã€‚ å ´åˆã¯ã‚ãªãŸã®ãŸã‚ã® console.log() ã®ä½œå“ã«æº€è¶³ã—ã¦ã„ã‚‹ã—ã€ãŠãらãå¿…è¦ã¯ã‚りã¾ã›ã‚“ã“ã®ãƒ—ラグイン。 + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« ・ `console` オブジェクトを定義ã—ã¾ã™ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープã§ã§ã™ãŒã€ã“ã®ãƒ—ラグインã«ã‚ˆã£ã¦æä¾›ã•れる機能ã¯ã€`deviceready` イベントã®å¾Œã¾ã§ä½¿ç”¨ã§ãã¾ã›ã‚“。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## インストール + + cordova plugin add cordova-plugin-console + + +### Android ã®ç™– + +アンドロイド以外ã®ã„ãã¤ã‹ã®ãƒ—ラットフォーム㧠console.log() 㯠console.log (「1ã€ã€ã€Œ2ã€ã€ã€Œ3ã€) ãªã©ã€è¤‡æ•°ã®å¼•æ•°ã«å‹•作ã—ã¾ã™ã€‚ ã—ã‹ã—ã€ã‚¢ãƒ³ãƒ‰ãƒ­ã‚¤ãƒ‰ã¯ã€æœ€åˆã®å¼•æ•°ã§ã®ã¿å‹•作ã—ã¾ã™ã€‚ console.log() ã«å¾Œç¶šã®å¼•æ•°ã¯ç„¡è¦–ã•れã¾ã™ã€‚ ã“ã®ãƒ—ラグインãŒåŽŸå› ã§ã¯ãªã„ã€ãれ㯠Android ã®è‡ªä½“ã®åˆ¶é™ã§ã™ã€‚ \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/ja/index.md b/plugins/cordova-plugin-console/doc/ja/index.md new file mode 100644 index 0000000..413593c --- /dev/null +++ b/plugins/cordova-plugin-console/doc/ja/index.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +ã“ã®ãƒ—ラグインã¯ã€ãã® console.log() ãŒã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ä¾¿åˆ©ãªã“ã¨ã‚’確èªã™ã‚‹ã‚‚ã®ã§ã™ã€‚ ãれã¯ã€iOSã€Ubuntuã€Windows Phone 8 ãŠã‚ˆã³ Windows 8 ã®è¿½åŠ é–¢æ•°ã‚’è¿½åŠ ã—ã¾ã™ã€‚ å ´åˆã¯ã‚ãªãŸã®ãŸã‚ã® console.log() ã®ä½œå“ã«æº€è¶³ã—ã¦ã„ã‚‹ã—ã€ãŠãらãå¿…è¦ã¯ã‚りã¾ã›ã‚“ã“ã®ãƒ—ラグイン。 + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« ・ `console` オブジェクトを定義ã—ã¾ã™ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープã§ã§ã™ãŒã€ã“ã®ãƒ—ラグインã«ã‚ˆã£ã¦æä¾›ã•れる機能ã¯ã€`deviceready` イベントã®å¾Œã¾ã§ä½¿ç”¨ã§ãã¾ã›ã‚“。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## インストール + + cordova plugin add cordova-plugin-console + + +### Android ã®ç™– + +アンドロイド以外ã®ã„ãã¤ã‹ã®ãƒ—ラットフォーム㧠console.log() 㯠console.log (「1ã€ã€ã€Œ2ã€ã€ã€Œ3ã€) ãªã©ã€è¤‡æ•°ã®å¼•æ•°ã«å‹•作ã—ã¾ã™ã€‚ ã—ã‹ã—ã€ã‚¢ãƒ³ãƒ‰ãƒ­ã‚¤ãƒ‰ã¯ã€æœ€åˆã®å¼•æ•°ã§ã®ã¿å‹•作ã—ã¾ã™ã€‚ console.log() ã«å¾Œç¶šã®å¼•æ•°ã¯ç„¡è¦–ã•れã¾ã™ã€‚ ã“ã®ãƒ—ラグインãŒåŽŸå› ã§ã¯ãªã„ã€ãれ㯠Android ã®è‡ªä½“ã®åˆ¶é™ã§ã™ã€‚ diff --git a/plugins/cordova-plugin-console/doc/ko/README.md b/plugins/cordova-plugin-console/doc/ko/README.md new file mode 100644 index 0000000..d03ee5a --- /dev/null +++ b/plugins/cordova-plugin-console/doc/ko/README.md @@ -0,0 +1,43 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +ì´ í”ŒëŸ¬ê·¸ì¸ì„ console.log()로 수 유용 ë˜ë„ë¡ ì˜ë¯¸ìž…니다. ê·¸ê²ƒì€ iOS, 우분투, Windows Phone 8, ë° ì°½ì— ëŒ€ 한 추가 ê¸°ëŠ¥ì„ ì¶”ê°€í•©ë‹ˆë‹¤. Console.log() ë‹¹ì‹ ì„ ìœ„í•´ ìž‘ë™ í•˜ëŠ” 어떻게 행복 한 경우ì—, 그때 ë‹¹ì‹ ì€ ì•„ë§ˆ í•„ìš” 하지 ì•ŠìŠµë‹ˆë‹¤ì´ í”ŒëŸ¬ê·¸ì¸. + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `console` 개체를 ì •ì˜í•©ë‹ˆë‹¤. + +개체가 ì „ì—­ ë²”ìœ„ì— ìžˆì§€ë§Œ,ì´ í”ŒëŸ¬ê·¸ì¸ì— ì˜í•´ 제공 ë˜ëŠ” 기능 하지 사용할 수 있습니다까지 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## 설치 + + cordova plugin add cordova-plugin-console + + +### 안 드 로ì´ë“œ ë‹¨ì  + +안 드 로ì´ë“œ ì´ì™¸ì˜ ì¼ë¶€ 플랫í¼ì—서 console.log() console.log ("1", "2", "3")와 ê°™ì´ ì—¬ëŸ¬ ì¸ìˆ˜ì— ìž‘ë™í•  것 ì´ë‹¤. 그러나, 안 드 로ì´ë“œëŠ” 첫 번째 ì¸ìˆ˜ì—ë§Œ ìž‘ë™í•  것 ì´ë‹¤. Console.log() í›„ì† ì¸ìˆ˜ëŠ” 무시 ë©ë‹ˆë‹¤. ì´ í”ŒëŸ¬ê·¸ì¸ì˜ ì›ì¸ì´ ë˜ì§€ 않습니다, 그리고 ê·¸ê²ƒì€ ì•ˆ 드 로ì´ë“œ ìžì²´ì˜ 한계입니다. \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/ko/index.md b/plugins/cordova-plugin-console/doc/ko/index.md new file mode 100644 index 0000000..ca631e4 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/ko/index.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +ì´ í”ŒëŸ¬ê·¸ì¸ì„ console.log()로 수 유용 ë˜ë„ë¡ ì˜ë¯¸ìž…니다. IOS, 우분투, Windows Phone 8 ë° ìœˆë„ìš° 8ì— ëŒ€ 한 추가 ê¸°ëŠ¥ì„ ì¶”ê°€ 하 ê³  합니다. Console.log() ë‹¹ì‹ ì„ ìœ„í•´ ìž‘ë™ í•˜ëŠ” 어떻게 행복 한 경우ì—, 그때 ë‹¹ì‹ ì€ ì•„ë§ˆ í•„ìš” 하지 ì•ŠìŠµë‹ˆë‹¤ì´ í”ŒëŸ¬ê·¸ì¸. + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `console` 개체를 ì •ì˜í•©ë‹ˆë‹¤. + +개체가 ì „ì—­ ë²”ìœ„ì— ìžˆì§€ë§Œ,ì´ í”ŒëŸ¬ê·¸ì¸ì— ì˜í•´ 제공 ë˜ëŠ” 기능 하지 사용할 수 있습니다까지 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## 설치 + + cordova plugin add cordova-plugin-console + + +### 안 드 로ì´ë“œ ë‹¨ì  + +안 드 로ì´ë“œ ì´ì™¸ì˜ ì¼ë¶€ 플랫í¼ì—서 console.log() console.log ("1", "2", "3")와 ê°™ì´ ì—¬ëŸ¬ ì¸ìˆ˜ì— ìž‘ë™í•  것 ì´ë‹¤. 그러나, 안 드 로ì´ë“œëŠ” 첫 번째 ì¸ìˆ˜ì—ë§Œ ìž‘ë™í•  것 ì´ë‹¤. Console.log() í›„ì† ì¸ìˆ˜ëŠ” 무시 ë©ë‹ˆë‹¤. ì´ í”ŒëŸ¬ê·¸ì¸ì˜ ì›ì¸ì´ ë˜ì§€ 않습니다, 그리고 ê·¸ê²ƒì€ ì•ˆ 드 로ì´ë“œ ìžì²´ì˜ 한계입니다. diff --git a/plugins/cordova-plugin-console/doc/pl/README.md b/plugins/cordova-plugin-console/doc/pl/README.md new file mode 100644 index 0000000..78ab9d2 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/pl/README.md @@ -0,0 +1,43 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +Ten plugin jest przeznaczona do zapewnienia, że console.log() jest tak przydatne, jak to może być. To dodaje dodatkowÄ… funkcjÄ™ dla iOS, Ubuntu, Windows Phone 8 i Windows. JeÅ›li jesteÅ› zadowolony z jak console.log() pracuje dla Ciebie, wtedy prawdopodobnie nie potrzebujÄ… tej wtyczki. + +Ten plugin definiuje obiekt globalny `console`. + +Mimo, że obiekt jest w globalnym zasiÄ™gu, funkcji oferowanych przez ten plugin nie sÄ… dostÄ™pne dopiero po turnieju `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## Instalacja + + cordova plugin add cordova-plugin-console + + +### Dziwactwa Androida + +Na niektórych platformach innych niż Android console.log() bÄ™dzie dziaÅ‚ać na wielu argumentów, takich jak console.log ("1", "2", "3"). Jednak Android bÄ™dzie dziaÅ‚ać tylko na pierwszy argument. Kolejne argumenty do console.log() bÄ™dÄ… ignorowane. Ten plugin nie jest przyczynÄ…, że, jest to ograniczenie Androida, sam. \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/pl/index.md b/plugins/cordova-plugin-console/doc/pl/index.md new file mode 100644 index 0000000..922b577 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/pl/index.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +Ten plugin jest przeznaczona do zapewnienia, że console.log() jest tak przydatne, jak to może być. To dodaje dodatkowÄ… funkcjÄ™ dla iOS, Ubuntu, Windows Phone 8 i Windows 8. JeÅ›li jesteÅ› zadowolony z jak console.log() pracuje dla Ciebie, wtedy prawdopodobnie nie potrzebujÄ… tej wtyczki. + +Ten plugin definiuje obiekt globalny `console`. + +Mimo, że obiekt jest w globalnym zasiÄ™gu, funkcji oferowanych przez ten plugin nie sÄ… dostÄ™pne dopiero po turnieju `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## Instalacja + + cordova plugin add cordova-plugin-console + + +### Dziwactwa Androida + +Na niektórych platformach innych niż Android console.log() bÄ™dzie dziaÅ‚ać na wielu argumentów, takich jak console.log ("1", "2", "3"). Jednak Android bÄ™dzie dziaÅ‚ać tylko na pierwszy argument. Kolejne argumenty do console.log() bÄ™dÄ… ignorowane. Ten plugin nie jest przyczynÄ…, że, jest to ograniczenie Androida, sam. diff --git a/plugins/cordova-plugin-console/doc/ru/index.md b/plugins/cordova-plugin-console/doc/ru/index.md new file mode 100644 index 0000000..3cfe15d --- /dev/null +++ b/plugins/cordova-plugin-console/doc/ru/index.md @@ -0,0 +1,31 @@ + + +# cordova-plugin-console + +Этот плагин предназначен Ð´Ð»Ñ Ð¾Ð±ÐµÑÐ¿ÐµÑ‡ÐµÐ½Ð¸Ñ ÐºÐ°Ðº полезным, поÑкольку Ñто может быть что console.log(). Он добавлÑет дополнительные функции Ð´Ð»Ñ iOS, Ubuntu, Windows Phone 8 и Windows 8. ЕÑли вы не довольны как console.log() работает Ð´Ð»Ñ Ð²Ð°Ñ, то вы вероÑтно не нужен Ñтот плагин. + +## УÑтановка + + cordova plugin add cordova-plugin-console + + +### ОÑобенноÑти Android + +Ðа некоторых платформах, отличных от Android console.log() будет дейÑтвовать на неÑкольких аргументов, например console.log («1», «2», «3»). Тем не менее Android будет дейÑтвовать только на первого аргумента. ПоÑледующие аргументы Ð´Ð»Ñ console.log() будет игнорироватьÑÑ. Этот плагин не ÑвлÑетÑÑ Ð¿Ñ€Ð¸Ñ‡Ð¸Ð½Ð¾Ð¹ Ñтого, Ñто ограничение Android Ñам. diff --git a/plugins/cordova-plugin-console/doc/zh/README.md b/plugins/cordova-plugin-console/doc/zh/README.md new file mode 100644 index 0000000..ce27c3e --- /dev/null +++ b/plugins/cordova-plugin-console/doc/zh/README.md @@ -0,0 +1,43 @@ + + +# cordova-plugin-console + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-console.svg)](https://travis-ci.org/apache/cordova-plugin-console) + +é€™å€‹å¤–æŽ›ç¨‹å¼æ˜¯ç‚ºäº†ç¢ºä¿è©² console.log() 是一樣有用,它å¯ä»¥æ˜¯ã€‚ 它將添加附加功能的 iOS,Ubuntu,Windows Phone 8 和視窗。 如果你是快樂與 console.log() 是如何為你工作,那麼å¯èƒ½ä¸éœ€è¦é€™å€‹å¤–掛程å¼ã€‚ + +這個外掛程å¼å®šç¾©äº†ä¸€å€‹å…¨åŸŸ `console` 物件。 + +儘管物件是在全çƒç¯„åœå…§ï¼Œæä¾›é€™å€‹å¤–掛程å¼çš„功能ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-console + + +### Android 的怪癖 + +在一些平臺上除了 Android,console.log() äº¦æœƒæ ¹æ“šå¤šå€‹åƒæ•¸ï¼Œå¦‚ console.log ("1"ã€"2"ã€"3")。 然而,安å“系統åªäº¦æœƒæ ¹æ“šç¬¬ä¸€å€‹åƒæ•¸ã€‚ å° console.log() çš„å¾ŒçºŒåƒæ•¸å°‡è¢«å¿½ç•¥ã€‚ 這個外掛程å¼ä¸æ˜¯çš„原因,它是一個 android 作業系統本身的é™åˆ¶ã€‚ \ No newline at end of file diff --git a/plugins/cordova-plugin-console/doc/zh/index.md b/plugins/cordova-plugin-console/doc/zh/index.md new file mode 100644 index 0000000..e18a141 --- /dev/null +++ b/plugins/cordova-plugin-console/doc/zh/index.md @@ -0,0 +1,41 @@ + + +# cordova-plugin-console + +é€™å€‹å¤–æŽ›ç¨‹å¼æ˜¯ç‚ºäº†ç¢ºä¿è©² console.log() 是一樣有用,它å¯ä»¥æ˜¯ã€‚ 它將添加附加功能的 iOS〠Ubuntu,Windows Phone 8 å’Œ Windows 8。 如果你是快樂與 console.log() 是如何為你工作,那麼å¯èƒ½ä¸éœ€è¦é€™å€‹å¤–掛程å¼ã€‚ + +這個外掛程å¼å®šç¾©äº†ä¸€å€‹å…¨åŸŸ `console` 物件。 + +儘管物件是在全çƒç¯„åœå…§ï¼Œæä¾›é€™å€‹å¤–掛程å¼çš„功能ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log("console.log works well"); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-console + + +### Android 的怪癖 + +在一些平臺上除了 Android,console.log() äº¦æœƒæ ¹æ“šå¤šå€‹åƒæ•¸ï¼Œå¦‚ console.log ("1"ã€"2"ã€"3")。 然而,安å“系統åªäº¦æœƒæ ¹æ“šç¬¬ä¸€å€‹åƒæ•¸ã€‚ å° console.log() çš„å¾ŒçºŒåƒæ•¸å°‡è¢«å¿½ç•¥ã€‚ 這個外掛程å¼ä¸æ˜¯çš„原因,它是一個 android 作業系統本身的é™åˆ¶ã€‚ diff --git a/plugins/cordova-plugin-console/package.json b/plugins/cordova-plugin-console/package.json new file mode 100644 index 0000000..91ccc7c --- /dev/null +++ b/plugins/cordova-plugin-console/package.json @@ -0,0 +1,47 @@ +{ + "name": "cordova-plugin-console", + "version": "1.0.5", + "description": "Cordova Console Plugin", + "cordova": { + "id": "cordova-plugin-console", + "platforms": [ + "ios", + "ubuntu", + "wp7", + "wp8", + "windows8", + "windows" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-console" + }, + "keywords": [ + "cordova", + "console", + "ecosystem:cordova", + "cordova-ios", + "cordova-ubuntu", + "cordova-wp7", + "cordova-wp8", + "cordova-windows8", + "cordova-windows" + ], + "scripts": { + "test": "npm run jshint", + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "engines": { + "cordovaDependencies": { + "2.0.0": { + "cordova": ">100" + } + } + }, + "devDependencies": { + "jshint": "^2.6.0" + } +} diff --git a/plugins/cordova-plugin-console/plugin.xml b/plugins/cordova-plugin-console/plugin.xml new file mode 100644 index 0000000..114c665 --- /dev/null +++ b/plugins/cordova-plugin-console/plugin.xml @@ -0,0 +1,127 @@ + + + + + + Console + Cordova Console Plugin + Apache 2.0 + cordova,console + https://git-wip-us.apache.org/repos/asf/cordova-plugin-console.git + https://issues.apache.org/jira/browse/CB/component/12320644 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-console/src/ios/CDVLogger.h b/plugins/cordova-plugin-console/src/ios/CDVLogger.h new file mode 100644 index 0000000..7cfb306 --- /dev/null +++ b/plugins/cordova-plugin-console/src/ios/CDVLogger.h @@ -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. + */ + +#import + +@interface CDVLogger : CDVPlugin + +- (void)logLevel:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/cordova-plugin-console/src/ios/CDVLogger.m b/plugins/cordova-plugin-console/src/ios/CDVLogger.m new file mode 100644 index 0000000..ccfa3a5 --- /dev/null +++ b/plugins/cordova-plugin-console/src/ios/CDVLogger.m @@ -0,0 +1,38 @@ +/* + 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 "CDVLogger.h" +#import + +@implementation CDVLogger + +/* log a message */ +- (void)logLevel:(CDVInvokedUrlCommand*)command +{ + id level = [command argumentAtIndex:0]; + id message = [command argumentAtIndex:1]; + + if ([level isEqualToString:@"LOG"]) { + NSLog(@"%@", message); + } else { + NSLog(@"%@: %@", level, message); + } +} + +@end diff --git a/plugins/cordova-plugin-console/src/ubuntu/console.cpp b/plugins/cordova-plugin-console/src/ubuntu/console.cpp new file mode 100644 index 0000000..9de09f4 --- /dev/null +++ b/plugins/cordova-plugin-console/src/ubuntu/console.cpp @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#include "console.h" + +#include + +Console::Console(Cordova *cordova) : CPlugin(cordova) { +} + +void Console::logLevel(int scId, int ecId, QString level, QString message) { + Q_UNUSED(scId) + Q_UNUSED(ecId) + + if (level != "LOG") + std::cout << "[" << level.toStdString() << "] "; + std::cout << message.toStdString() << std::endl; +} diff --git a/plugins/cordova-plugin-console/src/ubuntu/console.h b/plugins/cordova-plugin-console/src/ubuntu/console.h new file mode 100644 index 0000000..3f3d163 --- /dev/null +++ b/plugins/cordova-plugin-console/src/ubuntu/console.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef CONSOLE_H_FDSVCXGFRS +#define CONSOLE_H_FDSVCXGFRS + +#include + +#include + +class Console : public CPlugin { + Q_OBJECT +public: + explicit Console(Cordova *cordova); + + virtual const QString fullName() override { + return Console::fullID(); + } + + virtual const QString shortName() override { + return "Console"; + } + + static const QString fullID() { + return "Console"; + } + +public slots: + void logLevel(int scId, int ecId, QString level, QString message); +}; + +#endif diff --git a/plugins/cordova-plugin-console/src/wp/DebugConsole.cs b/plugins/cordova-plugin-console/src/wp/DebugConsole.cs new file mode 100644 index 0000000..9bb5476 --- /dev/null +++ b/plugins/cordova-plugin-console/src/wp/DebugConsole.cs @@ -0,0 +1,47 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Diagnostics; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class DebugConsole : BaseCommand + { + public void logLevel(string options) + { + string[] args = JSON.JsonHelper.Deserialize(options); + string level = args[0]; + string msg = args[1]; + + if (level.Equals("LOG")) + { + Debug.WriteLine(msg); + } + else + { + Debug.WriteLine(level + ": " + msg); + } + } + } +} diff --git a/plugins/cordova-plugin-console/tests/plugin.xml b/plugins/cordova-plugin-console/tests/plugin.xml new file mode 100644 index 0000000..da47af4 --- /dev/null +++ b/plugins/cordova-plugin-console/tests/plugin.xml @@ -0,0 +1,31 @@ + + + + + Cordova Console Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-console/tests/tests.js b/plugins/cordova-plugin-console/tests/tests.js new file mode 100644 index 0000000..74765d9 --- /dev/null +++ b/plugins/cordova-plugin-console/tests/tests.js @@ -0,0 +1,43 @@ +/* + * + * 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. + * +*/ + +/* jshint jasmine: true */ + +exports.defineAutoTests = function () { + describe("Console", function () { + it("console.spec.1 should exist", function() { + expect(window.console).toBeDefined(); + }); + + it("console.spec.2 has required methods log|warn|error", function(){ + expect(window.console.log).toBeDefined(); + expect(typeof window.console.log).toBe('function'); + + expect(window.console.warn).toBeDefined(); + expect(typeof window.console.warn).toBe('function'); + + expect(window.console.error).toBeDefined(); + expect(typeof window.console.error).toBe('function'); + }); + }); +}; + +exports.defineManualTests = function (contentEl, createActionButton) {}; diff --git a/plugins/cordova-plugin-console/www/console-via-logger.js b/plugins/cordova-plugin-console/www/console-via-logger.js new file mode 100644 index 0000000..ffee326 --- /dev/null +++ b/plugins/cordova-plugin-console/www/console-via-logger.js @@ -0,0 +1,186 @@ +/* + * + * 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 logger = require("./logger"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = logger.format.apply(logger.format, [].slice.call(arguments, 1)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrappedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrappedOrigCall(WinConsole[key], console[key]); + } +} diff --git a/plugins/cordova-plugin-console/www/logger.js b/plugins/cordova-plugin-console/www/logger.js new file mode 100644 index 0000000..430d887 --- /dev/null +++ b/plugins/cordova-plugin-console/www/logger.js @@ -0,0 +1,354 @@ +/* + * + * 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. + * +*/ + +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); + +var UseConsole = false; +var UseLogger = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +var originalConsole = console; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i 0){ + formatArgs.unshift(fmtString); // add formatString + } + + var message = logger.format.apply(logger.format, formatArgs); + + if (LevelsMap[level] === null) { + throw new Error("invalid logging level: " + level); + } + + if (LevelsMap[level] > CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // Log using the native logger if that is enabled + if (UseLogger) { + exec(null, null, "Console", "logLevel", [level, message]); + } + + // Log using the console if that is enabled + if (UseConsole) { + // make sure console is not using logger + if (console.useLogger()) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: originalConsole.log(message); break; + case logger.ERROR: originalConsole.log("ERROR: " + message); break; + case logger.WARN: originalConsole.log("WARN: " + message); break; + case logger.INFO: originalConsole.log("INFO: " + message); break; + case logger.DEBUG: originalConsole.log("DEBUG: " + message); break; + } + } +}; + + +/** + * Formats a string and arguments following it ala console.log() + * + * Any remaining arguments will be appended to the formatted string. + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +logger.format = function(formatString, args) { + return __format(arguments[0], [].slice.call(arguments,1)).join(' '); +}; + + +//------------------------------------------------------------------------------ +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * Returns an array containing the formatted string and any remaining + * arguments. + */ +function __format(formatString, args) { + if (formatString === null || formatString === undefined) return [""]; + if (arguments.length == 1) return [formatString.toString()]; + + if (typeof formatString != "string") + formatString = formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var match = pattern.exec(rest); + if (!match) break; + + var arg = args.shift(); + rest = match[3]; + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(__formatted(arg, match[2])); + } + + result.push(rest); + + var remainingArgs = [].slice.call(args); + remainingArgs.unshift(result.join('')); + return remainingArgs; +} + +function __formatted(object, formatChar) { + + try { + switch(formatChar) { + case 'j': + case 'o': return JSON.stringify(object); + case 'c': return ''; + } + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + + if ((object === null) || (object === undefined)) { + return Object.prototype.toString.call(object); + } + + return object.toString(); +} + + +//------------------------------------------------------------------------------ +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i + +# 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/plugins/cordova-plugin-contacts/LICENSE b/plugins/cordova-plugin-contacts/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-contacts/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/plugins/cordova-plugin-contacts/NOTICE b/plugins/cordova-plugin-contacts/NOTICE new file mode 100644 index 0000000..b0398ea --- /dev/null +++ b/plugins/cordova-plugin-contacts/NOTICE @@ -0,0 +1,6 @@ +Apache Cordova +Copyright 2012 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). +Microsoft Open Technologies, Inc. (http://msopentech.com/). \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/README.md b/plugins/cordova-plugin-contacts/README.md new file mode 100644 index 0000000..77e8c88 --- /dev/null +++ b/plugins/cordova-plugin-contacts/README.md @@ -0,0 +1,847 @@ + + +# cordova-plugin-contacts + +This plugin defines a global `navigator.contacts` object, which provides access to the device contacts database. + +Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + +__WARNING__: Collection and use of contact data raises +important privacy issues. Your app's privacy policy should discuss +how the app uses contact data and whether it is shared with any other +parties. Contact information is considered sensitive because it +reveals the people with whom a person communicates. Therefore, in +addition to the app's privacy policy, you should strongly consider +providing a just-in-time notice before the app accesses or uses +contact data, if the device operating system doesn't do so +already. That notice should provide the same information noted above, +as well as obtaining the user's permission (e.g., by presenting +choices for __OK__ and __No Thanks__). Note that some app +marketplaces may require the app to provide a just-in-time notice and +obtain the user's permission before accessing contact data. A +clear and easy-to-understand user experience surrounding the use of +contact data helps avoid user confusion and perceived misuse of +contact data. For more information, please see the Privacy Guide. + +:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Contacts%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + +This requires cordova 5.0+ ( current stable v1.0.0 ) + + cordova plugin add cordova-plugin-contacts +Older versions of cordova can still install via the __deprecated__ id ( stale v0.2.16 ) + + cordova plugin add org.apache.cordova.contacts +It is also possible to install via repo url directly ( unstable ) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS Quirks + +Create __www/manifest.webapp__ as described in +[Manifest Docs](https://developer.mozilla.org/en-US/Apps/Developing/Manifest). +Add relevant permisions. +There is also a need to change the webapp type to "privileged" - [Manifest Docs](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type). +__WARNING__: All privileged apps enforce [Content Security Policy](https://developer.mozilla.org/en-US/Apps/CSP) which forbids inline script. Initialize your application in another way. + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + +### Windows Quirks + +**Prior to Windows 10:** Any contacts returned from `find` and `pickContact` methods are readonly, so your application cannot modify them. +`find` method available only on Windows Phone 8.1 devices. + +**Windows 10 and above:** Contacts may be saved and will be saved to app-local contacts storage. Contacts may also be deleted. + +### Windows 8 Quirks + +Windows 8 Contacts are readonly. Via the Cordova API Contacts are not queryable/searchable, you should inform the user to pick a contact as a call to contacts.pickContact which will open the 'People' app where the user must choose a contact. +Any contacts returned are readonly, so your application cannot modify them. + +## navigator.contacts + +### Methods + +- navigator.contacts.create +- navigator.contacts.find +- navigator.contacts.pickContact + +### Objects + +- Contact +- ContactName +- ContactField +- ContactAddress +- ContactOrganization +- ContactFindOptions +- ContactError +- ContactFieldType + +## navigator.contacts.create + +The `navigator.contacts.create` method is synchronous, and returns a new `Contact` object. + +This method does not retain the Contact object in the device contacts +database, for which you need to invoke the `Contact.save` method. + +### Supported Platforms + +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 + +### Example + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + +## navigator.contacts.find + +The `navigator.contacts.find` method executes asynchronously, querying the +device contacts database and returning an array of `Contact` objects. +The resulting objects are passed to the `contactSuccess` callback +function specified by the __contactSuccess__ parameter. + +The __contactFields__ parameter specifies the fields to be used as a +search qualifier. A zero-length __contactFields__ parameter is invalid and results in +`ContactError.INVALID_ARGUMENT_ERROR`. A __contactFields__ value of +`"*"` searches all contact fields. + +The __contactFindOptions.filter__ string can be used as a search +filter when querying the contacts database. If provided, a +case-insensitive, partial value match is applied to each field +specified in the __contactFields__ parameter. If there's a match for +_any_ of the specified fields, the contact is returned. Use __contactFindOptions.desiredFields__ +parameter to control which contact properties must be returned back. + +Supported values for both __contactFields__ and __contactFindOptions.desiredFields__ parameters are enumerated in [`ContactFieldType`](#contactfieldtype) object. + +### Parameters + +- __contactFields__: Contact fields to use as a search qualifier. _(DOMString[])_ [Required] + +- __contactSuccess__: Success callback function invoked with the array of Contact objects returned from the database. [Required] + +- __contactError__: Error callback function, invoked when an error occurs. [Optional] + +- __contactFindOptions__: Search options to filter navigator.contacts. [Optional] + + Keys include: + + - __filter__: The search string used to find navigator.contacts. _(DOMString)_ (Default: `""`) + + - __multiple__: Determines if the find operation returns multiple navigator.contacts. _(Boolean)_ (Default: `false`) + + - __desiredFields__: Contact fields to be returned back. If specified, the resulting `Contact` object only features values for these fields. _(DOMString[])_ [Optional] + + - __hasPhoneNumber__(Android only): Filters the search to only return contacts with a phone number informed. _(Boolean)_ (Default: `false`) + +### Supported Platforms + +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 +- Windows (Windows Phone 8.1 and Windows 10) + +### Example + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + options.hasPhoneNumber = true; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + +### Windows Quirks + +- `__contactFields__` is not supported and will be ignored. `find` method will always attempt to match the name, email address, or phone number of a contact. + +## navigator.contacts.pickContact + +The `navigator.contacts.pickContact` method launches the Contact Picker to select a single contact. +The resulting object is passed to the `contactSuccess` callback +function specified by the __contactSuccess__ parameter. + +### Parameters + +- __contactSuccess__: Success callback function invoked with the single Contact object. [Required] + +- __contactError__: Error callback function, invoked when an error occurs. [Optional] + +### Supported Platforms + +- Android +- iOS +- Windows Phone 8 +- Windows 8 +- Windows + +### Example + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + +### Android Quirks + +This plugin launches an external Activity for picking contacts. See the +[Android Lifecycle Guide](http://cordova.apache.org/docs/en/dev/guide/platforms/android/lifecycle.html) +for an explanation of how this affects your application. If the plugin returns +its result in the `resume` event, then you must first wrap the returned object +in a `Contact` object before using it. Here is an example: + +```javascript +function onResume(resumeEvent) { + if(resumeEvent.pendingResult) { + if(resumeEvent.pendingResult.pluginStatus === "OK") { + var contact = navigator.contacts.create(resumeEvent.pendingResult.result); + successCallback(contact); + } else { + failCallback(resumeEvent.pendingResult.result); + } + } +} +``` + +## Contact + +The `Contact` object represents a user's contact. Contacts can be +created, stored, or removed from the device contacts database. +Contacts can also be retrieved (individually or in bulk) from the +database by invoking the `navigator.contacts.find` method. + +__NOTE__: Not all of the contact fields listed above are supported on +every device platform. Please check each platform's _Quirks_ section +for details. + + +### Properties + +- __id__: A globally unique identifier. _(DOMString)_ + +- __displayName__: The name of this Contact, suitable for display to end users. _(DOMString)_ + +- __name__: An object containing all components of a persons name. _(ContactName)_ + +- __nickname__: A casual name by which to address the contact. _(DOMString)_ + +- __phoneNumbers__: An array of all the contact's phone numbers. _(ContactField[])_ + +- __emails__: An array of all the contact's email addresses. _(ContactField[])_ + +- __addresses__: An array of all the contact's addresses. _(ContactAddress[])_ + +- __ims__: An array of all the contact's IM addresses. _(ContactField[])_ + +- __organizations__: An array of all the contact's organizations. _(ContactOrganization[])_ + +- __birthday__: The birthday of the contact. _(Date)_ + +- __note__: A note about the contact. _(DOMString)_ + +- __photos__: An array of the contact's photos. _(ContactField[])_ + +- __categories__: An array of all the user-defined categories associated with the contact. _(ContactField[])_ + +- __urls__: An array of web pages associated with the contact. _(ContactField[])_ + +### Methods + +- __clone__: Returns a new `Contact` object that is a deep copy of the calling object, with the `id` property set to `null`. + +- __remove__: Removes the contact from the device contacts database, otherwise executes an error callback with a `ContactError` object. + +- __save__: Saves a new contact to the device contacts database, or updates an existing contact if a contact with the same __id__ already exists. + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 +- Windows 8 +- Windows + +### Save Example + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + +### Clone Example + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + +### Remove Example + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X Quirks + +- __categories__: Not supported on Android 2.X devices, returning `null`. + +### BlackBerry 10 Quirks + +- __id__: Assigned by the device when saving the contact. + +### FirefoxOS Quirks + +- __categories__: Partially supported. Fields __pref__ and __type__ are returning `null` + +- __ims__: Not supported + +- __photos__: Not supported + + +### iOS Quirks + +- __displayName__: Not supported on iOS, returning `null` unless there is no `ContactName` specified, in which case it returns the composite name, __nickname__ or `""`, respectively. + +- __birthday__: Must be input as a JavaScript `Date` object, the same way it is returned. + +- __photos__: Returns a File URL to the image, which is stored in the application's temporary directory. Contents of the temporary directory are removed when the application exits. + +- __categories__: This property is currently not supported, returning `null`. + +### Windows Phone 8 Quirks + +- __displayName__: When creating a contact, the value provided for the display name parameter differs from the display name retrieved when finding the contact. + +- __urls__: When creating a contact, users can input and save more than one web address, but only one is available when searching the contact. + +- __phoneNumbers__: The _pref_ option is not supported. The _type_ is not supported in a _find_ operation. Only one `phoneNumber` is allowed for each _type_. + +- __emails__: The _pref_ option is not supported. Home and personal references same email entry. Only one entry is allowed for each _type_. + +- __addresses__: Supports only work, and home/personal _type_. The home and personal _type_ reference the same address entry. Only one entry is allowed for each _type_. + +- __organizations__: Only one is allowed, and does not support the _pref_, _type_, and _department_ attributes. + +- __note__: Not supported, returning `null`. + +- __ims__: Not supported, returning `null`. + +- __birthdays__: Not supported, returning `null`. + +- __categories__: Not supported, returning `null`. + +- __remove__: Method is not supported + +### Windows Quirks + +- __photos__: Returns a File URL to the image, which is stored in the application's temporary directory. + +- __birthdays__: Not supported, returning `null`. + +- __categories__: Not supported, returning `null`. + +- __remove__: Method is only supported in Windows 10 or above. + +## ContactAddress + +The `ContactAddress` object stores the properties of a single address +of a contact. A `Contact` object may include more than one address in +a `ContactAddress[]` array. + + +### Properties + +- __pref__: Set to `true` if this `ContactAddress` contains the user's preferred value. _(boolean)_ + +- __type__: A string indicating what type of field this is, _home_ for example. _(DOMString)_ + +- __formatted__: The full address formatted for display. _(DOMString)_ + +- __streetAddress__: The full street address. _(DOMString)_ + +- __locality__: The city or locality. _(DOMString)_ + +- __region__: The state or region. _(DOMString)_ + +- __postalCode__: The zip code or postal code. _(DOMString)_ + +- __country__: The country name. _(DOMString)_ + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 +- Windows 8 +- Windows + +### Example + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + options.multiple = true; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + +### Android 2.X Quirks + +- __pref__: Not supported, returning `false` on Android 2.X devices. + +### BlackBerry 10 Quirks + +- __pref__: Not supported on BlackBerry devices, returning `false`. + +- __type__: Partially supported. Only one each of _Work_ and _Home_ type addresses can be stored per contact. + +- __formatted__: Partially supported. Returns a concatenation of all BlackBerry address fields. + +- __streetAddress__: Supported. Returns a concatenation of BlackBerry __address1__ and __address2__ address fields. + +- __locality__: Supported. Stored in BlackBerry __city__ address field. + +- __region__: Supported. Stored in BlackBerry __stateProvince__ address field. + +- __postalCode__: Supported. Stored in BlackBerry __zipPostal__ address field. + +- __country__: Supported. + +### FirefoxOS Quirks + +- __formatted__: Currently not supported + +### iOS Quirks + +- __pref__: Not supported on iOS devices, returning `false`. + +- __formatted__: Currently not supported. + +### Windows 8 Quirks + +- __pref__: Not supported + +### Windows Quirks + +- __pref__: Not supported + + +## ContactError + +The `ContactError` object is returned to the user through the +`contactError` callback function when an error occurs. + +### Properties + +- __code__: One of the predefined error codes listed below. + +### Constants + +- `ContactError.UNKNOWN_ERROR` (code 0) +- `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +- `ContactError.TIMEOUT_ERROR` (code 2) +- `ContactError.PENDING_OPERATION_ERROR` (code 3) +- `ContactError.IO_ERROR` (code 4) +- `ContactError.NOT_SUPPORTED_ERROR` (code 5) +- `ContactError.OPERATION_CANCELLED_ERROR` (code 6) +- `ContactError.PERMISSION_DENIED_ERROR` (code 20) + + +## ContactField + +The `ContactField` object is a reusable component that represents +contact fields generically. Each `ContactField` object contains a +`value`, `type`, and `pref` property. A `Contact` object stores +several properties in `ContactField[]` arrays, such as phone numbers +and email addresses. + +In most instances, there are no pre-determined values for a +`ContactField` object's __type__ attribute. For example, a phone +number can specify __type__ values of _home_, _work_, _mobile_, +_iPhone_, or any other value that is supported by a particular device +platform's contact database. However, for the `Contact` __photos__ +field, the __type__ field indicates the format of the returned image: +__url__ when the __value__ attribute contains a URL to the photo +image, or _base64_ when the __value__ contains a base64-encoded image +string. + +### Properties + +- __type__: A string that indicates what type of field this is, _home_ for example. _(DOMString)_ + +- __value__: The value of the field, such as a phone number or email address. _(DOMString)_ + +- __pref__: Set to `true` if this `ContactField` contains the user's preferred value. _(boolean)_ + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 +- Windows 8 +- Windows + +### Example + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + +### Android Quirks + +- __pref__: Not supported, returning `false`. + +### BlackBerry 10 Quirks + +- __type__: Partially supported. Used for phone numbers. + +- __value__: Supported. + +- __pref__: Not supported, returning `false`. + +### iOS Quirks + +- __pref__: Not supported, returning `false`. + +### Windows8 Quirks + +- __pref__: Not supported, returning `false`. + +### Windows Quirks + +- __pref__: Not supported, returning `false`. + + +## ContactName + +Contains different kinds of information about a `Contact` object's name. + +### Properties + +- __formatted__: The complete name of the contact. _(DOMString)_ + +- __familyName__: The contact's family name. _(DOMString)_ + +- __givenName__: The contact's given name. _(DOMString)_ + +- __middleName__: The contact's middle name. _(DOMString)_ + +- __honorificPrefix__: The contact's prefix (example _Mr._ or _Dr._) _(DOMString)_ + +- __honorificSuffix__: The contact's suffix (example _Esq._). _(DOMString)_ + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 +- Windows 8 +- Windows + +### Example + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + options.multiple = true; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + +### Android Quirks + +- __formatted__: Partially supported, and read-only. Returns a concatenation of `honorificPrefix`, `givenName`, `middleName`, `familyName`, and `honorificSuffix`. + +### BlackBerry 10 Quirks + +- __formatted__: Partially supported. Returns a concatenation of BlackBerry __firstName__ and __lastName__ fields. + +- __familyName__: Supported. Stored in BlackBerry __lastName__ field. + +- __givenName__: Supported. Stored in BlackBerry __firstName__ field. + +- __middleName__: Not supported, returning `null`. + +- __honorificPrefix__: Not supported, returning `null`. + +- __honorificSuffix__: Not supported, returning `null`. + +### FirefoxOS Quirks + +- __formatted__: Partially supported, and read-only. Returns a concatenation of `honorificPrefix`, `givenName`, `middleName`, `familyName`, and `honorificSuffix`. + + +### iOS Quirks + +- __formatted__: Partially supported. Returns iOS Composite Name, but is read-only. + +### Windows 8 Quirks + +- __formatted__: This is the only name property, and is identical to `displayName`, and `nickname` + +- __familyName__: not supported + +- __givenName__: not supported + +- __middleName__: not supported + +- __honorificPrefix__: not supported + +- __honorificSuffix__: not supported + +### Windows Quirks + +- __formatted__: It is identical to `displayName` + + +## ContactOrganization + +The `ContactOrganization` object stores a contact's organization +properties. A `Contact` object stores one or more +`ContactOrganization` objects in an array. + +### Properties + +- __pref__: Set to `true` if this `ContactOrganization` contains the user's preferred value. _(boolean)_ + +- __type__: A string that indicates what type of field this is, _home_ for example. _(DOMString) + +- __name__: The name of the organization. _(DOMString)_ + +- __department__: The department the contract works for. _(DOMString)_ + +- __title__: The contact's title at the organization. _(DOMString)_ + + +### Supported Platforms + +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Windows Phone 8 +- Windows (Windows 8.1 and Windows Phone 8.1 devices only) + +### Example + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + options.multiple = true; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + +### Android 2.X Quirks + +- __pref__: Not supported by Android 2.X devices, returning `false`. + +### BlackBerry 10 Quirks + +- __pref__: Not supported by BlackBerry devices, returning `false`. + +- __type__: Not supported by BlackBerry devices, returning `null`. + +- __name__: Partially supported. The first organization name is stored in the BlackBerry __company__ field. + +- __department__: Not supported, returning `null`. + +- __title__: Partially supported. The first organization title is stored in the BlackBerry __jobTitle__ field. + +### Firefox OS Quirks + +- __pref__: Not supported + +- __type__: Not supported + +- __department__: Not supported + +- Fields __name__ and __title__ stored in __org__ and __jobTitle__. + +### iOS Quirks + +- __pref__: Not supported on iOS devices, returning `false`. + +- __type__: Not supported on iOS devices, returning `null`. + +- __name__: Partially supported. The first organization name is stored in the iOS __kABPersonOrganizationProperty__ field. + +- __department__: Partially supported. The first department name is stored in the iOS __kABPersonDepartmentProperty__ field. + +- __title__: Partially supported. The first title is stored in the iOS __kABPersonJobTitleProperty__ field. + +### Windows Quirks + +- __pref__: Not supported, returning `false`. + +- __type__: Not supported, returning `null`. + +## ContactFieldType +The `ContactFieldType` object is an enumeration of possible field types, such as `'phoneNumbers'` or `'emails'`, that could be used to control which contact properties must be returned back from `contacts.find()` method (see `contactFindOptions.desiredFields`), or to specify fields to search in (through `contactFields` parameter). Possible values are: + +- `navigator.contacts.fieldType.addresses` +- `navigator.contacts.fieldType.birthday` +- `navigator.contacts.fieldType.categories` +- `navigator.contacts.fieldType.country` +- `navigator.contacts.fieldType.department` +- `navigator.contacts.fieldType.displayName` +- `navigator.contacts.fieldType.emails` +- `navigator.contacts.fieldType.familyName` +- `navigator.contacts.fieldType.formatted` +- `navigator.contacts.fieldType.givenName` +- `navigator.contacts.fieldType.honorificPrefix` +- `navigator.contacts.fieldType.honorificSuffix` +- `navigator.contacts.fieldType.id` +- `navigator.contacts.fieldType.ims` +- `navigator.contacts.fieldType.locality` +- `navigator.contacts.fieldType.middleName` +- `navigator.contacts.fieldType.name` +- `navigator.contacts.fieldType.nickname` +- `navigator.contacts.fieldType.note` +- `navigator.contacts.fieldType.organizations` +- `navigator.contacts.fieldType.phoneNumbers` +- `navigator.contacts.fieldType.photos` +- `navigator.contacts.fieldType.postalCode` +- `navigator.contacts.fieldType.region` +- `navigator.contacts.fieldType.streetAddress` +- `navigator.contacts.fieldType.title` +- `navigator.contacts.fieldType.urls` diff --git a/plugins/cordova-plugin-contacts/RELEASENOTES.md b/plugins/cordova-plugin-contacts/RELEASENOTES.md new file mode 100644 index 0000000..3d7a6c9 --- /dev/null +++ b/plugins/cordova-plugin-contacts/RELEASENOTES.md @@ -0,0 +1,208 @@ + +# Release Notes + +### 2.0.1 (Jan 15, 2016) +* CB-10159 **Android** Adding restore callback to handle Activity destruction +* CB-10319 **Android** Adding reflective helper methods for permission requests +* CB-10117 Added new tests +* CB-10131 Fixed null contact creation. +* CB-10053 Documents `ContactFieldType` enumeration. +* CB-10148 **Android** Added `READ_CONTACTS` permission request when picking a contact +* CB-10053 Accept assets `URIs` for contact photos +* CB-8115 Save contact birthday properly +* CB-6979 Don't create duplicates for extracted contacts photos +* CB-5308 Makes contacts save specs passing +* CB-5308 Return `rawId` instead of id when modifying existing contact +* CB-4921 Corrects examples by adding missing `multiple` option where multiple contacts are expected +* CB-10094 **Android** Fixed empty string comparison +* CB-3950 Adds support for custom labels +* CB-9770 Request user permissions before picking a contact +* CB-8156 Call error callback on `pickContact` cancellation +* CB-7906 Prevent app crash when `desiredFields` option has undefined items +* CB-7021 Adds manual test for `pickContact` + +### 2.0.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* [CB-9728](https://issues.apache.org/jira/browse/CB-9728) Solving memory leak issues due to opened cursor objects +* [CB-9940](https://issues.apache.org/jira/browse/CB-9940) Adding namespace declarations for `m3` and uap to `plugin.xml`. +* [CB-9905](https://issues.apache.org/jira/browse/CB-9905) mark tests as pending if **iOS** permission is blocked. +* Refactored `ContactManager` after feedback +* Commit of Contacts Plugin with new `API` for new **MarshMallow** permissions for **Android 6.0** +* Fixing contribute link. +* [CB-9823](https://issues.apache.org/jira/browse/CB-9823) Making sure the `photoCursor` is always closed. +* Shortened multiple references to use `CommonDataKinds` directly +* removed mulitple calls `toLowerCase(Locale.getDefault())` for the same string, use type Phone `enum` directly. +* [CB-8537](https://issues.apache.org/jira/browse/CB-8537) Updated source to pass `Fortify` scan. +* Update `ContactProxy.js` +* Do not return absolute path for contact images. +* [CB-9579](https://issues.apache.org/jira/browse/CB-9579) Fixed failed tests when `DeleteMe` contact already exists +* [CB-9054](https://issues.apache.org/jira/browse/CB-9054): Can't fully reproduce, but we should probably wrap this in an exception anyway. + +### 1.1.0 (Jun 17, 2015) +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-contacts documentation translation: cordova-plugin-contacts +* fix npm md issue +* Add more install text for legacy versions of cordova tools. This closes #60 +* [CB-9056](https://issues.apache.org/jira/browse/CB-9056) Increased timeout of failing tests +* [CB-8987](https://issues.apache.org/jira/browse/CB-8987): Support for save and remove for Windows 10 +* [CB-5278](https://issues.apache.org/jira/browse/CB-5278): We must close the cursor or we take down the whole app, and the debugger doesn't catch it. + +### 1.0.0 (Apr 15, 2015) +* [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) updated wp specific references of old id to new id +* [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 +* [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 +* [CB-8659](https://issues.apache.org/jira/browse/CB-8659): ios: 4.0.x Compatibility: Remove use of deprecated headers +* [CB-8604](https://issues.apache.org/jira/browse/CB-8604) Pended unsupported test for wp8, updated documentation +* [CB-8561](https://issues.apache.org/jira/browse/CB-8561) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-contacts documentation translation: cordova-plugin-contacts +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* windows: pended .remove test as it is not supported on windows +* [CB-8395](https://issues.apache.org/jira/browse/CB-8395) marked unsupported tests pending on wp8 + +### 0.2.16 (Feb 04, 2015) +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Stop using (newly) deprecated CordovaLib functions +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension +* android: Update ContactName support +* Updated the comments for ContactOrganization constructor. + +### 0.2.15 (Dec 02, 2014) +* [CB-7131](https://issues.apache.org/jira/browse/CB-7131) Check for profile photo existance +* [CB-7896](https://issues.apache.org/jira/browse/CB-7896) Better way to detect **Windows** and **WindowsPhone8.1** +* [CB-7896](https://issues.apache.org/jira/browse/CB-7896) Pending tests for `Save` and `Find` methods for **Windows** cause they are not supported yet +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* [CB-7772](https://issues.apache.org/jira/browse/CB-7772) - [Contacts] Cancelling `pickContact` should call the error callback, not the success callback +* [CB-7761](https://issues.apache.org/jira/browse/CB-7761) - Misleading text in documentation +* [CB-7762](https://issues.apache.org/jira/browse/CB-7762) - Parameter list is incorrect for `contacts.find` +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-contacts documentation translation: cordova-plugin-contacts + +### 0.2.14 (Oct 03, 2014) +* [CB-7373](https://issues.apache.org/jira/browse/CB-7373) Removes unnecessary Error object creation +* [CB-7373](https://issues.apache.org/jira/browse/CB-7373) Adds additional output if method is not supported. +* [CB-7357](https://issues.apache.org/jira/browse/CB-7357) Adds missing 'capability' element to phone's appxmanifest. + +### 0.2.13 (Sep 17, 2014) +* [CB-7546](https://issues.apache.org/jira/browse/CB-7546) [Contacts][iOS] pickContact shows exception in the console log +* [CB-6374](https://issues.apache.org/jira/browse/CB-6374) Fix iOS 6 deprecation warnings in Contacts +* [CB-7544](https://issues.apache.org/jira/browse/CB-7544) [Contacts][iOS 8] Contact picker is read-only in iOS 8 +* [CB-7523](https://issues.apache.org/jira/browse/CB-7523) Fixing "ContactFieldType" error in the config.xml +* [CB-6724](https://issues.apache.org/jira/browse/CB-6724) Empty may be expected. +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-contacts documentation translation +* Add missing test, skip some specs on wp +* rm old test folder and merged with renamed tests folder +* [CB-7290](https://issues.apache.org/jira/browse/CB-7290) Adds support for universal Windows platform. +* Renamed test dir, added nested plugin.xml +* [CB-7148](https://issues.apache.org/jira/browse/CB-7148) Added manual tests +* Removed js-module for tests from plugin.xml +* Changing cdvtest format to use module exports +* register tests using new style +* convert test to new style +* added documentation for manual tests +* merged changes for test framework plugin + +### 0.2.12 (Aug 06, 2014) +* fixes .find method when 'options' param is not passed. Will return all contacts on missing 'options' param +* [FFOS] update ContactsProxy.js +* Removing a stray unicode character +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* [CB-5698](https://issues.apache.org/jira/browse/CB-5698) ios: Check to see if photoData exists before using + +### 0.2.11 (Jul 2, 2014) +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #25 +* Remove deprecated symbols for iOS < 6 +* [CB-6797](https://issues.apache.org/jira/browse/CB-6797) Add license +* [wp8] now pupulates contact photos +* Update license headers format +* Add pickContact functionality to cordova contacts plugin +* [CB-5416](https://issues.apache.org/jira/browse/CB-5416) - Adding support for auto-managing permissions +* [CB-6682](https://issues.apache.org/jira/browse/CB-6682) move windows8 command proxy into it's missing platform tag. This closes #30 +* Add ContactError codes to index.md doc (closes #28) +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* Docs typo: navigator.contacts.length -> contacts.length +* [CB-5698](https://issues.apache.org/jira/browse/CB-5698) ios: Check to see if photoData exists before using +* [CB-7003](https://issues.apache.org/jira/browse/CB-7003) android: Make pickContact pick correct contact on Android 4.3 and 4.4.3 + +### 0.2.10 (Apr 17, 2014) +* [CB-6126](https://issues.apache.org/jira/browse/CB-6126): [BlackBerry10] Update docs quirks section for fields which are supported +* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* Add NOTICE file + +### 0.2.9 (Feb 26, 2014) +* [CB-6086](https://issues.apache.org/jira/browse/CB-6086) Fix typo in ffos part of plugin.xml: Camera -> Contacts +* [CB-5994](https://issues.apache.org/jira/browse/CB-5994) Switch Contact ID lookup to use Raw contact id. + +### 0.2.8 (Feb 05, 2014) +* [CB-3208](https://issues.apache.org/jira/browse/CB-3208) FFOS docs updated +* [CB-4590](https://issues.apache.org/jira/browse/CB-4590) - chooseContact in CDVContacts crashes app + +### 0.2.7 (Jan 02, 2014) +* B-5658 Add doc/index.md for Contacts plugin + +### 0.2.6 (Dec 4, 2013) +* Fix bad commit/merge +* [CB-3035](https://issues.apache.org/jira/browse/CB-3035) Fix issue with windows new line char \n\r +* wrong example given +* docs added +* FxOS name fields are arrays hackedSearch refactored search based on find commented out +* search hacked via getAll +* search added - no idea if this is working +* createMozillaFromCordova and vice versa are used to translate contact objects from one API to another. +* add/remove working +* save is working +* attempt to save is failing trying to limit the translated contact fields to name and familyName, but still failing +* save is linked with the proxy contact.name doesn't exist www/Contact.js#Contact.prototype.save check on which side is the error +* [CB-5214](https://issues.apache.org/jira/browse/CB-5214) Make mobile spec tests on WP8 to run w/o user interaction + Sync with cordova-mobile-spec +* [CB-5525](https://issues.apache.org/jira/browse/CB-5525) WP8. Contacts Api fails in case of there is special character in contact field +* fixed ubuntu policy error +* [ubuntu] specify policy_group +* add ubuntu platform +* [CB-3035](https://issues.apache.org/jira/browse/CB-3035) Fix issue with windows new line char \n\r +* 1. Added amazon-fireos platform. 2. Change to use amazon-fireos as the platform if user agent string contains 'cordova-amazon-fireos'. +* [CB-5198](https://issues.apache.org/jira/browse/CB-5198) [BlackBerry10] Update dependencies to point to registry +* handle null filter when fields are specified. ( long standing pull-req from @kevfromireland ) + +### 0.2.5 (Oct 28, 2013) +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tags for contacts +* [CB-5010](https://issues.apache.org/jira/browse/CB-5010) Incremented plugin version on dev branch. + +### 0.2.4 (Oct 9, 2013) +* [CB-4950](https://issues.apache.org/jira/browse/CB-4950) Remove the dependence on concrete component android.webkit.WebView. +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.2.3 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [BlackBerry10] removed uneeded permission tags in plugin.xml +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming blackberry10 reference in plugin.xml +* [CB-4888](https://issues.apache.org/jira/browse/CB-4888) renaming org.apache.cordova.core.contacts to org.apache.cordova.contacts +* added contacts api for firefoxos +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4824](https://issues.apache.org/jira/browse/CB-4824) Fix XCode 5 contacts plugin warnings +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. + +### 0.2.1 (Sept 5, 2013) +* [CB-4580](https://issues.apache.org/jira/browse/CB-4580) Fixed up duplicate definitions of module id +* [CB-4432](https://issues.apache.org/jira/browse/CB-4432) Copyright notice change + diff --git a/plugins/cordova-plugin-contacts/doc/de/README.md b/plugins/cordova-plugin-contacts/doc/de/README.md new file mode 100644 index 0000000..676ca2e --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/de/README.md @@ -0,0 +1,735 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +Dieses Plugin definiert eine globale `navigator.contacts`-Objekt bietet Zugriff auf die Geräte-Kontakte-Datenbank. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**Warnung**: Erhebung und Nutzung von Kontaktdaten löst wichtige Datenschutzprobleme. Ihre app-Datenschutzerklärung sollten besprechen, wie die app Kontaktdaten verwendet und ob es mit irgendwelchen anderen Parteien geteilt wird. Kontaktinformationen ist als vertraulich angesehen, weil es die Menschen zeigt, mit denen eine Person kommuniziert. Daher neben der app-Privacy Policy sollten stark Sie Bereitstellung eine just-in-Time-Bekanntmachung, bevor die app zugreift oder Kontaktdaten verwendet, wenn Betriebssystem des Geräts nicht dies bereits tun. Diese Benachrichtigung sollte der gleichen Informationen, die vorstehend, sowie die Zustimmung des Benutzers (z.B. durch Präsentation Entscheidungen für das **OK** und **Nein danke**). Beachten Sie, dass einige app-Marktplätze die app eine Frist eine just-in-Time und erhalten die Erlaubnis des Benutzers vor dem Zugriff auf Kontaktdaten verlangen können. Eine klare und leicht verständliche Benutzererfahrung rund um die Verwendung der Kontakt-Daten Benutzer Verwirrung zu vermeiden können und wahrgenommene Missbrauch der Kontaktdaten. Weitere Informationen finden Sie in der Datenschutz-Guide. + +## Installation + +Dies erfordert Cordova 5.0 + (aktuelle stabile v1) + + cordova plugin add cordova-plugin-contacts + + +Ältere Versionen von Cordova können noch über die Id **veraltet** (veraltet v0.2.16) installieren. + + cordova plugin add org.apache.cordova.contacts + + +Es ist auch möglich, über Repo Url direkt zu installieren (unstable) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS Macken + +Erstellen Sie **www/manifest.webapp**, wie in [Docs Manifest](https://developer.mozilla.org/en-US/Apps/Developing/Manifest) beschrieben. Fügen Sie die entsprechenden Permisions. Es muss auch die Webapp um "privileged" - [Manifest Docs](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type) ändern. **Warnung**: alle privilegierten apps [Content Security Policy](https://developer.mozilla.org/en-US/Apps/CSP), welche Inlineskript verbietet zu erzwingen. Initialisieren Sie die Anwendung auf andere Weise. + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows-Eigenheiten + +**Vor Windows 10:** Alle Kontakte von `finden` und `PickContact` -Methoden zurückgegebenen sind Readonly, so dass sie von die Anwendung nicht geändert werden kann. `find`-Methode nur auf Windows Phone 8.1-Geräten verfügbar. + +**Windows 10 und höher:** Kontakte können gespeichert werden und in den app-lokale Kontakte-Speicher gespeichert werden. Kontakte können auch gelöscht werden. + +### Windows 8 Macken + +Windows 8 Kontakte sind Readonly. Über die Cordova-API-Kontakte nicht abgefragt werden/können durchsucht werden, Sie sollten den Benutzer informieren, wählen Sie einen Kontakt als Aufruf an contacts.pickContact, die 'People'-app öffnet, wo muss der Benutzer einen Kontakt auswählen. Alle zurückgegebenen Kontakte sind Readonly, so dass sie von die Anwendung nicht geändert werden kann. + +## Navigator.Contacts + +### Methoden + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### Objekte + + * Kontakt + * ContactName + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +Die `navigator.contacts.create`-Methode ist synchron und gibt ein neues `Contact` objekt. + +Diese Methode behält nicht das Kontakt-Objekt in der Gerät-Kontakte-Datenbank, für die Sie benötigen, um die `Contact.save`-Methode aufzurufen. + +### Unterstützte Plattformen + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + +### Beispiel + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Die `navigator.contacts.find`-Methode führt asynchron, Abfragen der Gerät-Kontakte-Datenbank und gibt ein Array von `Contact`-Objekte. Die resultierenden Objekte werden an die durch den **contactSuccess**-Parameter angegebenen `contactSuccess`-Callback-Funktion übergeben. + +Der **contactFields**-Parameter gibt die Felder als Qualifizierer Suche verwendet werden. Ein leere **contactFields**-Parameter ist ungültig und führt zu `ContactError.INVALID_ARGUMENT_ERROR`. **contactFields** Wert `"*"` sucht alle Kontaktfelder. + +Die **contactFindOptions.filter**-Zeichenfolge kann als einen Suchfilter verwendet, wenn die Kontaktdatenbank Abfragen. Wenn angeboten, ein groß-und Kleinschreibung, wird jedes Feld in der **contactFields**-Parameter angegebenen Teilwert Übereinstimmung. Wenn eine Übereinstimmung für *alle* angegebenen Felder vorliegt, wird der Kontakt zurückgegeben. Verwendung **contactFindOptions.desiredFields** Parameter steuern, welche Eigenschaften kontaktieren muss wieder zurückgegeben werden. + +### Parameter + + * **contactFields**: Kontaktfelder als Qualifizierer Suche verwenden. *(DOMString[])* [Required] + + * **contactSuccess**: Erfolg-Callback-Funktion aufgerufen, die mit dem Array von Contact-Objekte aus der Datenbank zurückgegeben. [Required] + + * **ContactError**: Fehler-Callback-Funktion wird aufgerufen, wenn ein Fehler auftritt. [Optional] + + * **contactFindOptions**: Optionen zum Filtern von navigator.contacts zu suchen. [Optional] + + Schlüssel enthalten: + + * **filter**: die zu suchende Zeichenfolge verwendet, um navigator.contacts zu finden. *(DOM-String und enthält)* (Standard: `""`) + + * **multiple**: bestimmt, ob der Suchvorgang mehrere navigator.contacts gibt. *(Boolesch)* (Standard: `false`) + + * **desiredFields**: Kontaktfelder wieder zurückgegeben werden. Wenn angegeben, Objekt der daraus resultierenden `Contact` nur Funktionen Werte für diese Felder. *(DOMString[])* [Optional] + +### Unterstützte Plattformen + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows (Windows Phone 8.1 und Windows 10) + +### Beispiel + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows-Eigenheiten + + * `__contactFields__`wird nicht unterstützt und wird ignoriert. `find`Methode wird immer versucht, die Namen, e-Mail-Adresse oder Telefonnummer eines Kontakts übereinstimmen. + +## navigator.contacts.pickContact + +Die `navigator.contacts.pickContact`-Methode startet im Kontakt Farbwähler wählen Sie einen einzigen Ansprechpartner. Das resultierende Objekt wird an die durch den **contactSuccess**-Parameter angegebenen `contactSuccess`-Callback-Funktion übergeben. + +### Parameter + + * **ContactSuccess**: Erfolg-Callback-Funktion, die mit den einzelnen Kontakt-Objekt aufgerufen. [Erforderlich] + + * **ContactError**: Fehler-Callback-Funktion wird aufgerufen, wenn ein Fehler auftritt. [Optional] + +### Unterstützte Plattformen + + * Android + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### Beispiel + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Kontakt + +Das `Contact`-Objekt repräsentiert einen Benutzer Kontakt. Kontakte können erstellt, gespeichert oder aus der Gerät-Kontakte-Datenbank entfernt werden. Kontakte können auch (einzeln oder als Gruppe) aus der Datenbank abgerufen werden durch Aufrufen der `navigator.contacts.find`-Methode. + +**Hinweis**: nicht alle oben aufgeführten Kontaktfelder werden auf jedes Geräteplattform unterstützt. Bitte überprüfen Sie jede Plattform *Quirks* Abschnitt für Details. + +### Eigenschaften + + * **ID**: einen globally unique Identifier. *(DOM-String und enthält)* + + * **DisplayName**: der Name dieses Kontakts, geeignet für die Anzeige für Endbenutzer. *(DOM-String und enthält)* + + * **Name**: ein Objekt, das alle Komponenten eines Personen-Namen enthält. *(Kontaktperson)* + + * **Nickname**: einen lässig ein, um den Kontakt zu adressieren. *(DOM-String und enthält)* + + * **Telefonnummern**: ein Array von der Kontakt-Telefonnummern. *(ContactField[])* + + * **Email**: ein Array von e-Mail-Adressen des Kontakts. *(ContactField[])* + + * **Adressen**: ein Array von allen Kontaktadressen. *(ContactAddress[])* + + * **IMS**: ein Array von IM-Adressen des Kontakts. *(ContactField[])* + + * **Organisationen**: ein Array von Organisationen des Kontakts. *(ContactOrganization[])* + + * **Geburtstag**: der Geburtstag des Kontakts. *(Datum)* + + * **Anmerkung**: eine Anmerkung über den Kontakt. *(DOM-String und enthält)* + + * **Fotos**: ein Array mit den Kontakt-Fotos. *(ContactField[])* + + * **Kategorien**: ein Array mit allen benutzerdefinierten Kategorien zugeordnet den Kontakt. *(ContactField[])* + + * **URLs**: ein Array von Web-Seiten, die den Kontakt zugeordnet. *(ContactField[])* + +### Methoden + + * **clone**: gibt eine neue `Contact` Objekt, das eine tiefe Kopie des aufrufenden Objekts, mit der `id` -Eigenschaft festgelegt`null`. + + * **remove**: entfernt den Kontakt aus der Gerät-Kontakte-Datenbank, ansonsten führt eine Fehler-Callback mit einem `ContactError` Objekt. + + * **save**: speichert einen neuen Kontakt in der Gerätedatenbank Kontakte, oder einen vorhandenen Kontakt aktualisiert, wenn ein Kontakt mit der gleichen **Id** bereits vorhanden ist. + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Speichern Sie Beispiel + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### Clone-Beispiel + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Beispiel zu entfernen + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X Macken + + * **Kategorien**: Android 2.X Geräten, Rückgabe nicht unterstützt`null`. + +### BlackBerry 10 Macken + + * **ID**: vom Gerät zugewiesen werden, wenn den Kontakt zu speichern. + +### FirefoxOS Macken + + * **Kategorien**: teilweise unterstützt. Felder **Pref** und **Typ** kehren zurück`null` + + * **IMS**: nicht unterstützt + + * **Fotos**: nicht unterstützt + +### iOS Macken + + * **DisplayName**: nicht auf iOS, Rückkehr unterstützt `null` es sei kein `ContactName` angegeben, in welchem Fall es gibt den zusammengesetzten Namen, **Spitznamen** oder `""` bzw.. + + * **Geburtstag**: muss eingegeben werden, als JavaScript `Date` Objekt, die gleiche Weise zurückgegeben wird. + + * **Fotos**: gibt einen Datei-URL auf das Bild, das im temporären Verzeichnis der Anwendung gespeichert ist. Inhalt des temporären Verzeichnisses werden entfernt, wenn die Anwendung beendet wird. + + * **Kategorien**: Diese Eigenschaft wird derzeit nicht unterstützt, Rückgabe`null`. + +### Windows Phone 7 und 8 Eigenarten + + * **DisplayName**: Wenn Sie einen Kontakt erstellen, der Nutzen für den Anzeigenamen der Display-Name-Parameter unterscheidet abgerufen, wenn den Kontakt zu finden. + + * **URLs**: Wenn Sie einen Kontakt erstellen, können Benutzer eingegeben und mehrere Web-Adressen zu speichern, aber nur einer ist verfügbar, wenn Sie den Kontakt zu suchen. + + * **Telefonnummern**: die *Pref* -Option wird nicht unterstützt. Der *Typ* wird in eine *find* -Operation nicht unterstützt. Nur ein `phoneNumber` ist erlaubt für jeden *Typ*. + + * **Email**: *Pref* -Option wird nicht unterstützt. Haus und persönliche verweist auf dasselbe e-Mail-Eintrag. Nur ein Eintrag ist für jeden *Typ* zulässig.. + + * **Adressen**: unterstützt nur Arbeit und Home/persönliche *Art*. Den gleichen Adresseintrag auf den privaten und persönlichen *Typ* verweisen. Nur ein Eintrag ist für jeden *Typ* zulässig.. + + * **Organisationen**: nur zulässig ist, und unterstützt nicht die Attribute *Pref*, *Typ*und *Abteilung* . + + * **Hinweis**: nicht unterstützt, Rückgabe`null`. + + * **IMS**: nicht unterstützt, Rückgabe`null`. + + * **Geburtstage**: nicht unterstützt, Rückgabe`null`. + + * **Kategorien**: nicht unterstützt, Rückgabe`null`. + + * **remove**: Methode wird nicht unterstützt + +### Windows-Eigenheiten + + * **Fotos**: gibt einen Datei-URL auf das Bild, das im temporären Verzeichnis der Anwendung gespeichert ist. + + * **Geburtstage**: nicht unterstützt, Rückgabe`null`. + + * **Kategorien**: nicht unterstützt, Rückgabe`null`. + + * **remove**: Methode ist nur in Windows 10 oder höher unterstützt. + +## ContactAddress + +Das `ContactAddress`-Objekt speichert die Eigenschaften einer einzelnen Adresse eines Kontakts. Ein `Contact` objekt kann mehr als eine Adresse in einem `ContactAddress []`-Array enthalten. + +### Eigenschaften + + * **Pref**: Legen Sie auf `true` Wenn dieses `ContactAddress` des Benutzers bevorzugten Wert enthält. *(boolesch)* + + * **Typ**: eine Zeichenfolge, die angibt, welche Art von Feld in diesem *Hause* zum Beispiel. *(DOM-String und enthält)* + + * **formatiert**: die vollständige Adresse, die für die Anzeige formatiert. *(DOM-String und enthält)* + + * **StreetAddress**: die vollständige Postanschrift. *(DOM-String und enthält)* + + * **Ort**: die Stadt oder Gemeinde. *(DOM-String und enthält)* + + * **Region**: dem Staat oder der Region. *(DOM-String und enthält)* + + * **Postleitzahl**: die Postleitzahl oder Postleitzahl. *(DOM-String und enthält)* + + * **Land**: den Ländernamen. *(DOM-String und enthält)* + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Beispiel + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X Macken + + * **Pref**: nicht unterstützt, Rückkehr `false` auf Android 2.X Geräten. + +### BlackBerry 10 Macken + + * **Pref**: BlackBerry-Geräten, Rückgabe nicht unterstützt`false`. + + * **Typ**: teilweise unterstützt. Nur eine *Arbeit* und *Home* Typ Adressen kann pro Kontakt gespeichert werden. + + * **formatiert**: teilweise unterstützt. Gibt eine Verkettung von allen BlackBerry-Adressfelder. + + * **StreetAddress**: unterstützt. Gibt eine Verkettung von BlackBerry **Adresse1** und **Adresse2** Adressfelder. + + * **Ort**: unterstützt. Gespeichert in BlackBerry **Stadt** Adressfeld. + + * **Region**: unterstützt. Gespeichert in BlackBerry **StateProvince** Adressfeld. + + * **Postleitzahl**: unterstützt. Im Feld für die Adresse des BlackBerry- **ZipPostal** gespeichert. + + * **Land**: unterstützt. + +### FirefoxOS Macken + + * **formatiert**: derzeit nicht unterstützt + +### iOS Macken + + * **Pref**: iOS-Geräten, Rückgabe nicht unterstützt`false`. + + * **formatiert**: derzeit nicht unterstützt. + +### Windows 8 Macken + + * **Pref**: nicht unterstützt + +### Windows-Eigenheiten + + * **Pref**: nicht unterstützt + +## ContactError + +Das `ContactError`-Objekt wird dem Benutzer über die `contactError`-Callback-Funktion zurückgegeben, wenn ein Fehler auftritt. + +### Eigenschaften + + * **Code**: einer der vordefinierten Fehlercodes aufgeführt. + +### Konstanten + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Das `ContactField`-Objekt ist eine wieder verwendbare Komponenten stellt Felder generisch kontaktieren. Jedes `ContactField`-Objekt enthält eine Eigenschaft `value`, `type` und `pref`. Ein `Contact`-Objekt speichert mehrere Eigenschaften in `ContactField []`-Arrays, wie Telefonnummern und e-Mail-Adressen. + +In den meisten Fällen gibt es keine vorher festgelegten Werte für ein `ContactField`-Objekt-**Type**-Attribut. Beispielsweise kann eine Telefonnummer angeben **type** werte von *home*, *work*, *mobile*, *iPhone* oder ein beliebiger anderer Wert, der von einem bestimmten Geräteplattform Kontaktdatenbank unterstützt wird. Jedoch für die `Contact`-**photos**-Feld, das **type**-Feld gibt das Format des zurückgegebenen Bild: **url** Wenn das **value**-Attribut eine URL zu dem Foto Bild oder *base64*, enthält Wenn der **Wert** eine base64-codierte Bild-Zeichenfolge enthält. + +### Eigenschaften + + * **type**: eine Zeichenfolge, die angibt, welche Art von Feld in diesem *Hause* zum Beispiel. *(DOM-String und enthält)* + + * **value**: der Wert des Feldes, wie z. B. eine Telefonnummer oder e-Mail-Adresse. *(DOM-String und enthält)* + + * **pref**: Legen Sie auf `true` Wenn dieses `ContactField` des Benutzers bevorzugten Wert enthält. *(boolesch)* + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Beispiel + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Android Eigenarten + + * **Pref**: nicht unterstützt, Rückgabe`false`. + +### BlackBerry 10 Macken + + * **Typ**: teilweise unterstützt. Für Telefonnummern verwendet. + + * **Wert**: unterstützt. + + * **Pref**: nicht unterstützt, Rückgabe`false`. + +### iOS Macken + + * **Pref**: nicht unterstützt, Rückgabe`false`. + +### Windows8 Macken + + * **Pref**: nicht unterstützt, Rückgabe`false`. + +### Windows-Eigenheiten + + * **Pref**: nicht unterstützt, Rückgabe`false`. + +## ContactName + +Enthält verschiedene Arten von Informationen über `ein Kontaktobjekt` Namen. + +### Eigenschaften + + * **formatiert**: den vollständigen Namen des Kontakts. *(DOM-String und enthält)* + + * **Nachname**: Familienname des Kontakts. *(DOM-String und enthält)* + + * **GivenName**: Given Name des Kontaktes. *(DOM-String und enthält)* + + * **MiddleName**: Middle Name des Kontaktes. *(DOM-String und enthält)* + + * **HonorificPrefix**: der Kontakt-Präfix (z.B. *Mr.* oder *Dr.*) *(DOM-String und enthält)* + + * **HonorificSuffix**: der Kontakt-Suffix (Beispiel *Esq.*). *(DOM-String und enthält)* + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Beispiel + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android Eigenarten + + * **formatiert**: teilweise unterstützte "und" Read-only. Gibt eine Verkettung von `honorificPrefix` , `givenName` , `middleName` , `familyName` , und`honorificSuffix`. + +### BlackBerry 10 Macken + + * **formatiert**: teilweise unterstützt. Gibt eine Verkettung von BlackBerry- **FirstName** und **LastName** -Feldern. + + * **Nachname**: unterstützt. Im Feld der BlackBerry- **Nachname** gespeichert. + + * **GivenName**: unterstützt. Im BlackBerry **FirstName** -Feld gespeichert. + + * **MiddleName**: nicht unterstützt, Rückgabe`null`. + + * **HonorificPrefix**: nicht unterstützte, Rückgabe`null`. + + * **HonorificSuffix**: nicht unterstützte, Rückgabe`null`. + +### FirefoxOS Macken + + * **formatiert**: teilweise unterstützte "und" Read-only. Gibt eine Verkettung von `honorificPrefix` , `givenName` , `middleName` , `familyName` , und`honorificSuffix`. + +### iOS Macken + + * **formatiert**: teilweise unterstützt. IOS zusammengesetzten Namen gibt, aber ist schreibgeschützt. + +### Windows 8 Macken + + * **formatiert**: Dies ist die einzige Eigenschaft, und ist identisch mit `displayName` , und`nickname` + + * **Nachname**: nicht unterstützt + + * **GivenName**: nicht unterstützt + + * **MiddleName**: nicht unterstützt + + * **HonorificPrefix**: nicht unterstützt + + * **HonorificSuffix**: nicht unterstützt + +### Windows-Eigenheiten + + * **formatiert**: Er ist identisch mit`displayName` + +## ContactOrganization + +Das `ContactOrganization`-Objekt speichert Organisationseigenschaften eines Kontakts. Ein `Contact` objekt werden ein oder mehrere `ContactOrganization`-Objekte in einem Array gespeichert. + +### Eigenschaften + + * **Pref**: Legen Sie auf `true` Wenn dieses `ContactOrganization` des Benutzers bevorzugten Wert enthält. *(boolesch)* + + * **Typ**: eine Zeichenfolge, die angibt, welche Art von Feld in diesem *Hause* zum Beispiel. _(DOMString) + + * **Name**: der Name der Organisation. *(DOM-String und enthält)* + + * **Abteilung**: die Abteilung, die der Vertrag für arbeitet. *(DOM-String und enthält)* + + * **Titel**: Titel des Kontakts in der Organisation. *(DOM-String und enthält)* + +### Unterstützte Plattformen + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows (nur Windows-8.1 und Windows Phone 8.1-Geräte) + +### Beispiel + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X Macken + + * **Pref**: von Android 2.X-Geräte, Rückgabe nicht unterstützt`false`. + +### BlackBerry 10 Macken + + * **Pref**: von BlackBerry-Geräten zurückgeben nicht unterstützt`false`. + + * **Typ**: von BlackBerry-Geräten zurückgeben nicht unterstützt`null`. + + * **Name**: teilweise unterstützt. Der Name der ersten Organisation wird im Feld **Firma** BlackBerry gespeichert. + + * **Abteilung**: nicht unterstützt, Rückgabe`null`. + + * **Titel**: teilweise unterstützt. Der erste Titel der Organisation wird im Feld **JobTitle** BlackBerry gespeichert. + +### Firefox OS Macken + + * **Pref**: nicht unterstützt + + * **Typ**: nicht unterstützt + + * **Abteilung**: nicht unterstützt + + * Felder **Name** und **Titel** in **Org** und **JobTitle** gespeichert. + +### iOS Macken + + * **Pref**: iOS-Geräten, Rückgabe nicht unterstützt`false`. + + * **Typ**: iOS-Geräten, Rückgabe nicht unterstützt`null`. + + * **Name**: teilweise unterstützt. Der Name der ersten Organisation wird im Feld **kABPersonOrganizationProperty** iOS gespeichert. + + * **Abteilung**: teilweise unterstützt. Die Abteilungsnamen der erste ist im Feld **kABPersonDepartmentProperty** iOS gespeichert. + + * **Titel**: teilweise unterstützt. Der erste Titel wird im Feld **kABPersonJobTitleProperty** iOS gespeichert. + +### Windows-Eigenheiten + + * **Pref**: nicht unterstützt, Rückgabe`false`. + + * **Typ**: nicht unterstützt, Rückgabe`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/de/index.md b/plugins/cordova-plugin-contacts/doc/de/index.md new file mode 100644 index 0000000..4252e8f --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/de/index.md @@ -0,0 +1,719 @@ + + +# cordova-plugin-contacts + +Dieses Plugin definiert eine globale `navigator.contacts`-Objekt bietet Zugriff auf die Geräte-Kontakte-Datenbank. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**Warnung**: Erhebung und Nutzung von Kontaktdaten löst wichtige Datenschutzprobleme. Ihre app-Datenschutzerklärung sollten besprechen, wie die app Kontaktdaten verwendet und ob es mit irgendwelchen anderen Parteien geteilt wird. Kontaktinformationen ist als vertraulich angesehen, weil es die Menschen zeigt, mit denen eine Person kommuniziert. Daher neben der app-Privacy Policy sollten stark Sie Bereitstellung eine just-in-Time-Bekanntmachung, bevor die app zugreift oder Kontaktdaten verwendet, wenn Betriebssystem des Geräts nicht dies bereits tun. Diese Benachrichtigung sollte der gleichen Informationen, die vorstehend, sowie die Zustimmung des Benutzers (z.B. durch Präsentation Entscheidungen für das **OK** und **Nein danke**). Beachten Sie, dass einige app-Marktplätze die app eine Frist eine just-in-Time und erhalten die Erlaubnis des Benutzers vor dem Zugriff auf Kontaktdaten verlangen können. Eine klare und leicht verständliche Benutzererfahrung rund um die Verwendung der Kontakt-Daten Benutzer Verwirrung zu vermeiden können und wahrgenommene Missbrauch der Kontaktdaten. Weitere Informationen finden Sie in der Datenschutz-Guide. + +## Installation + + cordova plugin add cordova-plugin-contacts + + +### Firefox OS Macken + +Erstellen Sie **www/manifest.webapp**, wie in [Docs Manifest][1] beschrieben. Fügen Sie die entsprechenden Permisions. Es muss auch die Webapp um "privileged" - [Manifest Docs][2] ändern. **Warnung**: alle privilegierten apps [Content Security Policy][3], welche Inlineskript verbietet zu erzwingen. Initialisieren Sie die Anwendung auf andere Weise. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows-Eigenheiten + +Keine Kontakte von `find` und `pickContact`-Methoden zurückgegebenen sind schreibgeschützt, so die Anwendung geändert werden kann. `find`-Methode nur auf Windows Phone 8.1-Geräten verfügbar. + +### Windows 8 Macken + +Windows 8 Kontakte sind Readonly. Über die Cordova-API-Kontakte nicht abgefragt werden/können durchsucht werden, Sie sollten den Benutzer informieren, wählen Sie einen Kontakt als Aufruf an contacts.pickContact, die 'People'-app öffnet, wo muss der Benutzer einen Kontakt auswählen. Alle zurückgegebenen Kontakte sind Readonly, so dass sie von die Anwendung nicht geändert werden kann. + +## Navigator.Contacts + +### Methoden + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### Objekte + +* Kontakt +* ContactName +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## Navigator.Contacts.Create + +Die `navigator.contacts.create`-Methode ist synchron und gibt ein neues `Contact` objekt. + +Diese Methode behält nicht das Kontakt-Objekt in der Gerät-Kontakte-Datenbank, für die Sie benötigen, um die `Contact.save`-Methode aufzurufen. + +### Unterstützte Plattformen + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 + +### Beispiel + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Die `navigator.contacts.find`-Methode führt asynchron, Abfragen der Gerät-Kontakte-Datenbank und gibt ein Array von `Contact`-Objekte. Die resultierenden Objekte werden an die durch den **contactSuccess**-Parameter angegebenen `contactSuccess`-Callback-Funktion übergeben. + +Der **contactFields**-Parameter gibt die Felder als Qualifizierer Suche verwendet werden. Ein leere **contactFields**-Parameter ist ungültig und führt zu `ContactError.INVALID_ARGUMENT_ERROR`. **contactFields** Wert `"*"` sucht alle Kontaktfelder. + +Die **contactFindOptions.filter**-Zeichenfolge kann als einen Suchfilter verwendet, wenn die Kontaktdatenbank Abfragen. Wenn angeboten, ein groß-und Kleinschreibung, wird jedes Feld in der **contactFields**-Parameter angegebenen Teilwert Übereinstimmung. Wenn eine Übereinstimmung für *alle* angegebenen Felder vorliegt, wird der Kontakt zurückgegeben. Verwendung **contactFindOptions.desiredFields** Parameter steuern, welche Eigenschaften kontaktieren muss wieder zurückgegeben werden. + +### Parameter + +* **contactFields**: Kontaktfelder als Qualifizierer Suche verwenden. *(DOMString[])* [Required] + +* **contactSuccess**: Erfolg-Callback-Funktion aufgerufen, die mit dem Array von Contact-Objekte aus der Datenbank zurückgegeben. [Required] + +* **contactError**: Fehler-Callback-Funktion wird aufgerufen, wenn ein Fehler auftritt.[Optional] + +* **contactFindOptions**: Optionen zum Filtern von navigator.contacts zu suchen. [Optional] + + Schlüssel enthalten: + + * **filter**: die zu suchende Zeichenfolge verwendet, um navigator.contacts zu finden. *(DOM-String und enthält)* (Standard: `""`) + + * **multiple**: bestimmt, ob der Suchvorgang mehrere navigator.contacts gibt. *(Boolesch)* (Standard: `false`) + + * **desiredFields**: Kontaktfelder wieder zurückgegeben werden. Wenn angegeben, Objekt der daraus resultierenden `Contact` nur Funktionen Werte für diese Felder. *(DOMString[])* [Optional] + +### Unterstützte Plattformen + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows (nur Windows Phone 8.1-Geräte) + +### Beispiel + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows-Eigenheiten + +* `__contactFields__`wird nicht unterstützt und wird ignoriert. `find`Methode wird immer versucht, die Namen, e-Mail-Adresse oder Telefonnummer eines Kontakts übereinstimmen. + +## navigator.contacts.pickContact + +Die `navigator.contacts.pickContact`-Methode startet im Kontakt Farbwähler wählen Sie einen einzigen Ansprechpartner. Das resultierende Objekt wird an die durch den **contactSuccess**-Parameter angegebenen `contactSuccess`-Callback-Funktion übergeben. + +### Parameter + +* **ContactSuccess**: Erfolg-Callback-Funktion, die mit den einzelnen Kontakt-Objekt aufgerufen. [Erforderlich] + +* **ContactError**: Fehler-Callback-Funktion wird aufgerufen, wenn ein Fehler auftritt. [Optional] + +### Unterstützte Plattformen + +* Android +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### Beispiel + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Kontakt + +Das `Contact`-Objekt repräsentiert einen Benutzer Kontakt. Kontakte können erstellt, gespeichert oder aus der Gerät-Kontakte-Datenbank entfernt werden. Kontakte können auch (einzeln oder als Gruppe) aus der Datenbank abgerufen werden durch Aufrufen der `navigator.contacts.find`-Methode. + +**Hinweis**: nicht alle oben aufgeführten Kontaktfelder werden auf jedes Geräteplattform unterstützt. Bitte überprüfen Sie jede Plattform *Quirks* Abschnitt für Details. + +### Eigenschaften + +* **ID**: einen globally unique Identifier. *(DOM-String und enthält)* + +* **DisplayName**: der Name dieses Kontakts, geeignet für die Anzeige für Endbenutzer. *(DOM-String und enthält)* + +* **Name**: ein Objekt, das alle Komponenten eines Personen-Namen enthält. *(Kontaktperson)* + +* **Nickname**: einen lässig ein, um den Kontakt zu adressieren. *(DOM-String und enthält)* + +* **Telefonnummern**: ein Array von der Kontakt-Telefonnummern. *(ContactField[])* + +* **Email**: ein Array von e-Mail-Adressen des Kontakts. *(ContactField[])* + +* **Adressen**: ein Array von allen Kontaktadressen. *(ContactAddress[])* + +* **IMS**: ein Array von IM-Adressen des Kontakts. *(ContactField[])* + +* **Organisationen**: ein Array von Organisationen des Kontakts. *(ContactOrganization[])* + +* **Geburtstag**: der Geburtstag des Kontakts. *(Datum)* + +* **Anmerkung**: eine Anmerkung über den Kontakt. *(DOM-String und enthält)* + +* **Fotos**: ein Array mit den Kontakt-Fotos. *(ContactField[])* + +* **Kategorien**: ein Array mit allen benutzerdefinierten Kategorien zugeordnet den Kontakt. *(ContactField[])* + +* **URLs**: ein Array von Web-Seiten, die den Kontakt zugeordnet. *(ContactField[])* + +### Methoden + +* **clone**: gibt eine neue `Contact` Objekt, das eine tiefe Kopie des aufrufenden Objekts, mit der `id` -Eigenschaft festgelegt`null`. + +* **remove**: entfernt den Kontakt aus der Gerät-Kontakte-Datenbank, ansonsten führt eine Fehler-Callback mit einem `ContactError` Objekt. + +* **save**: speichert einen neuen Kontakt in der Gerätedatenbank Kontakte, oder einen vorhandenen Kontakt aktualisiert, wenn ein Kontakt mit der gleichen **Id** bereits vorhanden ist. + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Speichern Sie Beispiel + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### Clone-Beispiel + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Beispiel zu entfernen + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X Macken + +* **Kategorien**: Android 2.X Geräten, Rückgabe nicht unterstützt`null`. + +### BlackBerry 10 Macken + +* **ID**: vom Gerät zugewiesen werden, wenn den Kontakt zu speichern. + +### FirefoxOS Macken + +* **Kategorien**: teilweise unterstützt. Felder **Pref** und **Typ** kehren zurück`null` + +* **IMS**: nicht unterstützt + +* **Fotos**: nicht unterstützt + +### iOS Macken + +* **DisplayName**: nicht auf iOS, Rückkehr unterstützt `null` es sei kein `ContactName` angegeben, in welchem Fall es gibt den zusammengesetzten Namen, **Spitznamen** oder `""` bzw.. + +* **Geburtstag**: muss eingegeben werden, als JavaScript `Date` Objekt, die gleiche Weise zurückgegeben wird. + +* **Fotos**: gibt einen Datei-URL auf das Bild, das im temporären Verzeichnis der Anwendung gespeichert ist. Inhalt des temporären Verzeichnisses werden entfernt, wenn die Anwendung beendet wird. + +* **Kategorien**: Diese Eigenschaft wird derzeit nicht unterstützt, Rückgabe`null`. + +### Windows Phone 7 und 8 Eigenarten + +* **DisplayName**: Wenn Sie einen Kontakt erstellen, der Nutzen für den Anzeigenamen der Display-Name-Parameter unterscheidet abgerufen, wenn den Kontakt zu finden. + +* **URLs**: Wenn Sie einen Kontakt erstellen, können Benutzer eingegeben und mehrere Web-Adressen zu speichern, aber nur einer ist verfügbar, wenn Sie den Kontakt zu suchen. + +* **Telefonnummern**: die *Pref* -Option wird nicht unterstützt. Der *Typ* wird in eine *find* -Operation nicht unterstützt. Nur ein `phoneNumber` ist erlaubt für jeden *Typ*. + +* **Email**: *Pref* -Option wird nicht unterstützt. Haus und persönliche verweist auf dasselbe e-Mail-Eintrag. Nur ein Eintrag ist für jeden *Typ* zulässig.. + +* **Adressen**: unterstützt nur Arbeit und Home/persönliche *Art*. Den gleichen Adresseintrag auf den privaten und persönlichen *Typ* verweisen. Nur ein Eintrag ist für jeden *Typ* zulässig.. + +* **Organisationen**: nur zulässig ist, und unterstützt nicht die Attribute *Pref*, *Typ*und *Abteilung* . + +* **Hinweis**: nicht unterstützt, Rückgabe`null`. + +* **IMS**: nicht unterstützt, Rückgabe`null`. + +* **Geburtstage**: nicht unterstützt, Rückgabe`null`. + +* **Kategorien**: nicht unterstützt, Rückgabe`null`. + +### Windows-Eigenheiten + +* **Fotos**: gibt einen Datei-URL auf das Bild, das im temporären Verzeichnis der Anwendung gespeichert ist. + +* **Geburtstage**: nicht unterstützt, Rückgabe`null`. + +* **Kategorien**: nicht unterstützt, Rückgabe`null`. + +## ContactAddress + +Das `ContactAddress`-Objekt speichert die Eigenschaften einer einzelnen Adresse eines Kontakts. Ein `Contact` objekt kann mehr als eine Adresse in einem `ContactAddress []`-Array enthalten. + +### Eigenschaften + +* **Pref**: Legen Sie auf `true` Wenn dieses `ContactAddress` des Benutzers bevorzugten Wert enthält. *(boolesch)* + +* **Typ**: eine Zeichenfolge, die angibt, welche Art von Feld in diesem *Hause* zum Beispiel. *(DOM-String und enthält)* + +* **formatiert**: die vollständige Adresse, die für die Anzeige formatiert. *(DOM-String und enthält)* + +* **StreetAddress**: die vollständige Postanschrift. *(DOM-String und enthält)* + +* **Ort**: die Stadt oder Gemeinde. *(DOM-String und enthält)* + +* **Region**: dem Staat oder der Region. *(DOM-String und enthält)* + +* **Postleitzahl**: die Postleitzahl oder Postleitzahl. *(DOM-String und enthält)* + +* **Land**: den Ländernamen. *(DOM-String und enthält)* + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Beispiel + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X Macken + +* **Pref**: nicht unterstützt, Rückkehr `false` auf Android 2.X Geräten. + +### BlackBerry 10 Macken + +* **Pref**: BlackBerry-Geräten, Rückgabe nicht unterstützt`false`. + +* **Typ**: teilweise unterstützt. Nur eine *Arbeit* und *Home* Typ Adressen kann pro Kontakt gespeichert werden. + +* **formatiert**: teilweise unterstützt. Gibt eine Verkettung von allen BlackBerry-Adressfelder. + +* **StreetAddress**: unterstützt. Gibt eine Verkettung von BlackBerry **Adresse1** und **Adresse2** Adressfelder. + +* **Ort**: unterstützt. Gespeichert in BlackBerry **Stadt** Adressfeld. + +* **Region**: unterstützt. Gespeichert in BlackBerry **StateProvince** Adressfeld. + +* **Postleitzahl**: unterstützt. Im Feld für die Adresse des BlackBerry- **ZipPostal** gespeichert. + +* **Land**: unterstützt. + +### FirefoxOS Macken + +* **formatiert**: derzeit nicht unterstützt + +### iOS Macken + +* **Pref**: iOS-Geräten, Rückgabe nicht unterstützt`false`. + +* **formatiert**: derzeit nicht unterstützt. + +### Windows 8 Macken + +* **Pref**: nicht unterstützt + +### Windows-Eigenheiten + +* **Pref**: nicht unterstützt + +## ContactError + +Das `ContactError`-Objekt wird dem Benutzer über die `contactError`-Callback-Funktion zurückgegeben, wenn ein Fehler auftritt. + +### Eigenschaften + +* **Code**: einer der vordefinierten Fehlercodes aufgeführt. + +### Konstanten + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Das `ContactField`-Objekt ist eine wieder verwendbare Komponenten stellt Felder generisch kontaktieren. Jedes `ContactField`-Objekt enthält eine Eigenschaft `value`, `type` und `pref`. Ein `Contact`-Objekt speichert mehrere Eigenschaften in `ContactField []`-Arrays, wie Telefonnummern und e-Mail-Adressen. + +In den meisten Fällen gibt es keine vorher festgelegten Werte für ein `ContactField`-Objekt-**Type**-Attribut. Beispielsweise kann eine Telefonnummer angeben **type** werte von *home*, *work*, *mobile*, *iPhone* oder ein beliebiger anderer Wert, der von einem bestimmten Geräteplattform Kontaktdatenbank unterstützt wird. Jedoch für die `Contact`-**photos**-Feld, das **type**-Feld gibt das Format des zurückgegebenen Bild: **url** Wenn das **value**-Attribut eine URL zu dem Foto Bild oder *base64*, enthält Wenn der **Wert** eine base64-codierte Bild-Zeichenfolge enthält. + +### Eigenschaften + +* **type**: eine Zeichenfolge, die angibt, welche Art von Feld in diesem *Hause* zum Beispiel. *(DOM-String und enthält)* + +* **value**: der Wert des Feldes, wie z. B. eine Telefonnummer oder e-Mail-Adresse. *(DOM-String und enthält)* + +* **pref**: Legen Sie auf `true` Wenn dieses `ContactField` des Benutzers bevorzugten Wert enthält. *(boolesch)* + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Beispiel + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Android Eigenarten + +* **Pref**: nicht unterstützt, Rückgabe`false`. + +### BlackBerry 10 Macken + +* **Typ**: teilweise unterstützt. Für Telefonnummern verwendet. + +* **Wert**: unterstützt. + +* **Pref**: nicht unterstützt, Rückgabe`false`. + +### iOS Macken + +* **Pref**: nicht unterstützt, Rückgabe`false`. + +### Windows8 Macken + +* **Pref**: nicht unterstützt, Rückgabe`false`. + +### Windows-Eigenheiten + +* **Pref**: nicht unterstützt, Rückgabe`false`. + +## ContactName + +Enthält verschiedene Arten von Informationen über `ein Kontaktobjekt` Namen. + +### Eigenschaften + +* **formatiert**: den vollständigen Namen des Kontakts. *(DOM-String und enthält)* + +* **Nachname**: Familienname des Kontakts. *(DOM-String und enthält)* + +* **GivenName**: Given Name des Kontaktes. *(DOM-String und enthält)* + +* **MiddleName**: Middle Name des Kontaktes. *(DOM-String und enthält)* + +* **HonorificPrefix**: der Kontakt-Präfix (z.B. *Mr.* oder *Dr.*) *(DOM-String und enthält)* + +* **HonorificSuffix**: der Kontakt-Suffix (Beispiel *Esq.*). *(DOM-String und enthält)* + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Beispiel + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android Eigenarten + +* **formatiert**: teilweise unterstützte "und" Read-only. Gibt eine Verkettung von `honorificPrefix` , `givenName` , `middleName` , `familyName` , und`honorificSuffix`. + +### BlackBerry 10 Macken + +* **formatiert**: teilweise unterstützt. Gibt eine Verkettung von BlackBerry- **FirstName** und **LastName** -Feldern. + +* **Nachname**: unterstützt. Im Feld der BlackBerry- **Nachname** gespeichert. + +* **GivenName**: unterstützt. Im BlackBerry **FirstName** -Feld gespeichert. + +* **MiddleName**: nicht unterstützt, Rückgabe`null`. + +* **HonorificPrefix**: nicht unterstützte, Rückgabe`null`. + +* **HonorificSuffix**: nicht unterstützte, Rückgabe`null`. + +### FirefoxOS Macken + +* **formatiert**: teilweise unterstützte "und" Read-only. Gibt eine Verkettung von `honorificPrefix` , `givenName` , `middleName` , `familyName` , und`honorificSuffix`. + +### iOS Macken + +* **formatiert**: teilweise unterstützt. IOS zusammengesetzten Namen gibt, aber ist schreibgeschützt. + +### Windows 8 Macken + +* **formatiert**: Dies ist die einzige Eigenschaft, und ist identisch mit `displayName` , und`nickname` + +* **Nachname**: nicht unterstützt + +* **GivenName**: nicht unterstützt + +* **MiddleName**: nicht unterstützt + +* **HonorificPrefix**: nicht unterstützt + +* **HonorificSuffix**: nicht unterstützt + +### Windows-Eigenheiten + +* **formatiert**: Er ist identisch mit`displayName` + +## ContactOrganization + +Das `ContactOrganization`-Objekt speichert Organisationseigenschaften eines Kontakts. Ein `Contact` objekt werden ein oder mehrere `ContactOrganization`-Objekte in einem Array gespeichert. + +### Eigenschaften + +* **Pref**: Legen Sie auf `true` Wenn dieses `ContactOrganization` des Benutzers bevorzugten Wert enthält. *(boolesch)* + +* **Typ**: eine Zeichenfolge, die angibt, welche Art von Feld in diesem *Hause* zum Beispiel. _(DOMString) + +* **Name**: der Name der Organisation. *(DOM-String und enthält)* + +* **Abteilung**: die Abteilung, die der Vertrag für arbeitet. *(DOM-String und enthält)* + +* **Titel**: Titel des Kontakts in der Organisation. *(DOM-String und enthält)* + +### Unterstützte Plattformen + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows (nur Windows-8.1 und Windows Phone 8.1-Geräte) + +### Beispiel + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X Macken + +* **Pref**: von Android 2.X-Geräte, Rückgabe nicht unterstützt`false`. + +### BlackBerry 10 Macken + +* **Pref**: von BlackBerry-Geräten zurückgeben nicht unterstützt`false`. + +* **Typ**: von BlackBerry-Geräten zurückgeben nicht unterstützt`null`. + +* **Name**: teilweise unterstützt. Der Name der ersten Organisation wird im Feld **Firma** BlackBerry gespeichert. + +* **Abteilung**: nicht unterstützt, Rückgabe`null`. + +* **Titel**: teilweise unterstützt. Der erste Titel der Organisation wird im Feld **JobTitle** BlackBerry gespeichert. + +### Firefox OS Macken + +* **Pref**: nicht unterstützt + +* **Typ**: nicht unterstützt + +* **Abteilung**: nicht unterstützt + +* Felder **Name** und **Titel** in **Org** und **JobTitle** gespeichert. + +### iOS Macken + +* **Pref**: iOS-Geräten, Rückgabe nicht unterstützt`false`. + +* **Typ**: iOS-Geräten, Rückgabe nicht unterstützt`null`. + +* **Name**: teilweise unterstützt. Der Name der ersten Organisation wird im Feld **kABPersonOrganizationProperty** iOS gespeichert. + +* **Abteilung**: teilweise unterstützt. Die Abteilungsnamen der erste ist im Feld **kABPersonDepartmentProperty** iOS gespeichert. + +* **Titel**: teilweise unterstützt. Der erste Titel wird im Feld **kABPersonJobTitleProperty** iOS gespeichert. + +### Windows-Eigenheiten + +* **Pref**: nicht unterstützt, Rückgabe`false`. + +* **Typ**: nicht unterstützt, Rückgabe`null`. diff --git a/plugins/cordova-plugin-contacts/doc/es/README.md b/plugins/cordova-plugin-contacts/doc/es/README.md new file mode 100644 index 0000000..9bfe407 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/es/README.md @@ -0,0 +1,732 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +Este plugin define un global `navigator.contacts` objeto que proporciona acceso a la base de datos de contactos de dispositivo. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**ADVERTENCIA**: recopilación y uso de datos plantea cuestiones de privacidad importante. Política de privacidad de su aplicación debe discutir cómo la aplicación utiliza datos de contacto y si es compartida con terceros. Información de contacto se considera sensible porque revela la gente con quien se comunica una persona. Por lo tanto, además de política de privacidad de la app, fuertemente considere dar un aviso de just-in-time antes de la aplicación accede a ellos o utiliza los datos de contacto, si el sistema operativo del dispositivo no hacerlo ya. Que el aviso debe proporcionar la misma información mencionada, además de obtener un permiso del usuario (por ejemplo, presentando opciones para **Aceptar** y **No gracias**). Tenga en cuenta que algunos mercados de aplicación podrán exigir la aplicación para proporcionar un aviso de just-in-time y obtener el permiso del usuario antes de acceder a datos de contacto. Una experiencia de usuario clara y fácil de entender que rodean el uso de contacto datos ayuda a evitar la confusión del usuario y percibe uso indebido de los datos de contacto. Para obtener más información, por favor consulte a la guía de privacidad. + +## Instalación + +Esto requiere cordova 5.0 + (v1.0.0 estable actual) + + cordova plugin add cordova-plugin-contacts + + +Las versiones más antiguas de Córdoba todavía pueden instalar mediante el id de **obsoleto** (viejo v0.2.16) + + cordova plugin add org.apache.cordova.contacts + + +También es posible instalar directamente vía url repo (inestable) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS rarezas + +Crear **www/manifest.webapp** como se describe en [Manifestar Docs](https://developer.mozilla.org/en-US/Apps/Developing/Manifest). Agregar permisos pertinentes. También hay una necesidad de cambiar el tipo de aplicación a "privilegiados" - [Docs manifiestan](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type). **ADVERTENCIA**: todas las apps privilegiadas aplicar [Política de seguridad de contenidos](https://developer.mozilla.org/en-US/Apps/CSP) que prohíbe en línea de comandos. Inicializar la aplicación de otra manera. + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows rarezas + +**Antes de Windows 10:** Contactos `encontrar` y `pickContact` métodos son sólo lectura, por lo que su aplicación no puede modificarlos. método `Find` disponible sólo en dispositivos Windows Phone 8.1. + +**Windows 10 y anteriores:** Contactos se pueden guardar y se guarda en almacenamiento local de la aplicación contactos. Contactos también pueden ser eliminados. + +### Rarezas de Windows 8 + +Windows 8 contactos son de sólo lectura. Través de los contactos de la API de Córdoba no son consultables/búsqueda, se debe informar al usuario a buscar un contacto como una llamada a contacts.pickContact que se abrirá la aplicación 'Personas' donde el usuario debe elegir un contacto. Cualquier contacto volvió es readonly, su aplicación no puede modificarlos. + +## Navigator.Contacts + +### Métodos + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### Objetos + + * Contact + * ContactName + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +El `navigator.contacts.create` método es sincrónico y devuelve una nueva `Contact` objeto. + +Este método no retiene el objeto de contacto en la base de contactos de dispositivo, para lo cual necesita invocar el `Contact.save` método. + +### Plataformas soportadas + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + +### Ejemplo + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +El `navigator.contacts.find` método se ejecuta asincrónicamente, consultando la base de datos de contactos de dispositivo y devolver una matriz de `Contact` objetos. Los objetos resultantes son pasados a la `contactSuccess` función de devolución de llamada especificada por el parámetro **contactSuccess** . + +El parámetro **contactFields** especifica los campos para ser utilizado como un calificador de búsqueda. Un parámetro de longitud cero **contactFields** no es válido y resultados en `ContactError.INVALID_ARGUMENT_ERROR` . Un valor de **contactFields** de `"*"` busca campos todo contactos. + +La cadena de **contactFindOptions.filter** puede ser usada como un filtro de búsqueda al consultar la base de datos de contactos. Si proporciona, una entre mayúsculas y minúsculas, coincidencia parcial valor se aplica a cada campo especificado en el parámetro **contactFields** . Si hay un partido para *cualquier* de los campos especificados, se devuelve el contacto. Uso **contactFindOptions.desiredFields** parámetro al control que Contacta con propiedades debe devolverse atrás. + +### Parámetros + + * **contactFields**: póngase en contacto con campos para usar como un calificador de búsqueda. *(DOMString[])* [Required] + + * **contactSuccess**: función de callback de éxito se invoca con la matriz de objetos contacto devueltos desde la base de datos. [Required] + + * **contactError**: función de callback de Error, se invoca cuando se produce un error. [Opcional] + + * **contactFindOptions**: buscar opciones para filtrar navigator.contacts. [Optional] + + Claves incluyen: + + * **filtro**: la cadena de búsqueda utilizada para encontrar navigator.contacts. *(DOMString)* (Por defecto:`""`) + + * **múltiples**: determina si la operación de búsqueda devuelve múltiples navigator.contacts. *(Booleano)* (Por defecto:`false`) + + * **desiredFields**: póngase en contacto con campos para volver atrás. Si se especifica, la resultante `Contact` objeto sólo cuenta con los valores de estos campos. *(DOMString[])* [Optional] + +### Plataformas soportadas + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows (Windows Phone 8.1 y Windows 10) + +### Ejemplo + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows rarezas + + * `__contactFields__`No se admite y se ignorará. `find`método siempre tratará de coincidir con el nombre, dirección de correo electrónico o número de teléfono de un contacto. + +## navigator.contacts.pickContact + +El `navigator.contacts.pickContact` método lanza el selector para seleccionar un único contacto contacto. El objeto resultante se pasa a la `contactSuccess` función de devolución de llamada especificada por el parámetro **contactSuccess** . + +### Parámetros + + * **contactSuccess**: función de callback de éxito se invoca con el único objeto de contacto. [Obligatorio] + + * **contactError**: función de callback de Error, se invoca cuando se produce un error. [Opcional] + +### Plataformas soportadas + + * Android + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### Ejemplo + + navigator.contacts.pickContact(function(contact) {console.log (' se ha seleccionado el siguiente contacto: "+ JSON.stringify(contact)); + }, function(err) {console.log ('Error: ' + err); + }); + + +## Contact + +El `Contact` objeto representa el contacto de un usuario. Contactos pueden ser creados, almacenados o eliminados de la base de datos de contactos de dispositivo. Contactos pueden también ser obtenidos (individualmente o a granel) de la base de datos invocando el `navigator.contacts.find` método. + +**Nota**: no todos los campos de contacto mencionados son compatibles con la plataforma de cada dispositivo. Consulte sección *peculiaridades* de cada plataforma para más detalles. + +### Propiedades + + * **ID**: un identificador único global. *(DOMString)* + + * **displayName**: el nombre de este contacto, conveniente para la exhibición a los usuarios finales. *(DOMString)* + + * **nombre**: un objeto que contiene todos los componentes de un nombre de las personas. *(ContactName)* + + * **apodo**: un nombre para abordar el contacto casual. *(DOMString)* + + * **números**: una matriz de números de teléfono de contacto. *(ContactField[])* + + * **correos electrónicos**: un conjunto de direcciones de correo electrónico del contacto. *(ContactField[])* + + * **direcciones**: un conjunto de direcciones de todos los contactos. *(ContactAddress[])* + + * **IMS**: un conjunto de direcciones de todos los contactos IM. *(ContactField[])* + + * **organizaciones**: un conjunto de organizaciones de todos los contactos. *(ContactOrganization[])* + + * **cumpleaños**: el cumpleaños del contacto. *(Fecha)* + + * **Nota**: una nota sobre el contacto. *(DOMString)* + + * **fotos**: una serie de fotos de los contactos. *(ContactField[])* + + * **categorías**: una matriz de todas las categorías definidas por el usuario asociado con el contacto. *(ContactField[])* + + * **URL**: un conjunto de páginas web asociadas con el contacto. *(ContactField[])* + +### Métodos + + * **clon**: devuelve un nuevo `Contact` objeto que es una copia en profundidad del objeto de llamadas con el `id` propiedad establecida en`null`. + + * **eliminar**: elimina el contacto de la base de datos de contactos de dispositivo, si no se ejecuta un callback de error con un `ContactError` objeto. + + * **Guardar**: guarda un nuevo contacto en la base de datos de contactos de dispositivo o actualiza un contacto existente si ya existe un contacto con el mismo **id** . + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Salvar ejemplo + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### Ejemplo de clon + + clon del clon de contacto objeto var = contact.clone(); + clone.name.givenName = "John"; + Console.log ("contacto Original nombre =" + contact.name.givenName); + Console.log ("Cloned contacto nombre =" + clone.name.givenName); + + +### Quitar ejemplo + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Rarezas Android 2.X + + * **categories**: no compatible con dispositivos Android 2.X, devolver `null`. + +### BlackBerry 10 rarezas + + * **ID**: asignado por el dispositivo cuando se guarda el contacto. + +### FirefoxOS rarezas + + * **categorías**: parcialmente soportado. Campos **pref** y **tipo** regresan`null` + + * **IMS**: no se admite + + * **fotos**: no se admite + +### iOS rarezas + + * **displayName**: no compatible con iOS, regresando `null` si no hay ningún `ContactName` especifica, en cuyo caso devuelve el nombre del compuesto, **apodo** o `""` , respectivamente. + + * **cumpleaños**: debe ser de entrada como un JavaScript `Date` objeto, del mismo modo que se la devuelvan. + + * **fotos**: devuelve una dirección URL del archivo de la imagen, que se almacena en el directorio temporal de la aplicación. Contenidos del directorio temporal se eliminan cuando salga de la aplicación. + + * **categorías**: esta propiedad actualmente no es compatible, regresando`null`. + +### Windows Phone 7 y 8 rarezas + + * **displayName**: cuando se crea un contacto, previsto para el parámetro de nombre pantalla difiere el nombre para mostrar el valor obtenido al encontrar el contacto. + + * **URL**: cuando se crea un contacto, los usuarios pueden ingresar y salvar más de una dirección web, pero sólo está disponible cuando busque el contacto. + + * **números**: no se admite la opción *pref* . El *tipo* no se admite en una operación de *encontrar* . Solamente un `phoneNumber` está permitido para cada *tipo*. + + * **correos electrónicos**: no se admite la opción *pref* . Home y referencias misma entrada de correo electrónico. Se permite solamente una entrada para cada *tipo*. + + * **direcciones**: soporta sólo trabajo y hogar/personal *tipo*. La casa y personales de *tipo* referencia la misma entrada de dirección. Se permite solamente una entrada para cada *tipo*. + + * **organizaciones**: sólo está permitido y no es compatible con los atributos *pref*, *tipo*y *Departamento* . + + * **Nota**: no compatible, regresando`null`. + + * **ims**: no soportado, devolver `null`. + + * **cumpleaños**: no soportado, regresando`null`. + + * **categorías**: no soportado, regresando`null`. + + * **remove**: no se admite el método + +### Windows rarezas + + * **fotos**: devuelve una dirección URL del archivo de la imagen, que se almacena en el directorio temporal de la aplicación. + + * **cumpleaños**: no soportado, regresando`null`. + + * **categorías**: no soportado, regresando`null`. + + * **remove**: el método sólo es soportado en Windows 10 o superior. + +## ContactAddress + +El `ContactAddress` objeto almacena las propiedades de una única dirección de un contacto. A `Contact` objeto puede incluir más de una dirección en un `ContactAddress[]` matriz. + +### Propiedades + + * **pref**: establecido en `true` si esta `ContactAddress` contiene el usuario preferido de valor. *(boolean)* + + * **type**: una cadena que indica qué tipo de campo es, *home* por ejemplo. *(DOMString)* + + * **formatted**: la dirección completa con formato de visualización. *(DOMString)* + + * **streetAddress**: la dirección completa. *(DOMString)* + + * **locality**: la ciudad o localidad. *(DOMString)* + + * **region**: el estado o la región. *(DOMString)* + + * **postalCode**: el código postal o código postal. *(DOMString)* + + * **country**: el nombre del país. *(DOMString)* + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Ejemplo + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Rarezas Android 2.X + + * **pref**: no soportado, devolviendo `false` en dispositivos Android 2.X. + +### BlackBerry 10 rarezas + + * **pref**: no compatible con dispositivos BlackBerry, devolviendo `false`. + + * **type**: parcialmente soportado. Sólo uno de cada *Work* y *Home* tipo direcciones puede ser almacenado por contacto. + + * **formatted**: parcialmente soportado. Devuelve una concatenación de todos los campos de dirección de BlackBerry. + + * **streetAddress**: soportado. Devuelve una concatenación de BlackBerry **address1** y **2** campos de dirección. + + * **locality**: apoyado. Almacenada en el campo de dirección de la **city** de BlackBerry. + + * **region**: apoyado. Almacenada en el campo de dirección de BlackBerry **stateProvince**. + + * **postalCode**: apoyado. Almacenada en el campo de dirección de BlackBerry **zipPostal**. + + * **country**: apoyado. + +### FirefoxOS rarezas + + * **formato**: actualmente no se admite + +### iOS rarezas + + * **pref**: no se admite en dispositivos iOS, devolviendo `false`. + + * **formatted**: actualmente no se admite. + +### Rarezas de Windows 8 + + * **Pref**: no se admite + +### Windows rarezas + + * **Pref**: no se admite + +## ContactError + +El `ContactError` objeto se devuelve al usuario a través de la `contactError` función de devolución de llamada cuando se produce un error. + +### Propiedades + + * **code**: uno de los códigos de error predefinido enumerados a continuación. + +### Constantes + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +El `ContactField` objeto es un componente reutilizable que representa en contacto con campos genéricamente. Cada `ContactField` objeto contiene un `value` , `type` , y `pref` propiedad. A `Contact` objeto almacena varias propiedades en `ContactField[]` arreglos de discos, como las direcciones de teléfono números y correo electrónico. + +En la mayoría de los casos, no existen previamente determinados valores para un `ContactField` atributo **type** del objeto. Por ejemplo, un número de teléfono puede especificar valores de **tipo** de *hogar*, *trabajo*, *móvil*, *iPhone*o cualquier otro valor que es apoyado por contacto de base de datos de una plataforma dispositivo determinado. Sin embargo, para el `Contact` **fotos de** campo, el campo **tipo** indica el formato de la imagen devuelta: **url** cuando el atributo de **valor** contiene una dirección URL de la imagen de la foto, o *base64* cuando el **valor** contiene una cadena codificada en base64 imagen. + +### Propiedades + + * **tipo**: una cadena que indica qué tipo de campo es, *casa* por ejemplo. *(DOMString)* + + * **valor**: el valor del campo, como un teléfono número o dirección de email. *(DOMString)* + + * **Pref**: A `true` si este `ContactField` contiene el valor del usuario preferido. *(booleano)* + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Ejemplo + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Rarezas Android + + * **Pref**: no soportado, regresando`false`. + +### BlackBerry 10 rarezas + + * **tipo**: parcialmente soportado. Utilizado para los números de teléfono. + + * **valor**: apoyado. + + * **Pref**: no soportado, regresando`false`. + +### iOS rarezas + + * **Pref**: no soportado, regresando`false`. + +### Windows8 rarezas + + * **Pref**: no soportado, regresando`false`. + +### Windows rarezas + + * **Pref**: no soportado, regresando`false`. + +## ContactName + +Contiene diferentes tipos de información sobre un `Contact` nombre del objeto. + +### Propiedades + + * **formato**: el nombre completo del contacto. *(DOMString)* + + * **familia**: el nombre del contacto familiar. *(DOMString)* + + * **givenName**: nombre del contacto. *(DOMString)* + + * **middleName**: el nombre del contacto media. *(DOMString)* + + * **honorificPrefix**: prefijo del contacto (ejemplo *señor* o *doctor*) *(DOMString)* + + * **honorificSuffix**: sufijo de contacto (ejemplo *Esq.*). *(DOMString)* + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Ejemplo + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Rarezas Android + + * **formato**: parcialmente compatibles y de sólo lectura. Devuelve una concatenación de `honorificPrefix` , `givenName` , `middleName` , `familyName` , y`honorificSuffix`. + +### BlackBerry 10 rarezas + + * **formato**: parcialmente soportado. Devuelve una concatenación de campos **firstName** y **lastName** de BlackBerry. + + * **familia**: apoyo. Almacenada en el campo **lastName** BlackBerry. + + * **givenName**: apoyado. Almacenados en campo **firstName** BlackBerry. + + * **middleName**: no soportado, regresando`null`. + + * **honorificPrefix**: no soportado, regresando`null`. + + * **honorificSuffix**: no soportado, regresando`null`. + +### FirefoxOS rarezas + + * **formato**: parcialmente compatibles y de sólo lectura. Devuelve una concatenación de `honorificPrefix` , `givenName` , `middleName` , `familyName` , y`honorificSuffix`. + +### iOS rarezas + + * **formato**: parcialmente soportado. Devuelve iOS nombre compuesto, pero es de sólo lectura. + +### Rarezas de Windows 8 + + * **formato**: este es el único nombre de propiedad y es idéntico al `displayName` , y`nickname` + + * **familia**: no se admite + + * **givenName**: no se admite + + * **middleName**: no se admite + + * **honorificPrefix**: no se admite + + * **honorificSuffix**: no se admite + +### Windows rarezas + + * **formato**: es idéntica a`displayName` + +## ContactOrganization + +El `ContactOrganization` objeto almacena las propiedades de organización de un contacto. A `Contact` objeto almacena uno o más `ContactOrganization` los objetos en una matriz. + +### Propiedades + + * **Pref**: A `true` si este `ContactOrganization` contiene el valor del usuario preferido. *(booleano)* + + * **tipo**: una cadena que indica qué tipo de campo es, *casa* por ejemplo. _(DOMString) + + * **nombre**: el nombre de la organización. *(DOMString)* + + * **Departamento**: el contrato de obras para el departamento. *(DOMString)* + + * **título**: título del contacto de la organización. *(DOMString)* + +### Plataformas soportadas + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows (sólo dispositivos Windows 8.1 y 8.1 de Windows Phone) + +### Ejemplo + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Rarezas Android 2.X + + * **Pref**: no compatible con dispositivos Android 2.X, regresando`false`. + +### BlackBerry 10 rarezas + + * **Pref**: no compatible con dispositivos BlackBerry, regresando`false`. + + * **tipo**: no compatible con dispositivos BlackBerry, regresando`null`. + + * **nombre**: parcialmente soportado. El primer nombre de la organización se almacena en el campo de la **empresa** BlackBerry. + + * **Departamento**: no soportado, regresando`null`. + + * **título**: parcialmente soportado. El primer título de la organización se almacena en el campo de **jobTitle** BlackBerry. + +### Firefox OS rarezas + + * **Pref**: no se admite + + * **tipo**: no se admite + + * **Departamento**: no se admite + + * Los campos **nombre** y **título** almacenado en **org** y **jobTitle**. + +### iOS rarezas + + * **pref**: no se admite en dispositivos iOS, devolviendo `false`. + + * **tipo**: no se admite en dispositivos iOS, regresando`null`. + + * **nombre**: parcialmente soportado. El primer nombre de la organización se almacena en el campo de **kABPersonOrganizationProperty** de iOS. + + * **Departamento**: parcialmente soportado. El primer nombre de departamento se almacena en el campo de **kABPersonDepartmentProperty** de iOS. + + * **título**: parcialmente soportado. El primer título se almacena en el campo de **kABPersonJobTitleProperty** de iOS. + +### Windows rarezas + + * **Pref**: no soportado, regresando`false`. + + * **tipo**: no soportado, regresando`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/es/index.md b/plugins/cordova-plugin-contacts/doc/es/index.md new file mode 100644 index 0000000..dc1a4a1 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/es/index.md @@ -0,0 +1,652 @@ + + +# cordova-plugin-contacts + +Este plugin define un global `navigator.contacts` objeto que proporciona acceso a la base de datos de contactos de dispositivo. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(navigator.contacts)}; + + +**ADVERTENCIA**: recopilación y uso de datos plantea cuestiones de privacidad importante. Política de privacidad de su aplicación debe discutir cómo la aplicación utiliza datos de contacto y si es compartida con terceros. Información de contacto se considera sensible porque revela la gente con quien se comunica una persona. Por lo tanto, además de política de privacidad de la app, fuertemente considere dar un aviso de just-in-time antes de la aplicación accede a ellos o utiliza los datos de contacto, si el sistema operativo del dispositivo no hacerlo ya. Que el aviso debe proporcionar la misma información mencionada, además de obtener un permiso del usuario (por ejemplo, presentando opciones para **Aceptar** y **No gracias**). Tenga en cuenta que algunos mercados de aplicación podrán exigir la aplicación para proporcionar un aviso de just-in-time y obtener el permiso del usuario antes de acceder a datos de contacto. Una experiencia de usuario clara y fácil de entender que rodean el uso de contacto datos ayuda a evitar la confusión del usuario y percibe uso indebido de los datos de contacto. Para obtener más información, por favor consulte a la guía de privacidad. + +## Instalación + + Cordova plugin agregar cordova-plugin-contacts + + +### Firefox OS rarezas + +Crear **www/manifest.webapp** como se describe en [Manifestar Docs][1]. Agregar permisos pertinentes. También hay una necesidad de cambiar el tipo de aplicación a "privilegiados" - [Docs manifiestan][2]. **ADVERTENCIA**: todas las apps privilegiadas aplicar [Política de seguridad de contenidos][3] que prohíbe en línea de comandos. Inicializar la aplicación de otra manera. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "tipo": "el privilegio", "permisos": {"contactos": {"acceso": "readwrite", "Descripción": "describir por qué hay una necesidad de tal autorización"}} + + +### Windows rarezas + +Cualquier contacto regresado de `find` y `pickContact` métodos son readonly, por lo que su aplicación no puede modificarlos. `find`método disponible sólo en dispositivos Windows Phone 8.1. + +### Rarezas de Windows 8 + +Windows 8 contactos son de sólo lectura. Través de los contactos de la API de Córdoba no son consultables/búsqueda, se debe informar al usuario a buscar un contacto como una llamada a contacts.pickContact que se abrirá la aplicación 'Personas' donde el usuario debe elegir un contacto. Cualquier contacto volvió es readonly, su aplicación no puede modificarlos. + +## Navigator.Contacts + +### Métodos + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### Objetos + +* Contact +* ContactName +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## Navigator.contacts.Create + +El `navigator.contacts.create` método es sincrónico y devuelve una nueva `Contact` objeto. + +Este método no retiene el objeto de contacto en la base de contactos de dispositivo, para lo cual necesita invocar el `Contact.save` método. + +### Plataformas soportadas + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 + +### Ejemplo + + var myContact = navigator.contacts.create ({"displayName": "Test User"}); + + +## navigator.contacts.find + +El `navigator.contacts.find` método se ejecuta asincrónicamente, consultando la base de datos de contactos de dispositivo y devolver una matriz de `Contact` objetos. Los objetos resultantes son pasados a la `contactSuccess` función de devolución de llamada especificada por el parámetro **contactSuccess** . + +El parámetro **contactFields** especifica los campos para ser utilizado como un calificador de búsqueda. Un parámetro de longitud cero **contactFields** no es válido y resultados en `ContactError.INVALID_ARGUMENT_ERROR` . Un valor de **contactFields** de `"*"` busca campos todo contactos. + +La cadena de **contactFindOptions.filter** puede ser usada como un filtro de búsqueda al consultar la base de datos de contactos. Si proporciona, una entre mayúsculas y minúsculas, coincidencia parcial valor se aplica a cada campo especificado en el parámetro **contactFields** . Si hay un partido para *cualquier* de los campos especificados, se devuelve el contacto. Uso **contactFindOptions.desiredFields** parámetro al control que Contacta con propiedades debe devolverse atrás. + +### Parámetros + +* **contactFields**: póngase en contacto con campos para usar como un calificador de búsqueda. *(DOMString[])* [Required] + +* **contactSuccess**: función de callback de éxito se invoca con la matriz de objetos contacto devueltos desde la base de datos. [Required] + +* **contactError**: función de callback de Error, se invoca cuando se produce un error. [Optional] + +* **contactFindOptions**: buscar opciones para filtrar navigator.contacts. [Optional] + + Claves incluyen: + + * **filtro**: la cadena de búsqueda utilizada para encontrar navigator.contacts. *(DOMString)* (Por defecto:`""`) + + * **múltiples**: determina si la operación de búsqueda devuelve múltiples navigator.contacts. *(Booleano)* (Por defecto:`false`) + + * **desiredFields**: póngase en contacto con campos para volver atrás. Si se especifica, la resultante `Contact` objeto sólo cuenta con los valores de estos campos. *(DOMString[])* [Optional] + +### Plataformas soportadas + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows (sólo dispositivos Windows Phone 8.1) + +### Ejemplo + + function onSuccess(contacts) {alert ('Encontrados' + contacts.length + 'contactos de.');}; + + function onError(contactError) {alert('onError!');}; + + encuentra todos los contactos con 'Bob' en cualquier nombre campo var opciones = new ContactFindOptions(); + options.Filter = "Bob"; + options.Multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + campos var = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + Navigator.contacts.Find (campos, onSuccess, onError, opciones); + + +### Windows rarezas + +* `__contactFields__`No se admite y se ignorará. `find`método siempre tratará de coincidir con el nombre, dirección de correo electrónico o número de teléfono de un contacto. + +## navigator.contacts.pickContact + +El `navigator.contacts.pickContact` método lanza el selector para seleccionar un único contacto contacto. El objeto resultante se pasa a la `contactSuccess` función de devolución de llamada especificada por el parámetro **contactSuccess** . + +### Parámetros + +* **contactSuccess**: función de callback de éxito se invoca con el único objeto de contacto. [Obligatorio] + +* **contactError**: función de callback de Error, se invoca cuando se produce un error. [Opcional] + +### Plataformas soportadas + +* Android +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### Ejemplo + + navigator.contacts.pickContact(function(contact) {console.log (' se ha seleccionado el siguiente contacto: "+ JSON.stringify(contact)); + }, function(err) {console.log ('Error: ' + err); + }); + + +## Contact + +El `Contact` objeto representa el contacto de un usuario. Contactos pueden ser creados, almacenados o eliminados de la base de datos de contactos de dispositivo. Contactos pueden también ser obtenidos (individualmente o a granel) de la base de datos invocando el `navigator.contacts.find` método. + +**Nota**: no todos los campos de contacto mencionados son compatibles con la plataforma de cada dispositivo. Consulte sección *peculiaridades* de cada plataforma para más detalles. + +### Propiedades + +* **ID**: un identificador único global. *(DOMString)* + +* **displayName**: el nombre de este contacto, conveniente para la exhibición a los usuarios finales. *(DOMString)* + +* **nombre**: un objeto que contiene todos los componentes de un nombre de las personas. *(ContactName)* + +* **apodo**: un nombre para abordar el contacto casual. *(DOMString)* + +* **números**: una matriz de números de teléfono de contacto. *(ContactField[])* + +* **correos electrónicos**: un conjunto de direcciones de correo electrónico del contacto. *(ContactField[])* + +* **direcciones**: un conjunto de direcciones de todos los contactos. *(ContactAddress[])* + +* **IMS**: un conjunto de direcciones de todos los contactos IM. *(ContactField[])* + +* **organizaciones**: un conjunto de organizaciones de todos los contactos. *(ContactOrganization[])* + +* **cumpleaños**: el cumpleaños del contacto. *(Fecha)* + +* **Nota**: una nota sobre el contacto. *(DOMString)* + +* **fotos**: una serie de fotos de los contactos. *(ContactField[])* + +* **categorías**: una matriz de todas las categorías definidas por el usuario asociado con el contacto. *(ContactField[])* + +* **URL**: un conjunto de páginas web asociadas con el contacto. *(ContactField[])* + +### Métodos + +* **clon**: devuelve un nuevo `Contact` objeto que es una copia en profundidad del objeto de llamadas con el `id` propiedad establecida en`null`. + +* **eliminar**: elimina el contacto de la base de datos de contactos de dispositivo, si no se ejecuta un callback de error con un `ContactError` objeto. + +* **Guardar**: guarda un nuevo contacto en la base de datos de contactos de dispositivo o actualiza un contacto existente si ya existe un contacto con el mismo **id** . + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Salvar ejemplo + + function onSuccess(contact) {alert ("salvar con éxito");}; + + function onError(contactError) {alert ("Error =" + contactError.code);}; + + crear un nuevo objeto contacto var contacto = navigator.contacts.create(); + contact.displayName = "Plomero"; + Contact.nickname = "Plomero"; especificar tanto para todos los dispositivos de apoyo / / rellenar algún nombre var campos = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + Contact.name = nombre; + + guardar en el dispositivo contact.save(onSuccess,onError); + + +### Ejemplo de clon + + clon del clon de contacto objeto var = contact.clone(); + clone.name.givenName = "John"; + Console.log ("contacto Original nombre =" + contact.name.givenName); + Console.log ("Cloned contacto nombre =" + clone.name.givenName); + + +### Quitar ejemplo + + function onSuccess() {alert ("retiro el éxito");}; + + function onError(contactError) {alert ("Error =" + contactError.code);}; + + quitar el contacto de la contact.remove(onSuccess,onError) del dispositivo; + + +### Rarezas Android 2.X + +* **categories**: no compatible con dispositivos Android 2.X, devolver `null`. + +### BlackBerry 10 rarezas + +* **ID**: asignado por el dispositivo cuando se guarda el contacto. + +### FirefoxOS rarezas + +* **categorías**: parcialmente soportado. Campos **pref** y **tipo** regresan`null` + +* **IMS**: no se admite + +* **fotos**: no se admite + +### iOS rarezas + +* **displayName**: no compatible con iOS, regresando `null` si no hay ningún `ContactName` especifica, en cuyo caso devuelve el nombre del compuesto, **apodo** o `""` , respectivamente. + +* **cumpleaños**: debe ser de entrada como un JavaScript `Date` objeto, del mismo modo que se la devuelvan. + +* **fotos**: devuelve una dirección URL del archivo de la imagen, que se almacena en el directorio temporal de la aplicación. Contenidos del directorio temporal se eliminan cuando salga de la aplicación. + +* **categorías**: esta propiedad actualmente no es compatible, regresando`null`. + +### Windows Phone 7 y 8 rarezas + +* **displayName**: cuando se crea un contacto, previsto para el parámetro de nombre pantalla difiere el nombre para mostrar el valor obtenido al encontrar el contacto. + +* **URL**: cuando se crea un contacto, los usuarios pueden ingresar y salvar más de una dirección web, pero sólo está disponible cuando busque el contacto. + +* **números**: no se admite la opción *pref* . El *tipo* no se admite en una operación de *encontrar* . Solamente un `phoneNumber` está permitido para cada *tipo*. + +* **correos electrónicos**: no se admite la opción *pref* . Home y referencias misma entrada de correo electrónico. Se permite solamente una entrada para cada *tipo*. + +* **direcciones**: soporta sólo trabajo y hogar/personal *tipo*. La casa y personales de *tipo* referencia la misma entrada de dirección. Se permite solamente una entrada para cada *tipo*. + +* **organizaciones**: sólo está permitido y no es compatible con los atributos *pref*, *tipo*y *Departamento* . + +* **Nota**: no compatible, regresando`null`. + +* **ims**: no soportado, devolver `null`. + +* **cumpleaños**: no soportado, regresando`null`. + +* **categorías**: no soportado, regresando`null`. + +### Windows rarezas + +* **fotos**: devuelve una dirección URL del archivo de la imagen, que se almacena en el directorio temporal de la aplicación. + +* **cumpleaños**: no soportado, regresando`null`. + +* **categorías**: no soportado, regresando`null`. + +## ContactAddress + +El `ContactAddress` objeto almacena las propiedades de una única dirección de un contacto. A `Contact` objeto puede incluir más de una dirección en un `ContactAddress[]` matriz. + +### Propiedades + +* **pref**: establecido en `true` si esta `ContactAddress` contiene el usuario preferido de valor. *(boolean)* + +* **type**: una cadena que indica qué tipo de campo es, *home* por ejemplo. *(DOMString)* + +* **formatted**: la dirección completa con formato de visualización. *(DOMString)* + +* **streetAddress**: la dirección completa. *(DOMString)* + +* **locality**: la ciudad o localidad. *(DOMString)* + +* **region**: el estado o la región. *(DOMString)* + +* **postalCode**: el código postal o código postal. *(DOMString)* + +* **country**: el nombre del país. *(DOMString)* + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Ejemplo + + Mostrar la información de dirección para el funcionan de todos los contactos onSuccess(contacts) {para (var = 0; < contacts.length; i ++) {para (var j = 0; j < contacts[i].addresses.length; j ++) {alert ("Pref:" + contacts[i].addresses[j].pref + "\n" + "tipo:" + contacts[i].addresses[j].type + "\n" + "formato:" + contacts[i].addresses[j].formatted + "\n" + "dirección: "+ contacts[i].addresses[j].streetAddress +"\n"+" localidad: "+ contacts[i].addresses[j].locality +"\n"+" región: "+ contacts[i].addresses[j].region +"\n"+" Código Postal: "+ contacts[i].addresses[j].postalCode +"\n"+" país: "+ contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) {alert('onError!');}; + + encontrar todos los contactos var opciones = new ContactFindOptions(); + options.Filter = ""; + filtro var = ["displayName", "direcciones"]; + Navigator.contacts.Find (filtro, onSuccess, onError, opciones); + + +### Rarezas Android 2.X + +* **pref**: no soportado, devolviendo `false` en dispositivos Android 2.X. + +### BlackBerry 10 rarezas + +* **pref**: no compatible con dispositivos BlackBerry, devolviendo `false`. + +* **type**: parcialmente soportado. Sólo uno de cada *Work* y *Home* tipo direcciones puede ser almacenado por contacto. + +* **formatted**: parcialmente soportado. Devuelve una concatenación de todos los campos de dirección de BlackBerry. + +* **streetAddress**: soportado. Devuelve una concatenación de BlackBerry **address1** y **2** campos de dirección. + +* **locality**: apoyado. Almacenada en el campo de dirección de la **city** de BlackBerry. + +* **region**: apoyado. Almacenada en el campo de dirección de BlackBerry **stateProvince**. + +* **postalCode**: apoyado. Almacenada en el campo de dirección de BlackBerry **zipPostal**. + +* **country**: apoyado. + +### FirefoxOS rarezas + +* **formato**: actualmente no se admite + +### iOS rarezas + +* **pref**: no se admite en dispositivos iOS, devolviendo `false`. + +* **formatted**: actualmente no se admite. + +### Rarezas de Windows 8 + +* **Pref**: no se admite + +### Windows rarezas + +* **Pref**: no se admite + +## ContactError + +El `ContactError` objeto se devuelve al usuario a través de la `contactError` función de devolución de llamada cuando se produce un error. + +### Propiedades + +* **code**: uno de los códigos de error predefinido enumerados a continuación. + +### Constantes + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +El `ContactField` objeto es un componente reutilizable que representa en contacto con campos genéricamente. Cada `ContactField` objeto contiene un `value` , `type` , y `pref` propiedad. A `Contact` objeto almacena varias propiedades en `ContactField[]` arreglos de discos, como las direcciones de teléfono números y correo electrónico. + +En la mayoría de los casos, no existen previamente determinados valores para un `ContactField` atributo **type** del objeto. Por ejemplo, un número de teléfono puede especificar valores de **tipo** de *hogar*, *trabajo*, *móvil*, *iPhone*o cualquier otro valor que es apoyado por contacto de base de datos de una plataforma dispositivo determinado. Sin embargo, para el `Contact` **fotos de** campo, el campo **tipo** indica el formato de la imagen devuelta: **url** cuando el atributo de **valor** contiene una dirección URL de la imagen de la foto, o *base64* cuando el **valor** contiene una cadena codificada en base64 imagen. + +### Propiedades + +* **tipo**: una cadena que indica qué tipo de campo es, *casa* por ejemplo. *(DOMString)* + +* **valor**: el valor del campo, como un teléfono número o dirección de email. *(DOMString)* + +* **Pref**: A `true` si este `ContactField` contiene el valor del usuario preferido. *(booleano)* + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Ejemplo + + crear un nuevo contacto contacto var = navigator.contacts.create(); + + almacenar números de teléfono de contacto en números de var ContactField [] = []; + phoneNumbers[0] = new ContactField ('trabajo', ' 212-555-1234', false); + phoneNumbers[1] = new ContactField ('móviles', ' 917-555-5432', true); recomendado: número phoneNumbers[2] = new ContactField ('home', ' 203-555-7890', false); + contact.phoneNumbers = números; + + guardar el contacto contact.save(); + + +### Rarezas Android + +* **Pref**: no soportado, regresando`false`. + +### BlackBerry 10 rarezas + +* **tipo**: parcialmente soportado. Utilizado para los números de teléfono. + +* **valor**: apoyado. + +* **Pref**: no soportado, regresando`false`. + +### iOS rarezas + +* **Pref**: no soportado, regresando`false`. + +### Windows8 rarezas + +* **Pref**: no soportado, regresando`false`. + +### Windows rarezas + +* **Pref**: no soportado, regresando`false`. + +## ContactName + +Contiene diferentes tipos de información sobre un `Contact` nombre del objeto. + +### Propiedades + +* **formato**: el nombre completo del contacto. *(DOMString)* + +* **familia**: el nombre del contacto familiar. *(DOMString)* + +* **givenName**: nombre del contacto. *(DOMString)* + +* **middleName**: el nombre del contacto media. *(DOMString)* + +* **honorificPrefix**: prefijo del contacto (ejemplo *señor* o *doctor*) *(DOMString)* + +* **honorificSuffix**: sufijo de contacto (ejemplo *Esq.*). *(DOMString)* + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Ejemplo + + función onSuccess(contacts) {para (var = 0; < contacts.length; i ++) {alert ("formateada:" + contacts[i].name.formatted + "\n" + "Apellido:" + contacts[i].name.familyName + "\n" + "nombre:" + contacts[i].name.givenName + "\n" + "segundo nombre:" + contacts[i].name.middleName + "\n" + "sufijo:" + contacts[i].name.honorificSuffix + "\n" + "prefijo:" + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) {alert('onError!');}; + + var opciones = new ContactFindOptions(); + options.Filter = ""; + filtro = ["displayName", "nombre"]; + Navigator.contacts.Find (filtro, onSuccess, onError, opciones); + + +### Rarezas Android + +* **formato**: parcialmente compatibles y de sólo lectura. Devuelve una concatenación de `honorificPrefix` , `givenName` , `middleName` , `familyName` , y`honorificSuffix`. + +### BlackBerry 10 rarezas + +* **formato**: parcialmente soportado. Devuelve una concatenación de campos **firstName** y **lastName** de BlackBerry. + +* **familia**: apoyo. Almacenada en el campo **lastName** BlackBerry. + +* **givenName**: apoyado. Almacenados en campo **firstName** BlackBerry. + +* **middleName**: no soportado, regresando`null`. + +* **honorificPrefix**: no soportado, regresando`null`. + +* **honorificSuffix**: no soportado, regresando`null`. + +### FirefoxOS rarezas + +* **formato**: parcialmente compatibles y de sólo lectura. Devuelve una concatenación de `honorificPrefix` , `givenName` , `middleName` , `familyName` , y`honorificSuffix`. + +### iOS rarezas + +* **formato**: parcialmente soportado. Devuelve iOS nombre compuesto, pero es de sólo lectura. + +### Rarezas de Windows 8 + +* **formato**: este es el único nombre de propiedad y es idéntico al `displayName` , y`nickname` + +* **familia**: no se admite + +* **givenName**: no se admite + +* **middleName**: no se admite + +* **honorificPrefix**: no se admite + +* **honorificSuffix**: no se admite + +### Windows rarezas + +* **formato**: es idéntica a`displayName` + +## ContactOrganization + +El `ContactOrganization` objeto almacena las propiedades de organización de un contacto. A `Contact` objeto almacena uno o más `ContactOrganization` los objetos en una matriz. + +### Propiedades + +* **Pref**: A `true` si este `ContactOrganization` contiene el valor del usuario preferido. *(booleano)* + +* **tipo**: una cadena que indica qué tipo de campo es, *casa* por ejemplo. _(DOMString) + +* **nombre**: el nombre de la organización. *(DOMString)* + +* **Departamento**: el contrato de obras para el departamento. *(DOMString)* + +* **título**: título del contacto de la organización. *(DOMString)* + +### Plataformas soportadas + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows (sólo dispositivos Windows 8.1 y 8.1 de Windows Phone) + +### Ejemplo + + function onSuccess(contacts) {para (var = 0; < contacts.length; i ++) {para (var j = 0; j < contacts[i].organizations.length; j ++) {alert ("Pref:" + contacts[i].organizations[j].pref + "\n" + "tipo:" + contacts[i].organizations[j].type + "\n" + "nombre:" + contacts[i].organizations[j].name + "\n" + "Departamento:" + contacts[i].organizations[j].department + "\n" + "título: "+ contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) {alert('onError!');}; + + var opciones = new ContactFindOptions(); + options.Filter = ""; + filtro = ["displayName", "organizaciones"]; + Navigator.contacts.Find (filtro, onSuccess, onError, opciones); + + +### Rarezas Android 2.X + +* **Pref**: no compatible con dispositivos Android 2.X, regresando`false`. + +### BlackBerry 10 rarezas + +* **Pref**: no compatible con dispositivos BlackBerry, regresando`false`. + +* **tipo**: no compatible con dispositivos BlackBerry, regresando`null`. + +* **nombre**: parcialmente soportado. El primer nombre de la organización se almacena en el campo de la **empresa** BlackBerry. + +* **Departamento**: no soportado, regresando`null`. + +* **título**: parcialmente soportado. El primer título de la organización se almacena en el campo de **jobTitle** BlackBerry. + +### Firefox OS rarezas + +* **Pref**: no se admite + +* **tipo**: no se admite + +* **Departamento**: no se admite + +* Los campos **nombre** y **título** almacenado en **org** y **jobTitle**. + +### iOS rarezas + +* **pref**: no se admite en dispositivos iOS, devolviendo `false`. + +* **tipo**: no se admite en dispositivos iOS, regresando`null`. + +* **nombre**: parcialmente soportado. El primer nombre de la organización se almacena en el campo de **kABPersonOrganizationProperty** de iOS. + +* **Departamento**: parcialmente soportado. El primer nombre de departamento se almacena en el campo de **kABPersonDepartmentProperty** de iOS. + +* **título**: parcialmente soportado. El primer título se almacena en el campo de **kABPersonJobTitleProperty** de iOS. + +### Windows rarezas + +* **Pref**: no soportado, regresando`false`. + +* **tipo**: no soportado, regresando`null`. diff --git a/plugins/cordova-plugin-contacts/doc/fr/README.md b/plugins/cordova-plugin-contacts/doc/fr/README.md new file mode 100644 index 0000000..cd745ee --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/fr/README.md @@ -0,0 +1,668 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +Ce plugin définit un global `navigator.contacts` objet, ce qui permet d'accéder à la base de données de contacts de dispositif. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.contacts);} + + +**Avertissement**: collecte et utilisation des données de contact soulève des questions importantes de la vie privée. Politique de confidentialité de votre application doit examiner comment l'application utilise les données de contact et si il est partagé avec d'autres parties. Information de contact est considéré comme sensible parce qu'il révèle les gens avec lesquels une personne communique. Par conséquent, en plus de la politique de confidentialité de l'application, vous devez envisager fortement fournissant un avis juste-à-temps, avant que l'application accède ou utilise des données de contact, si le système d'exploitation de périphérique ne fait donc pas déjà. Cet avis doit fournir les mêmes renseignements susmentionnées, ainsi que d'obtenir l'autorisation de l'utilisateur (par exemple, en présentant des choix **OK** et **Non merci**). Notez que certains marchés app peuvent exiger l'application de fournir un avis juste-à-temps et obtenir la permission de l'utilisateur avant d'accéder à des données de contact. Une expérience utilisateur claire et facile à comprendre qui entourent l'utilisation de données permettent d'éviter la confusion des utilisateurs de contact et une utilisation jugée abusive des données de contact. Pour plus d'informations, consultez le Guide de la vie privée. + +## Installation + +Pour cela, cordova 5.0 + (v1.0.0 stable actuelle) + + cordova plugin add cordova-plugin-contacts + + +Anciennes versions de cordova peuvent toujours installer via l'id **déconseillée** (v0.2.16 rassis) + + cordova plugin add org.apache.cordova.contacts + + +Il est également possible d'installer directement via l'url de repo (instable) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS Quirks + +Créez **www/manifest.webapp** comme décrit dans [Les Docs manifeste](https://developer.mozilla.org/en-US/Apps/Developing/Manifest). Ajouter permisions pertinentes. Il est également nécessaire de changer le type d'application Web de « privilégiés » - [Docs manifeste](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type). **Avertissement**: toutes les applications privilégiées appliquer [Contenu politique de sécurité](https://developer.mozilla.org/en-US/Apps/CSP) qui interdit à un script inline. Initialiser votre application d'une autre manière. + + « type »: "le privilège", "autorisations": {« contacts »: {« accès »: "readwrite", "description": "décrire pourquoi il est nécessaire pour obtenir cette permission"}} + + +### Bizarreries de Windows + +**Avant Windows 10 :** Tout contact retourné par les méthodes `find` et `pickContact` est en lecture seule, afin que votre application ne puisse les modifier. `find`méthode disponible uniquement sur les appareils Windows Phone 8.1. + +**Windows 10 et plus :** Contacts peuvent être enregistrées et seront enregistrées dans le stockage local application contacts. Contacts peuvent également être supprimés. + +### Bizarreries de Windows 8 + +Windows 8 Contacts sont en lecture seule. Via les Contacts d'API Cordova ne sont pas queryable/consultables, vous devez en informer l'utilisateur de choisir un contact comme un appel à contacts.pickContact qui va ouvrir l'application « People » où l'utilisateur doit choisir un contact. Les contacts retournés sont en lecture seule, afin que votre application ne puisse les modifier. + +## Navigator.contacts + +### Méthodes + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### Objets + + * Contact + * ContactName + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +La `navigator.contacts.create` méthode est synchrone et retourne un nouveau `Contact` objet. + +Cette méthode ne conserve pas l'objet de Contact dans la base de données des contacts périphériques, dont vous avez besoin d'appeler le `Contact.save` méthode. + +### Plates-formes supportées + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + +### Exemple + + myContact var = navigator.contacts.create ({« displayName »: « Test User »}) ; + + +## navigator.contacts.find + +La `navigator.contacts.find` méthode s'exécute de façon asynchrone, l'interrogation de la base de données de contacts de dispositif et retourne un tableau de `Contact` objets. Les objets résultants sont passés à la `contactSuccess` la fonction de rappel spécifiée par le paramètre **contactSuccess** . + +Le paramètre **contactFields** spécifie les champs à utiliser comme un qualificateur de recherche. Un paramètre de longueur nulle **contactFields** n'est pas valide et se traduit par `ContactError.INVALID_ARGUMENT_ERROR` . Une valeur de **contactFields** de `"*"` recherche dans les champs de tout contact. + +La chaîne **contactFindOptions.filter** peut servir comme un filtre de recherche lors de l'interrogation de la base de données de contacts. Si fourni, un non-respect de la casse, correspondance de valeur partielle est appliquée à chaque champ spécifié dans le paramètre **contactFields** . S'il y a une correspondance pour *n'importe quel* des champs spécifiés, le contact est retourné. Utilisation **contactFindOptions.desiredFields** paramètre de contrôle qui contacter propriétés doit être retourné au retour. + +### Paramètres + + * **contactFields**: communiquer avec les champs à utiliser comme un qualificateur de recherche. *(DOMString[])* [Required] + + * **contactSuccess**: fonction de rappel de succès avec le tableau d'objets Contact appelée retournée par la base de données. [Required] + + * **contactError**: fonction de rappel d'erreur, appelée lorsqu'une erreur se produit. [Facultatif] + + * **contactFindOptions**: recherche d'options pour filtrer navigator.contacts. [Optional] + + Clés incluent : + + * **filtre**: la chaîne de recherche utilisée pour trouver navigator.contacts. *(DOMString)* (Par défaut :`""`) + + * **multiples**: détermine si l'opération find retourne plusieurs navigator.contacts. *(Booléen)* (Par défaut :`false`) + + * **desiredFields**: Contactez champs soit retourné en arrière. Si spécifié, l'entraînant `Contact` objet dispose seulement des valeurs de ces champs. *(DOMString[])* [Optional] + +### Plates-formes supportées + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows (Windows Phone, 8.1 et Windows 10) + +### Exemple + + function onSuccess(contacts) {alert (« Found » + contacts.length + « contacts. »);} ; + + function onError(contactError) {alert('onError!');} ; + + trouver tous les contacts avec « Bob » dans toute option de var de champ nom = new ContactFindOptions() ; + options.Filter = « Bob » ; + options.multiple = true ; + options.desiredFields = [navigator.contacts.fieldType.id] ; + champs var = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name] ; + Navigator.contacts.Find (champs, onSuccess, onError, options) ; + + +### Bizarreries de Windows + + * `__contactFields__`n'est pas prise en charge et sera ignorée. `find`méthode toujours tenter de faire correspondre le nom, adresse e-mail ou numéro de téléphone d'un contact. + +## navigator.contacts.pickContact + +La `navigator.contacts.pickContact` méthode lance le sélecteur de Contact pour sélectionner un contact unique. L'objet qui en résulte est passé à la `contactSuccess` la fonction de rappel spécifiée par le paramètre **contactSuccess** . + +### Paramètres + + * **contactSuccess**: fonction de rappel de succès appelée avec l'objet de Contact unique. [Obligatoire] + + * **contactError**: fonction de rappel d'erreur, appelée lorsqu'une erreur se produit. [Facultatif] + +### Plates-formes supportées + + * Android + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### Exemple + + navigator.contacts.pickContact(function(contact) {console.log ("le contact suivant a été retenu:" + JSON.stringify(contact)) ; + }, function(err) {console.log ("Error:" + err) ; + }); + + +## Contact + +Le `Contact` objet représente le contact de l'utilisateur. Contacts peuvent être créés, conservés ou supprimés de la base de données de contacts de dispositif. Contacts peuvent également être récupérées (individuellement ou en vrac) de la base de données en appelant le `navigator.contacts.find` méthode. + +**NOTE**: tous les champs de contact susmentionnés ne sont pris en charge sur chaque plate-forme de périphérique. S'il vous plaît vérifier la section *bizarreries* de chaque plate-forme pour plus de détails. + +### Propriétés + + * **ID**: un identificateur global unique. *(DOMString)* + + * **displayName**: le nom de ce Contact, approprié pour l'affichage à l'utilisateur final. *(DOMString)* + + * **nom**: un objet contenant tous les composants d'un nom de personnes. *(ContactName)* + + * **Pseudo**: un nom occasionnel permettant de régler le contact. *(DOMString)* + + * **phoneNumbers**: un tableau des numéros de téléphone du contact. *(ContactField[])* + + * **courriels**: un tableau d'adresses de courriel du contact. *(ContactField[])* + + * **adresses**: un tableau d'adresses tous les contacts. *(ContactAddress[])* + + * **IMS**: un tableau d'adresses IM tout le contact. *(ContactField[])* + + * **organisations**: un tableau des organisations de tout le contact. *(ContactOrganization[])* + + * **anniversaire**: l'anniversaire du contact. *(Date)* + + * **Remarque**: une remarque sur le contact. *(DOMString)* + + * **photos**: un tableau de photos du contact. *(ContactField[])* + + * **catégories**: un tableau de toutes les catégories définies par l'utilisateur attribuée au contact. *(ContactField[])* + + * **URL**: un tableau des pages web attribuée au contact. *(ContactField[])* + +### Méthodes + + * **Clone**: retourne un nouveau `Contact` objet qui est une copie complète de l'objet appelant, avec le `id` propriété la valeur`null`. + + * **supprimer**: supprime le contact de la base de données de contacts de dispositif, sinon exécute un rappel d'erreur avec un `ContactError` objet. + + * **Enregistrer**: enregistre un nouveau contact dans la base de données de contacts de périphérique, ou met à jour un contact existant, si un contact avec le même **id** existe déjà. + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Enregistrez l'exemple + + function onSuccess(contact) {alert ("sauver succès");} ; + + function onError(contactError) {alert ("erreur =" + contactError.code);} ; + + créer un objet contact contact var = navigator.contacts.create() ; + contact.displayName = « Plombier » ; + contact.Nickname = « Plombier » ; spécifier à la fois pour prendre en charge tous les périphériques / / renseigner certains champs var nom = new ContactName() ; + name.givenName = « Jane » ; + name.familyName = « Doe » ; + contact.name = nom ; + + enregistrer dans contact.save(onSuccess,onError) de l'appareil ; + + +### Exemple de clone + + Clone clone objet contact var = contact.clone() ; + clone.name.givenName = « John » ; + Console.log ("contact Original nom =" + contact.name.givenName) ; + Console.log ("nom de contact clonés =" + clone.name.givenName) ; + + +### Supprimer l'exemple + + fonction onSuccess() {alert ("succès");} ; + + function onError(contactError) {alert ("erreur =" + contactError.code);} ; + + supprimer le contact de l'appareil contact.remove(onSuccess,onError) ; + + +### Android 2.X Quirks + + * **catégories**: non pris en charge sur les périphériques Android 2.X, retour`null`. + +### BlackBerry 10 Quirks + + * **ID**: assignés par l'appareil lors de l'enregistrement du contact. + +### Bizarreries de FirefoxOS + + * **catégories**: partiellement pris en charge. Champs **pref** et **type** sont de retour`null` + + * **IMS**: non pris en charge + + * **photos**: ne pas pris en charge + +### Notes au sujet d'iOS + + * **displayName**: ne pas possible sur iOS, retour `null` à moins qu'il n'y a aucun `ContactName` spécifié, auquel cas, il renvoie le nom composite, **Pseudo** ou `""` , respectivement. + + * **anniversaire**: doit être entré comme un JavaScript `Date` objet, de la même façon qu'il soit retourné. + + * **photos**: retourne une URL de fichier de l'image, qui est stocké dans le répertoire temporaire de l'application. Contenu du répertoire temporaire est supprimés lorsque l'application se ferme. + + * **catégories**: cette propriété n'est actuellement pas supportée, retour`null`. + +### Notes au sujet de Windows Phone 7 et 8 + + * **displayName**: lorsque vous créez un contact, la valeur fournie pour le paramètre de nom d'affichage est différent de l'affichage nom Récupérée lors de la recherche du contact. + + * **URL**: lorsque vous créez un contact, les utilisateurs peuvent entrer et enregistrer plus d'une adresse web, mais seulement un est disponible lors de la recherche du contact. + + * **phoneNumbers**: l'option de *pref* n'est pas pris en charge. Le *type* n'est pas pris en charge lors d'une opération de *trouver* . Seul `phoneNumber` est autorisé pour chaque *type*. + + * **courriels**: l'option de *pref* n'est pas pris en charge. Accueil et personnels références même courriel entrée. Une seule participation est autorisée pour chaque *type*. + + * **adresses**: prend en charge seulement travail et accueil/personal *type*. La maison et personnels de *type* référence la même entrée d'adresse. Une seule participation est autorisée pour chaque *type*. + + * **organisations**: seul est autorisé et ne supporte pas les attributs *pref*, *type*et *Département* . + + * **Remarque**: ne pas pris en charge, retour`null`. + + * **IMS**: ne pas pris en charge, retour`null`. + + * **anniversaires**: ne pas pris en charge, retour`null`. + + * **catégories**: ne pas pris en charge, retour`null`. + + * **remove**: méthode n'est pas prise en charge + +### Bizarreries de Windows + + * **photos**: retourne une URL de fichier de l'image, qui est stocké dans le répertoire temporaire de l'application. + + * **anniversaires**: ne pas pris en charge, retour`null`. + + * **catégories**: ne pas pris en charge, retour`null`. + + * **remove**: méthode est uniquement pris en charge dans Windows 10 ou supérieur. + +## ContactAddress + +Le `ContactAddress` objet Stocke les propriétés d'une seule adresse d'un contact. A `Contact` objet peut inclure plusieurs adresses dans un `ContactAddress[]` tableau. + +### Propriétés + + * **pref**: la valeur `true` si ce `ContactAddress` contient la valeur de préférence de l'utilisateur. *(booléen)* + + * **type**: une chaîne qui indique quel type de terrain c'est le cas, *maison* par exemple. *(DOMString)* + + * **mise en forme**: l'adresse complète au format pour l'affichage. *(DOMString)* + + * **adresse**: l'adresse complète. *(DOMString)* + + * **localité**: la ville ou la localité. *(DOMString)* + + * **région**: l'État ou la région. *(DOMString)* + + * **Code postal**: le code zip ou code postal. *(DOMString)* + + * **pays**: le nom du pays. *(DOMString)* + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Exemple + + Affichez les informations d'adresse pour tous les contacts fonctionnent onSuccess(contacts) {pour (var j'ai = 0; j'ai < contacts.length; i ++) {pour (var j = 0; j < contacts[i].addresses.length; j ++) {alert ("Pref:" + contacts[i].addresses[j].pref + « \n » + "Type:" + contacts[i].addresses[j].type + « \n » + "au format:" + contacts[i].addresses[j].formatted + « \n » + "adresse de rue: "+ contacts[i].addresses[j].streetAddress +"\n"+" localité: "+ contacts[i].addresses[j].locality +"\n"+" région: "+ contacts[i].addresses[j].region +"\n"+" Code Postal: "+ contacts[i].addresses[j].postalCode +"\n"+" pays: "+ contacts[i].addresses[j].country) ; + } + } + }; + + function onError(contactError) {alert('onError!');} ; + + trouver tous les contacts options var = new ContactFindOptions() ; + options.Filter = "" ; + filtre var = ["name", « adresses »] ; + Navigator.contacts.Find (filtre, onSuccess, onError, options) ; + + +### Android 2.X Quirks + + * **pref**: ne pas pris en charge, retour `false` sur les appareils Android 2.X. + +### BlackBerry 10 Quirks + + * **pref**: non pris en charge sur les appareils BlackBerry, retour`false`. + + * **type**: partiellement pris en charge. Seule chaque de *travail* et tapez les adresses de *la maison* peut être stockée par contact. + + * **au format**: partiellement pris en charge. Retourne la concaténation de tous les champs d'adresse BlackBerry. + + * **streetAddress**: prise en charge. Retourne la concaténation de BlackBerry **address1** et **address2** champs d'adresse. + + * **localité**: prise en charge. Stockée dans le champ d'adresse BlackBerry **ville** . + + * **région**: pris en charge. Stockée dans le champ d'adresse BlackBerry **stateProvince** . + + * **Code postal**: prise en charge. Stockée dans le champ d'adresse BlackBerry **zipPostal** . + + * **pays**: prise en charge. + +### Bizarreries de FirefoxOS + + * **au format**: actuellement ne pas pris en charge + +### Notes au sujet d'iOS + + * **pref**: non pris en charge sur les appareils iOS, retour`false`. + + * **au format**: actuellement ne pas pris en charge. + +### Bizarreries de Windows 8 + + * **pref**: non pris en charge + +### Bizarreries de Windows + + * **pref**: non pris en charge + +## ContactError + +Le `ContactError` objet est retourné à l'utilisateur via le `contactError` fonction de rappel lorsqu'une erreur survient. + +### Propriétés + + * **code**: l'un des codes d'erreur prédéfinis énumérés ci-dessous. + +### Constantes + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Le `ContactField` objet est un composant réutilisable que représente contacter champs génériquement. Chaque `ContactField` objet contient un `value` , `type` , et `pref` propriété. A `Contact` objet stocke plusieurs propriétés dans `ContactField[]` tableaux, tels que téléphone numéros et adresses e-mail. + +Dans la plupart des cas, il n'y a pas de valeurs prédéterminées pour une `ContactField` l'attribut **type** de l'objet. Par exemple, un numéro de téléphone peut spécifier des valeurs de **type** de la *maison*, *travail*, *mobile*, *iPhone*ou toute autre valeur qui est pris en charge par la base de contacts de la plate-forme un périphérique particulier. Toutefois, pour les `Contact` **photos** champ, le champ **type** indique le format de l'image retournée : **url** lorsque l'attribut **value** contient une URL vers l'image photo ou *base64* lorsque la **valeur** contient une chaîne codée en base64 image. + +### Propriétés + + * **type**: une chaîne qui indique quel type de terrain c'est le cas, *maison* par exemple. *(DOMString)* + + * **valeur**: la valeur du champ, comme un téléphone numéro ou adresse e-mail. *(DOMString)* + + * **pref**: la valeur `true` si ce `ContactField` contient la valeur de préférence de l'utilisateur. *(booléen)* + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Exemple + + créer un nouveau contact contact var = navigator.contacts.create() ; + + stocker des numéros de téléphone de contact en ContactField [] var phoneNumbers = [] ; + phoneNumbers[0] = new ContactField (« travail », ' 212-555-1234', false) ; + phoneNumbers[1] = new ContactField (« mobile », ' 917-555-5432', true) ; phoneNumbers[2] numéro préféré = new ContactField (« home », ' 203-555-7890', false) ; + contact.phoneNumbers = phoneNumbers ; + + enregistrer le contact contact.save() ; + + +### Quirks Android + + * **pref**: ne pas pris en charge, retour`false`. + +### BlackBerry 10 Quirks + + * **type**: partiellement pris en charge. Utilisé pour les numéros de téléphone. + + * **valeur**: prise en charge. + + * **pref**: ne pas pris en charge, retour`false`. + +### Notes au sujet d'iOS + + * **pref**: ne pas pris en charge, retour`false`. + +### Quirks Windows8 + + * **pref**: ne pas pris en charge, retour`false`. + +### Bizarreries de Windows + + * **pref**: ne pas pris en charge, retour`false`. + +## ContactName + +Contient différents types d'informations sur un `Contact` nom de l'objet. + +### Propriétés + + * **mise en forme**: le nom complet du contact. *(DOMString)* + + * **familyName**: nom de famille du contact. *(DOMString)* + + * **givenName**: prénom du contact. *(DOMString)* + + * **middleName**: deuxième prénom du contact. *(DOMString)* + + * **honorificPrefix**: préfixe du contact (exemple *M.* ou *Mme*) *(DOMString)* + + * **honorificSuffix**: suffixe du contact (exemple *Esq.*). *(DOMString)* + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Exemple + + function onSuccess(contacts) {pour (var j'ai = 0; j'ai < contacts.length; i ++) {alert ("Formatted:" + contacts[i].name.formatted + « \n » + "patronyme:" + contacts[i].name.familyName + « \n » + "Prénom:" + contacts[i].name.givenName + « \n » + "Prénom:" + contacts[i].name.middleName + « \n » + "suffixe:" + contacts[i].name.honorificSuffix + « \n » + "préfixe:" + contacts[i].name.honorificSuffix) ; + } + }; + + function onError(contactError) {alert('onError!');} ; + + options de var = new ContactFindOptions() ; + options.Filter = "" ; + filtre = ["name", « nom »] ; + Navigator.contacts.Find (filtre, onSuccess, onError, options) ; + + +### Quirks Android + + * **au format**: partiellement pris en charge et en lecture seule. Retourne la concaténation de `honorificPrefix` , `givenName` , `middleName` , `familyName` , et`honorificSuffix`. + +### BlackBerry 10 Quirks + + * **au format**: partiellement pris en charge. Retourne la concaténation de champs **firstName** et **lastName** de BlackBerry. + + * **familyName**: prise en charge. Stockée dans le champ **lastName** BlackBerry. + + * **givenName**: prise en charge. Stockée dans le champ **firstName** BlackBerry. + + * **middleName**: ne pas pris en charge, retour`null`. + + * **honorificPrefix**: ne pas pris en charge, retour`null`. + + * **honorificSuffix**: ne pas pris en charge, retour`null`. + +### Bizarreries de FirefoxOS + + * **au format**: partiellement pris en charge et en lecture seule. Retourne la concaténation de `honorificPrefix` , `givenName` , `middleName` , `familyName` , et`honorificSuffix`. + +### Notes au sujet d'iOS + + * **au format**: partiellement pris en charge. Retourne la dénomination composée d'iOS, mais est en lecture seule. + +### Bizarreries de Windows 8 + + * **au format**: c'est le seul nom de propriété et est identique à `displayName` , et`nickname` + + * **familyName**: non pris en charge + + * **givenName**: non pris en charge + + * **middleName**: non pris en charge + + * **honorificPrefix**: non pris en charge + + * **honorificSuffix**: non pris en charge + +### Bizarreries de Windows + + * **mise en forme**: il est identique à`displayName` + +## ContactOrganization + +Le `ContactOrganization` objet Stocke des propriétés un contact de l'organisation. A `Contact` objet contient un ou plusieurs `ContactOrganization` des objets dans un tableau. + +### Propriétés + + * **pref**: la valeur `true` si ce `ContactOrganization` contient la valeur de préférence de l'utilisateur. *(booléen)* + + * **type**: une chaîne qui indique quel type de terrain c'est le cas, *maison* par exemple. _(DOMString) + + * **nom**: le nom de l'organisation. *(DOMString)* + + * **Département**: le département et le contrat de travaille pour. *(DOMString)* + + * **titre**: titre du contact auprès de l'organisation. *(DOMString)* + +### Plates-formes supportées + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows (Windows 8.1 et Windows Phone 8.1 dispositifs seulement) + +### Exemple + + function onSuccess(contacts) {pour (var j'ai = 0; j'ai < contacts.length; i ++) {pour (var j = 0; j < contacts[i].organizations.length; j ++) {alert ("Pref:" + contacts[i].organizations[j].pref + « \n » + "Type:" + contacts[i].organizations[j].type + « \n » + "nom:" + contacts[i].organizations[j].name + « \n » + "Département:" + contacts[i].organizations[j].department + « \n » + "Title: "+ contacts[i].organizations[j].title) ; + } + } + }; + + function onError(contactError) {alert('onError!');} ; + + options de var = new ContactFindOptions() ; + options.Filter = "" ; + filtre = ["displayName", « organisations »] ; + Navigator.contacts.Find (filtre, onSuccess, onError, options) ; + + +### Android 2.X Quirks + + * **pref**: ne pas pris en charge par des dispositifs Android 2.X, retour`false`. + +### BlackBerry 10 Quirks + + * **pref**: ne pas pris en charge par les appareils BlackBerry, retour`false`. + + * **type**: ne pas pris en charge par les appareils BlackBerry, retour`null`. + + * **nom**: partiellement pris en charge. Le premier nom de l'organisme est stocké dans le champ **company** de BlackBerry. + + * **Département**: ne pas pris en charge, retour`null`. + + * **titre**: partiellement pris en charge. Le premier titre de l'organisation est stocké dans le champ de **jobTitle** BlackBerry. + +### Firefox OS Quirks + + * **pref**: non pris en charge + + * **type**: non pris en charge + + * **Département**: non pris en charge + + * Les champs **nom** et **titre** stocké dans **org** et **jobTitle**. + +### Notes au sujet d'iOS + + * **pref**: non pris en charge sur les appareils iOS, retour`false`. + + * **type**: non pris en charge sur les appareils iOS, retour`null`. + + * **nom**: partiellement pris en charge. Le premier nom de l'organisme est stocké dans le champ de **kABPersonOrganizationProperty** iOS. + + * **Département**: partiellement pris en charge. Le premier nom de département est stocké dans le champ de **kABPersonDepartmentProperty** iOS. + + * **titre**: partiellement pris en charge. Le premier titre est stocké dans le champ de **kABPersonJobTitleProperty** iOS. + +### Bizarreries de Windows + + * **pref**: ne pas pris en charge, retour`false`. + + * **type**: ne pas pris en charge, retour`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/fr/index.md b/plugins/cordova-plugin-contacts/doc/fr/index.md new file mode 100644 index 0000000..ba2fb47 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/fr/index.md @@ -0,0 +1,652 @@ + + +# cordova-plugin-contacts + +Ce plugin définit un global `navigator.contacts` objet, ce qui permet d'accéder à la base de données de contacts de dispositif. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.contacts);} + + +**Avertissement**: collecte et utilisation des données de contact soulève des questions importantes de la vie privée. Politique de confidentialité de votre application doit examiner comment l'application utilise les données de contact et si il est partagé avec d'autres parties. Information de contact est considéré comme sensible parce qu'il révèle les gens avec lesquels une personne communique. Par conséquent, en plus de la politique de confidentialité de l'application, vous devez envisager fortement fournissant un avis juste-à-temps, avant que l'application accède ou utilise des données de contact, si le système d'exploitation de périphérique ne fait donc pas déjà. Cet avis doit fournir les mêmes renseignements susmentionnées, ainsi que d'obtenir l'autorisation de l'utilisateur (par exemple, en présentant des choix **OK** et **Non merci**). Notez que certains marchés app peuvent exiger l'application de fournir un avis juste-à-temps et obtenir la permission de l'utilisateur avant d'accéder à des données de contact. Une expérience utilisateur claire et facile à comprendre qui entourent l'utilisation de données permettent d'éviter la confusion des utilisateurs de contact et une utilisation jugée abusive des données de contact. Pour plus d'informations, consultez le Guide de la vie privée. + +## Installation + + Cordova plugin ajouter cordova-plugin-contacts + + +### Firefox OS Quirks + +Créez **www/manifest.webapp** comme décrit dans [Les Docs manifeste][1]. Ajouter permisions pertinentes. Il est également nécessaire de changer le type d'application Web de « privilégiés » - [Docs manifeste][2]. **Avertissement**: toutes les applications privilégiées appliquer [Contenu politique de sécurité][3] qui interdit à un script inline. Initialiser votre application d'une autre manière. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + « type »: "le privilège", "autorisations": {« contacts »: {« accès »: "readwrite", "description": "décrire pourquoi il est nécessaire pour obtenir cette permission"}} + + +### Bizarreries de Windows + +Contacts éventuellement retournés par `find` et `pickContact` méthodes sont en lecture seule, afin que votre application ne puisse les modifier. `find`méthode disponible uniquement sur les appareils Windows Phone 8.1. + +### Bizarreries de Windows 8 + +Windows 8 Contacts sont en lecture seule. Via les Contacts d'API Cordova ne sont pas queryable/consultables, vous devez en informer l'utilisateur de choisir un contact comme un appel à contacts.pickContact qui va ouvrir l'application « People » où l'utilisateur doit choisir un contact. Les contacts retournés sont en lecture seule, afin que votre application ne puisse les modifier. + +## Navigator.contacts + +### Méthodes + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### Objets + +* Contact +* ContactName +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## Navigator.contacts.Create + +La `navigator.contacts.create` méthode est synchrone et retourne un nouveau `Contact` objet. + +Cette méthode ne conserve pas l'objet de Contact dans la base de données des contacts périphériques, dont vous avez besoin d'appeler le `Contact.save` méthode. + +### Plates-formes prises en charge + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 + +### Exemple + + myContact var = navigator.contacts.create ({« displayName »: « Test User »}) ; + + +## navigator.contacts.find + +La `navigator.contacts.find` méthode s'exécute de façon asynchrone, l'interrogation de la base de données de contacts de dispositif et retourne un tableau de `Contact` objets. Les objets résultants sont passés à la `contactSuccess` la fonction de rappel spécifiée par le paramètre **contactSuccess** . + +Le paramètre **contactFields** spécifie les champs à utiliser comme un qualificateur de recherche. Un paramètre de longueur nulle **contactFields** n'est pas valide et se traduit par `ContactError.INVALID_ARGUMENT_ERROR` . Une valeur de **contactFields** de `"*"` recherche dans les champs de tout contact. + +La chaîne **contactFindOptions.filter** peut servir comme un filtre de recherche lors de l'interrogation de la base de données de contacts. Si fourni, un non-respect de la casse, correspondance de valeur partielle est appliquée à chaque champ spécifié dans le paramètre **contactFields** . S'il y a une correspondance pour *n'importe quel* des champs spécifiés, le contact est retourné. Utilisation **contactFindOptions.desiredFields** paramètre de contrôle qui contacter propriétés doit être retourné au retour. + +### Paramètres + +* **contactFields**: communiquer avec les champs à utiliser comme un qualificateur de recherche. *(DOMString[])* [Required] + +* **contactSuccess**: fonction de rappel de succès avec le tableau d'objets Contact appelée retournée par la base de données. [Required] + +* **contactError**: fonction de rappel d'erreur, appelée lorsqu'une erreur se produit. [Optional] + +* **contactFindOptions**: recherche d'options pour filtrer navigator.contacts. [Optional] + + Clés incluent : + + * **filtre**: la chaîne de recherche utilisée pour trouver navigator.contacts. *(DOMString)* (Par défaut :`""`) + + * **multiples**: détermine si l'opération find retourne plusieurs navigator.contacts. *(Booléen)* (Par défaut :`false`) + + * **desiredFields**: Contactez champs soit retourné en arrière. Si spécifié, l'entraînant `Contact` objet dispose seulement des valeurs de ces champs. *(DOMString[])* [Optional] + +### Plates-formes prises en charge + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows (Windows Phone 8.1 dispositifs seulement) + +### Exemple + + function onSuccess(contacts) {alert (« Found » + contacts.length + « contacts. »);} ; + + function onError(contactError) {alert('onError!');} ; + + trouver tous les contacts avec « Bob » dans toute option de var de champ nom = new ContactFindOptions() ; + options.Filter = « Bob » ; + options.multiple = true ; + options.desiredFields = [navigator.contacts.fieldType.id] ; + champs var = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name] ; + Navigator.contacts.Find (champs, onSuccess, onError, options) ; + + +### Bizarreries de Windows + +* `__contactFields__`n'est pas prise en charge et sera ignorée. `find`méthode toujours tenter de faire correspondre le nom, adresse e-mail ou numéro de téléphone d'un contact. + +## navigator.contacts.pickContact + +La `navigator.contacts.pickContact` méthode lance le sélecteur de Contact pour sélectionner un contact unique. L'objet qui en résulte est passé à la `contactSuccess` la fonction de rappel spécifiée par le paramètre **contactSuccess** . + +### Paramètres + +* **contactSuccess**: fonction de rappel de succès appelée avec l'objet de Contact unique. [Obligatoire] + +* **contactError**: fonction de rappel d'erreur, appelée lorsqu'une erreur se produit. [Facultatif] + +### Plates-formes prises en charge + +* Android +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### Exemple + + navigator.contacts.pickContact(function(contact) {console.log ("le contact suivant a été retenu:" + JSON.stringify(contact)) ; + }, function(err) {console.log ("Error:" + err) ; + }); + + +## Contact + +Le `Contact` objet représente le contact de l'utilisateur. Contacts peuvent être créés, conservés ou supprimés de la base de données de contacts de dispositif. Contacts peuvent également être récupérées (individuellement ou en vrac) de la base de données en appelant le `navigator.contacts.find` méthode. + +**NOTE**: tous les champs de contact susmentionnés ne sont pris en charge sur chaque plate-forme de périphérique. S'il vous plaît vérifier la section *bizarreries* de chaque plate-forme pour plus de détails. + +### Propriétés + +* **ID**: un identificateur global unique. *(DOMString)* + +* **displayName**: le nom de ce Contact, approprié pour l'affichage à l'utilisateur final. *(DOMString)* + +* **nom**: un objet contenant tous les composants d'un nom de personnes. *(ContactName)* + +* **Pseudo**: un nom occasionnel permettant de régler le contact. *(DOMString)* + +* **phoneNumbers**: un tableau des numéros de téléphone du contact. *(ContactField[])* + +* **courriels**: un tableau d'adresses de courriel du contact. *(ContactField[])* + +* **adresses**: un tableau d'adresses tous les contacts. *(ContactAddress[])* + +* **IMS**: un tableau d'adresses IM tout le contact. *(ContactField[])* + +* **organisations**: un tableau des organisations de tout le contact. *(ContactOrganization[])* + +* **anniversaire**: l'anniversaire du contact. *(Date)* + +* **Remarque**: une remarque sur le contact. *(DOMString)* + +* **photos**: un tableau de photos du contact. *(ContactField[])* + +* **catégories**: un tableau de toutes les catégories définies par l'utilisateur attribuée au contact. *(ContactField[])* + +* **URL**: un tableau des pages web attribuée au contact. *(ContactField[])* + +### Méthodes + +* **Clone**: retourne un nouveau `Contact` objet qui est une copie complète de l'objet appelant, avec le `id` propriété la valeur`null`. + +* **supprimer**: supprime le contact de la base de données de contacts de dispositif, sinon exécute un rappel d'erreur avec un `ContactError` objet. + +* **Enregistrer**: enregistre un nouveau contact dans la base de données de contacts de périphérique, ou met à jour un contact existant, si un contact avec le même **id** existe déjà. + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Enregistrez l'exemple + + function onSuccess(contact) {alert ("sauver succès");} ; + + function onError(contactError) {alert ("erreur =" + contactError.code);} ; + + créer un objet contact contact var = navigator.contacts.create() ; + contact.displayName = « Plombier » ; + contact.Nickname = « Plombier » ; spécifier à la fois pour prendre en charge tous les périphériques / / renseigner certains champs var nom = new ContactName() ; + name.givenName = « Jane » ; + name.familyName = « Doe » ; + contact.name = nom ; + + enregistrer dans contact.save(onSuccess,onError) de l'appareil ; + + +### Exemple de clone + + Clone clone objet contact var = contact.clone() ; + clone.name.givenName = « John » ; + Console.log ("contact Original nom =" + contact.name.givenName) ; + Console.log ("nom de contact clonés =" + clone.name.givenName) ; + + +### Supprimer l'exemple + + fonction onSuccess() {alert ("succès");} ; + + function onError(contactError) {alert ("erreur =" + contactError.code);} ; + + supprimer le contact de l'appareil contact.remove(onSuccess,onError) ; + + +### Android 2.X Quirks + +* **catégories**: non pris en charge sur les périphériques Android 2.X, retour`null`. + +### BlackBerry 10 Quirks + +* **ID**: assignés par l'appareil lors de l'enregistrement du contact. + +### Bizarreries de FirefoxOS + +* **catégories**: partiellement pris en charge. Champs **pref** et **type** sont de retour`null` + +* **IMS**: non pris en charge + +* **photos**: ne pas pris en charge + +### iOS Quirks + +* **displayName**: ne pas possible sur iOS, retour `null` à moins qu'il n'y a aucun `ContactName` spécifié, auquel cas, il renvoie le nom composite, **Pseudo** ou `""` , respectivement. + +* **anniversaire**: doit être entré comme un JavaScript `Date` objet, de la même façon qu'il soit retourné. + +* **photos**: retourne une URL de fichier de l'image, qui est stocké dans le répertoire temporaire de l'application. Contenu du répertoire temporaire est supprimés lorsque l'application se ferme. + +* **catégories**: cette propriété n'est actuellement pas supportée, retour`null`. + +### Windows Phone 7 et 8 Quirks + +* **displayName**: lorsque vous créez un contact, la valeur fournie pour le paramètre de nom d'affichage est différent de l'affichage nom Récupérée lors de la recherche du contact. + +* **URL**: lorsque vous créez un contact, les utilisateurs peuvent entrer et enregistrer plus d'une adresse web, mais seulement un est disponible lors de la recherche du contact. + +* **phoneNumbers**: l'option de *pref* n'est pas pris en charge. Le *type* n'est pas pris en charge lors d'une opération de *trouver* . Seul `phoneNumber` est autorisé pour chaque *type*. + +* **courriels**: l'option de *pref* n'est pas pris en charge. Accueil et personnels références même courriel entrée. Une seule participation est autorisée pour chaque *type*. + +* **adresses**: prend en charge seulement travail et accueil/personal *type*. La maison et personnels de *type* référence la même entrée d'adresse. Une seule participation est autorisée pour chaque *type*. + +* **organisations**: seul est autorisé et ne supporte pas les attributs *pref*, *type*et *Département* . + +* **Remarque**: ne pas pris en charge, retour`null`. + +* **IMS**: ne pas pris en charge, retour`null`. + +* **anniversaires**: ne pas pris en charge, retour`null`. + +* **catégories**: ne pas pris en charge, retour`null`. + +### Bizarreries de Windows + +* **photos**: retourne une URL de fichier de l'image, qui est stocké dans le répertoire temporaire de l'application. + +* **anniversaires**: ne pas pris en charge, retour`null`. + +* **catégories**: ne pas pris en charge, retour`null`. + +## ContactAddress + +Le `ContactAddress` objet Stocke les propriétés d'une seule adresse d'un contact. A `Contact` objet peut inclure plusieurs adresses dans un `ContactAddress[]` tableau. + +### Propriétés + +* **pref**: la valeur `true` si ce `ContactAddress` contient la valeur de préférence de l'utilisateur. *(booléen)* + +* **type**: une chaîne qui indique quel type de terrain c'est le cas, *maison* par exemple. *(DOMString)* + +* **mise en forme**: l'adresse complète au format pour l'affichage. *(DOMString)* + +* **adresse**: l'adresse complète. *(DOMString)* + +* **localité**: la ville ou la localité. *(DOMString)* + +* **région**: l'État ou la région. *(DOMString)* + +* **Code postal**: le code zip ou code postal. *(DOMString)* + +* **pays**: le nom du pays. *(DOMString)* + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Exemple + + Affichez les informations d'adresse pour tous les contacts fonctionnent onSuccess(contacts) {pour (var j'ai = 0; j'ai < contacts.length; i ++) {pour (var j = 0; j < contacts[i].addresses.length; j ++) {alert ("Pref:" + contacts[i].addresses[j].pref + « \n » + "Type:" + contacts[i].addresses[j].type + « \n » + "au format:" + contacts[i].addresses[j].formatted + « \n » + "adresse de rue: "+ contacts[i].addresses[j].streetAddress +"\n"+" localité: "+ contacts[i].addresses[j].locality +"\n"+" région: "+ contacts[i].addresses[j].region +"\n"+" Code Postal: "+ contacts[i].addresses[j].postalCode +"\n"+" pays: "+ contacts[i].addresses[j].country) ; + } + } + }; + + function onError(contactError) {alert('onError!');} ; + + trouver tous les contacts options var = new ContactFindOptions() ; + options.Filter = "" ; + filtre var = ["name", « adresses »] ; + Navigator.contacts.Find (filtre, onSuccess, onError, options) ; + + +### Android 2.X Quirks + +* **pref**: ne pas pris en charge, retour `false` sur les appareils Android 2.X. + +### BlackBerry 10 Quirks + +* **pref**: non pris en charge sur les appareils BlackBerry, retour`false`. + +* **type**: partiellement pris en charge. Seule chaque de *travail* et tapez les adresses de *la maison* peut être stockée par contact. + +* **au format**: partiellement pris en charge. Retourne la concaténation de tous les champs d'adresse BlackBerry. + +* **streetAddress**: prise en charge. Retourne la concaténation de BlackBerry **address1** et **address2** champs d'adresse. + +* **localité**: prise en charge. Stockée dans le champ d'adresse BlackBerry **ville** . + +* **région**: pris en charge. Stockée dans le champ d'adresse BlackBerry **stateProvince** . + +* **Code postal**: prise en charge. Stockée dans le champ d'adresse BlackBerry **zipPostal** . + +* **pays**: prise en charge. + +### Bizarreries de FirefoxOS + +* **au format**: actuellement ne pas pris en charge + +### iOS Quirks + +* **pref**: non pris en charge sur les appareils iOS, retour`false`. + +* **au format**: actuellement ne pas pris en charge. + +### Bizarreries de Windows 8 + +* **pref**: non pris en charge + +### Bizarreries de Windows + +* **pref**: non pris en charge + +## ContactError + +Le `ContactError` objet est retourné à l'utilisateur via le `contactError` fonction de rappel lorsqu'une erreur survient. + +### Propriétés + +* **code**: l'un des codes d'erreur prédéfinis énumérés ci-dessous. + +### Constantes + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Le `ContactField` objet est un composant réutilisable que représente contacter champs génériquement. Chaque `ContactField` objet contient un `value` , `type` , et `pref` propriété. A `Contact` objet stocke plusieurs propriétés dans `ContactField[]` tableaux, tels que téléphone numéros et adresses e-mail. + +Dans la plupart des cas, il n'y a pas de valeurs prédéterminées pour une `ContactField` l'attribut **type** de l'objet. Par exemple, un numéro de téléphone peut spécifier des valeurs de **type** de la *maison*, *travail*, *mobile*, *iPhone*ou toute autre valeur qui est pris en charge par la base de contacts de la plate-forme un périphérique particulier. Toutefois, pour les `Contact` **photos** champ, le champ **type** indique le format de l'image retournée : **url** lorsque l'attribut **value** contient une URL vers l'image photo ou *base64* lorsque la **valeur** contient une chaîne codée en base64 image. + +### Propriétés + +* **type**: une chaîne qui indique quel type de terrain c'est le cas, *maison* par exemple. *(DOMString)* + +* **valeur**: la valeur du champ, comme un téléphone numéro ou adresse e-mail. *(DOMString)* + +* **pref**: la valeur `true` si ce `ContactField` contient la valeur de préférence de l'utilisateur. *(booléen)* + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Exemple + + créer un nouveau contact contact var = navigator.contacts.create() ; + + stocker des numéros de téléphone de contact en ContactField [] var phoneNumbers = [] ; + phoneNumbers[0] = new ContactField (« travail », ' 212-555-1234', false) ; + phoneNumbers[1] = new ContactField (« mobile », ' 917-555-5432', true) ; phoneNumbers[2] numéro préféré = new ContactField (« home », ' 203-555-7890', false) ; + contact.phoneNumbers = phoneNumbers ; + + enregistrer le contact contact.save() ; + + +### Quirks Android + +* **pref**: ne pas pris en charge, retour`false`. + +### BlackBerry 10 Quirks + +* **type**: partiellement pris en charge. Utilisé pour les numéros de téléphone. + +* **valeur**: prise en charge. + +* **pref**: ne pas pris en charge, retour`false`. + +### iOS Quirks + +* **pref**: ne pas pris en charge, retour`false`. + +### Quirks Windows8 + +* **pref**: ne pas pris en charge, retour`false`. + +### Bizarreries de Windows + +* **pref**: ne pas pris en charge, retour`false`. + +## ContactName + +Contient différents types d'informations sur un `Contact` nom de l'objet. + +### Propriétés + +* **mise en forme**: le nom complet du contact. *(DOMString)* + +* **familyName**: nom de famille du contact. *(DOMString)* + +* **givenName**: prénom du contact. *(DOMString)* + +* **middleName**: deuxième prénom du contact. *(DOMString)* + +* **honorificPrefix**: préfixe du contact (exemple *M.* ou *Mme*) *(DOMString)* + +* **honorificSuffix**: suffixe du contact (exemple *Esq.*). *(DOMString)* + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Exemple + + function onSuccess(contacts) {pour (var j'ai = 0; j'ai < contacts.length; i ++) {alert ("Formatted:" + contacts[i].name.formatted + « \n » + "patronyme:" + contacts[i].name.familyName + « \n » + "Prénom:" + contacts[i].name.givenName + « \n » + "Prénom:" + contacts[i].name.middleName + « \n » + "suffixe:" + contacts[i].name.honorificSuffix + « \n » + "préfixe:" + contacts[i].name.honorificSuffix) ; + } + }; + + function onError(contactError) {alert('onError!');} ; + + options de var = new ContactFindOptions() ; + options.Filter = "" ; + filtre = ["name", « nom »] ; + Navigator.contacts.Find (filtre, onSuccess, onError, options) ; + + +### Quirks Android + +* **au format**: partiellement pris en charge et en lecture seule. Retourne la concaténation de `honorificPrefix` , `givenName` , `middleName` , `familyName` , et`honorificSuffix`. + +### BlackBerry 10 Quirks + +* **au format**: partiellement pris en charge. Retourne la concaténation de champs **firstName** et **lastName** de BlackBerry. + +* **familyName**: prise en charge. Stockée dans le champ **lastName** BlackBerry. + +* **givenName**: prise en charge. Stockée dans le champ **firstName** BlackBerry. + +* **middleName**: ne pas pris en charge, retour`null`. + +* **honorificPrefix**: ne pas pris en charge, retour`null`. + +* **honorificSuffix**: ne pas pris en charge, retour`null`. + +### Bizarreries de FirefoxOS + +* **au format**: partiellement pris en charge et en lecture seule. Retourne la concaténation de `honorificPrefix` , `givenName` , `middleName` , `familyName` , et`honorificSuffix`. + +### iOS Quirks + +* **au format**: partiellement pris en charge. Retourne la dénomination composée d'iOS, mais est en lecture seule. + +### Bizarreries de Windows 8 + +* **au format**: c'est le seul nom de propriété et est identique à `displayName` , et`nickname` + +* **familyName**: non pris en charge + +* **givenName**: non pris en charge + +* **middleName**: non pris en charge + +* **honorificPrefix**: non pris en charge + +* **honorificSuffix**: non pris en charge + +### Bizarreries de Windows + +* **mise en forme**: il est identique à`displayName` + +## ContactOrganization + +Le `ContactOrganization` objet Stocke des propriétés un contact de l'organisation. A `Contact` objet contient un ou plusieurs `ContactOrganization` des objets dans un tableau. + +### Propriétés + +* **pref**: la valeur `true` si ce `ContactOrganization` contient la valeur de préférence de l'utilisateur. *(booléen)* + +* **type**: une chaîne qui indique quel type de terrain c'est le cas, *maison* par exemple. _(DOMString) + +* **nom**: le nom de l'organisation. *(DOMString)* + +* **Département**: le département et le contrat de travaille pour. *(DOMString)* + +* **titre**: titre du contact auprès de l'organisation. *(DOMString)* + +### Plates-formes prises en charge + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows (Windows 8.1 et Windows Phone 8.1 dispositifs seulement) + +### Exemple + + function onSuccess(contacts) {pour (var j'ai = 0; j'ai < contacts.length; i ++) {pour (var j = 0; j < contacts[i].organizations.length; j ++) {alert ("Pref:" + contacts[i].organizations[j].pref + « \n » + "Type:" + contacts[i].organizations[j].type + « \n » + "nom:" + contacts[i].organizations[j].name + « \n » + "Département:" + contacts[i].organizations[j].department + « \n » + "Title: "+ contacts[i].organizations[j].title) ; + } + } + }; + + function onError(contactError) {alert('onError!');} ; + + options de var = new ContactFindOptions() ; + options.Filter = "" ; + filtre = ["displayName", « organisations »] ; + Navigator.contacts.Find (filtre, onSuccess, onError, options) ; + + +### Android 2.X Quirks + +* **pref**: ne pas pris en charge par des dispositifs Android 2.X, retour`false`. + +### BlackBerry 10 Quirks + +* **pref**: ne pas pris en charge par les appareils BlackBerry, retour`false`. + +* **type**: ne pas pris en charge par les appareils BlackBerry, retour`null`. + +* **nom**: partiellement pris en charge. Le premier nom de l'organisme est stocké dans le champ **company** de BlackBerry. + +* **Département**: ne pas pris en charge, retour`null`. + +* **titre**: partiellement pris en charge. Le premier titre de l'organisation est stocké dans le champ de **jobTitle** BlackBerry. + +### Firefox OS Quirks + +* **pref**: non pris en charge + +* **type**: non pris en charge + +* **Département**: non pris en charge + +* Les champs **nom** et **titre** stocké dans **org** et **jobTitle**. + +### iOS Quirks + +* **pref**: non pris en charge sur les appareils iOS, retour`false`. + +* **type**: non pris en charge sur les appareils iOS, retour`null`. + +* **nom**: partiellement pris en charge. Le premier nom de l'organisme est stocké dans le champ de **kABPersonOrganizationProperty** iOS. + +* **Département**: partiellement pris en charge. Le premier nom de département est stocké dans le champ de **kABPersonDepartmentProperty** iOS. + +* **titre**: partiellement pris en charge. Le premier titre est stocké dans le champ de **kABPersonJobTitleProperty** iOS. + +### Bizarreries de Windows + +* **pref**: ne pas pris en charge, retour`false`. + +* **type**: ne pas pris en charge, retour`null`. diff --git a/plugins/cordova-plugin-contacts/doc/it/README.md b/plugins/cordova-plugin-contacts/doc/it/README.md new file mode 100644 index 0000000..53197b4 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/it/README.md @@ -0,0 +1,735 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +Questo plugin definisce un oggetto globale `navigator.contacts`, che fornisce l'accesso al database di contatti del dispositivo. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**Avviso**: raccolta e utilizzo dei dati di contatto solleva questioni di privacy importante. Politica sulla privacy dell'app dovrebbe discutere come app utilizza i dati di contatto e se è condiviso con altre parti. Informazioni di contatto sono considerate sensibile perché rivela le persone con cui una persona comunica. Pertanto, oltre alla politica di privacy dell'app, è fortemente consigliabile fornendo un preavviso di just-in-time prima app accede o utilizza i dati di contatto, se il sistema operativo del dispositivo non farlo già. Tale comunicazione deve fornire le informazioni stesse notate sopra, oltre ad ottenere l'autorizzazione (ad esempio, presentando scelte per **OK** e **No grazie**). Si noti che alcuni mercati app possono richiedere l'app per fornire un preavviso di just-in-time e ottenere l'autorizzazione dell'utente prima di accedere ai dati di contatto. Un'esperienza utente chiara e facile--capisce che circonda l'uso del contatto dati aiuta a evitare la confusione dell'utente e percepito un uso improprio dei dati di contatto. Per ulteriori informazioni, vedere la guida sulla Privacy. + +## Installazione + +Ciò richiede cordova 5.0 + (v 1.0.0 stabile corrente) + + cordova plugin add cordova-plugin-contacts + + +Versioni precedenti di cordova comunque possono installare tramite l'id **deprecato** (stantio v0.2.16) + + cordova plugin add org.apache.cordova.contacts + + +È anche possibile installare direttamente tramite url di repo (instabile) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS stranezze + +Creare **www/manifest.webapp** come descritto nel [Manifest Docs](https://developer.mozilla.org/en-US/Apps/Developing/Manifest). Aggiungi permisions rilevanti. C'è anche la necessità di modificare il tipo di webapp in "privilegiato" - [Manifest Docs](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type). **AVVERTENZA**: tutte le apps privilegiato applicare [Content Security Policy](https://developer.mozilla.org/en-US/Apps/CSP) che vieta script inline. Inizializzare l'applicazione in un altro modo. + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Stranezze di Windows + +**Prima del Windows 10:** Eventuali contatti restituiti dai metodi `trovare` e `pickContact` sono readonly, quindi l'applicazione non può modificarli. Metodo `find` disponibile solo sui dispositivi Windows Phone 8.1. + +**Windows 10 e sopra:** Contatti possono essere salvati e deposito di app locale contatti verranno salvate. Contatti possono anche essere eliminati. + +### Stranezze di Windows 8 + +Windows 8 contatti sono readonly. Tramite i contatti di Cordova API non sono queryable/ricerche, si dovrebbe informare l'utente di scegliere un contatto come una chiamata a contacts.pickContact che aprirà l'app 'Persone' dove l'utente deve scegliere un contatto. Eventuali contatti restituiti sono readonly, quindi l'applicazione non può modificarli. + +## Navigator.contacts + +### Metodi + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### Oggetti + + * Contact + * ContactName + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +Il metodo `navigator.contacts.create` è sincrono e restituisce un nuovo oggetto di `Contact`. + +Questo metodo non mantiene l'oggetto contatto nel database contatti dispositivo, per cui è necessario richiamare il metodo `Contact.save`. + +### Piattaforme supportate + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + +### Esempio + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Il metodo `navigator.contacts.find` in modo asincrono, esegue una query sul database di contatti del dispositivo e restituisce una matrice di oggetti `Contact`. Gli oggetti risultanti vengono passati alla funzione di callback `contactSuccess` specificata dal parametro **contactSuccess**. + +Il parametro **contactFields** specifica i campi per essere utilizzato come un qualificatore di ricerca. Un parametro di lunghezza zero, **contactFields** non è valido e si traduce in `ContactError.INVALID_ARGUMENT_ERROR`. Un valore di **contactFields** di `"*"` ricerche campi tutti i contatti. + +La stringa di **contactFindOptions.filter** può essere utilizzata come un filtro di ricerca quando una query sul database di contatti. Se fornito, una distinzione, corrispondenza parziale valore viene applicato a ogni campo specificato nel parametro **contactFields**. Se esiste una corrispondenza per *qualsiasi* dei campi specificati, viene restituito il contatto. Uso **contactFindOptions.desiredFields** parametro di controllo quale contattare la proprietà deve essere rispedito indietro. + +### Parametri + + * **contactFields**: contattare campi da utilizzare come un qualificatore di ricerca. *(DOMString[])* [Required] + + * **contactSuccess**: funzione di callback successo richiamato con la matrice di oggetti contatto restituiti dal database. [Required] + + * **contactError**: funzione di callback di errore, viene richiamato quando si verifica un errore. [Facoltativo] + + * **contactFindOptions**: opzioni per filtrare navigator.contacts di ricerca. [Optional] + + I tasti sono: + + * **filter**: la stringa di ricerca utilizzata per trovare navigator.contacts. *(DOMString)* (Default: `""`) + + * **multiple**: determina se l'operazione di ricerca restituisce più navigator.contacts. *(Boolean)* (Default: `false`) + + * **desiredFields**: contattare i campi per essere tornato indietro. Se specificato, il risultante `contatto` oggetto solo caratteristiche valori per questi campi. *(DOMString[])* [Optional] + +### Piattaforme supportate + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows (Windows Phone 8.1 e Windows 10) + +### Esempio + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Stranezze di Windows + + * `__contactFields__`non è supportato, verrà ignorato. `find`metodo cercherà sempre di abbinare il nome, indirizzo email o numero di telefono di un contatto. + +## navigator.contacts.pickContact + +Il metodo `navigator.contacts.pickContact` lancia il contatto selettore per selezionare un singolo contatto. L'oggetto risultante viene passato alla funzione di callback `contactSuccess` specificata dal parametro **contactSuccess**. + +### Parametri + + * **contactSuccess**: funzione di callback di successo viene richiamato con il singolo oggetto di contatto. [Richiesto] + + * **contactError**: funzione di callback di errore, viene richiamato quando si verifica un errore. [Facoltativo] + +### Piattaforme supportate + + * Android + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### Esempio + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Contact + +L'oggetto `Contact` rappresenta il contatto di un utente. Contatti possono essere creati, memorizzati o rimossi dal database di contatti dispositivo. Contatti possono anche essere estratto (singolarmente o in blocco) dal database richiamando il metodo `navigator.contacts.find`. + +**Nota**: non tutti i campi di contatto sopra elencati sono supportati su ogni piattaforma del dispositivo. Consultare la sezione di *stranezze* su ogni piattaforma per dettagli. + +### Proprietà + + * **ID**: un identificatore univoco globale. *(DOMString)* + + * **displayName**: il nome di questo contatto, adatto per la visualizzazione a utenti finali. *(DOMString)* + + * **nome**: un oggetto che contiene tutti i componenti di un nome di persone. *(ContactName)* + + * **Nickname**: un nome informale con cui affrontare il contatto. *(DOMString)* + + * **phoneNumbers**: una matrice di numeri di telefono del contatto. *(ContactField[])* + + * **email**: una matrice di indirizzi di posta elettronica del contatto. *(ContactField[])* + + * **indirizzi**: una matrice di indirizzi di contatto. *(ContactAddress[])* + + * **IMS**: una matrice di indirizzi IM tutto il contatto. *(ContactField[])* + + * **organizzazioni**: una matrice di organizzazioni di tutto il contatto. *(ContactOrganization[])* + + * **compleanno**: il compleanno del contatto. *(Data)* + + * **Nota**: una nota sul contatto. *(DOMString)* + + * **foto**: una matrice di foto del contatto. *(ContactField[])* + + * **categorie**: matrice di tutte le categorie definite dall'utente connesso con il contatto. *(ContactField[])* + + * **URL**: matrice di pagine web connesso con il contatto. *(ContactField[])* + +### Metodi + + * **clone**: restituisce una nuova `Contact` oggetto che è una copia completa dell'oggetto chiamante, con la `id` proprietà impostata`null`. + + * **rimuovere**: rimuove il contatto dal database contatti dispositivo, altrimenti esegue un callback di errore con un `ContactError` oggetto. + + * **Salva**: salva un nuovo contatto nel database di contatti del dispositivo, o aggiorna un contatto esistente se esiste già un contatto con lo stesso **id** . + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Esempio di salvare + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### Esempio di clone + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Rimuovere esempio + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Stranezze di Android 2. x + + * **categorie**: non è supportato sui dispositivi Android 2. x, restituendo`null`. + +### BlackBerry 10 capricci + + * **ID**: assegnate dal dispositivo quando si salva il contatto. + +### Capricci di FirefoxOS + + * **categorie**: parzialmente supportati. Campi **pref** e **tipo** stanno tornando`null` + + * **IMS**: non supportato + + * **foto**: non supportato + +### iOS stranezze + + * **displayName**: non supportata su iOS, tornando `null` se non c'è nessun `ContactName` specificato, nel qual caso restituisce il nome composito, **soprannome** o `""` , rispettivamente. + + * **compleanno**: deve essere inserito come un JavaScript `Date` oggetto, allo stesso modo viene restituito. + + * **foto**: restituisce un URL del File dell'immagine, che viene memorizzato nella directory temporanea dell'applicazione. Contenuto della directory temporanea vengono rimossi quando l'applicazione termina. + + * **categorie**: questa proprietà non è attualmente supportata, restituendo`null`. + +### Windows Phone 7 e 8 stranezze + + * **displayName**: quando si crea un contatto, il valore specificato per il parametro del nome di visualizzazione è diverso dal nome visualizzato Estratto quando trovare il contatto. + + * **URL**: quando si crea un contatto, gli utenti possono inserire e salvare più di un indirizzo web, ma solo uno è disponibile durante la ricerca del contatto. + + * **phoneNumbers**: non è supportata l'opzione *pref* . Il *tipo* non è supportato in un'operazione di *trovare* . Un solo `phoneNumber` è consentita per ogni *tipo*. + + * **email**: non è supportata l'opzione *pref* . Home e personal fa riferimento la stessa voce di posta elettronica. È consentito un solo ingresso per ogni *tipo*. + + * **indirizzi**: supporta solo lavoro e casa/personali *tipo*. Il riferimento principale e personale *tipo* la stessa voce di indirizzo. È consentito un solo ingresso per ogni *tipo*. + + * **organizzazioni**: solo uno è consentito e non supporta gli attributi *pref*, *tipo*e *dipartimento* . + + * **Nota**: non supportato, restituendo`null`. + + * **IMS**: non supportato, restituendo`null`. + + * **compleanni**: non supportato, restituendo`null`. + + * **categorie**: non supportato, restituendo`null`. + + * **remove**: metodo non è supportato + +### Stranezze di Windows + + * **foto**: restituisce un URL del File dell'immagine, che viene memorizzato nella directory temporanea dell'applicazione. + + * **compleanni**: non supportato, restituendo`null`. + + * **categorie**: non supportato, restituendo`null`. + + * **remove**: metodo è supportato solo in Windows 10 o superiore. + +## ContactAddress + +L'oggetto `ContactAddress` memorizza le proprietà di un singolo indirizzo di un contatto. Un oggetto `Contact` può includere più di un indirizzo in una matrice `[] ContactAddress`. + +### Proprietà + + * **pref**: impostare su `true` se questo `ContactAddress` contiene il valore dell'utente preferito. *(booleano)* + + * **tipo**: una stringa che indica il tipo di campo è, *casa* ad esempio. *(DOMString)* + + * **formattato**: indirizzo completo formattato per la visualizzazione. *(DOMString)* + + * **streetAddress**: l'indirizzo completo. *(DOMString)* + + * **località**: la città o località. *(DOMString)* + + * **regione**: lo stato o la regione. *(DOMString)* + + * **postalCode**: il codice postale o il codice postale. *(DOMString)* + + * **paese**: il nome del paese. *(DOMString)* + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Esempio + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Stranezze di Android 2. x + + * **pref**: non supportato, tornando `false` su dispositivi Android 2. x. + +### BlackBerry 10 capricci + + * **pref**: non è supportato sui dispositivi BlackBerry, restituendo`false`. + + * **tipo**: parzialmente supportati. Solo uno di *lavoro* e *casa* tipo indirizzi può essere memorizzato per ciascun contatto. + + * **formattato**: parzialmente supportati. Restituisce una concatenazione di tutti i campi Indirizzo BlackBerry. + + * **streetAddress**: supportato. Restituisce una concatenazione di BlackBerry **Indirizzo1** e **Indirizzo2** campi indirizzo. + + * **località**: supportato. Memorizzato nel campo indirizzo **città** di BlackBerry. + + * **regione**: supportato. Memorizzato nel campo indirizzo di **stateProvince** BlackBerry. + + * **postalCode**: supportato. Memorizzato nel campo dell'indirizzo **zipPostal** BlackBerry. + + * **paese**: supportato. + +### Capricci di FirefoxOS + + * **formattato**: attualmente non supportato + +### iOS stranezze + + * **pref**: non è supportato sui dispositivi iOS, restituendo`false`. + + * **formattato**: attualmente non supportati. + +### Stranezze di Windows 8 + + * **pref**: non supportato + +### Stranezze di Windows + + * **pref**: non supportato + +## ContactError + +L'oggetto `ContactError` viene restituito all'utente tramite la funzione di callback `contactError` quando si verifica un errore. + +### Proprietà + + * **codice**: uno dei codici di errore predefiniti elencati di seguito. + +### Costanti + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +L'oggetto `ContactField` è un componente riutilizzabile che rappresenta Contatta campi genericamente. Ogni oggetto `ContactField` contiene una proprietà di `value`, `type` e `pref`. Un oggetto `Contact` memorizza diverse proprietà in matrici `[] ContactField`, come numeri di telefono e indirizzi email. + +Nella maggior parte dei casi, esistono pre-determinati valori per l'attributo `type` di un oggetto **ContactField**. Ad esempio, un numero di telefono può specificare valori di **type** di *casa*, *lavoro*, *mobile*, *iPhone* o qualsiasi altro valore che è supportato dal database dei contatti su una piattaforma particolare dispositivo. Tuttavia, per il campo di **photo** del `Contacto`, il campo **type** indica il formato dell'immagine restituita: **url** quando il **value** di attributo contiene un URL per l'immagine fotografica, o *base64*, quando il **value** contiene una stringa con codifica base64 immagine. + +### Proprietà + + * **tipo**: una stringa che indica il tipo di campo è, *casa* ad esempio. *(DOMString)* + + * **valore**: il valore del campo, ad esempio un telefono numero o indirizzo e-mail. *(DOMString)* + + * **pref**: impostare su `true` se questo `ContactField` contiene il valore dell'utente preferito. *(booleano)* + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Esempio + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Stranezze Android + + * **pref**: non supportato, restituendo`false`. + +### BlackBerry 10 capricci + + * **tipo**: parzialmente supportati. Usato per i numeri di telefono. + + * **valore**: supportato. + + * **pref**: non supportato, restituendo`false`. + +### iOS stranezze + + * **pref**: non supportato, restituendo`false`. + +### Stranezze di Windows8 + + * **pref**: non supportato, restituendo`false`. + +### Stranezze di Windows + + * **pref**: non supportato, restituendo`false`. + +## ContactName + +Contiene diversi tipi di informazioni sul nome di un oggetto `Contact`. + +### Proprietà + + * **formattato**: il nome completo del contatto. *(DOMString)* + + * **familyName**: cognome del contatto. *(DOMString)* + + * **givenName**: nome del contatto. *(DOMString)* + + * **middleName**: il nome del contatto medio. *(DOMString)* + + * **honorificPrefix**: prefisso del contatto (esempio *Mr* o *Dr*) *(DOMString)* + + * **honorificSuffix**: suffisso del contatto (esempio *Esq.*). *(DOMString)* + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Esempio + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Stranezze Android + + * **formattato**: parzialmente supportati e di sola lettura. Restituisce una concatenazione di `honorificPrefix` , `givenName` , `middleName` , `familyName` , e`honorificSuffix`. + +### BlackBerry 10 capricci + + * **formattato**: parzialmente supportati. Restituisce una concatenazione di campi **firstName** e **lastName** BlackBerry. + + * **familyName**: supportato. Archiviato in campo **lastName** BlackBerry. + + * **givenName**: supportato. Archiviato in campo **firstName** BlackBerry. + + * **middleName**: non supportato, restituendo`null`. + + * **honorificPrefix**: non supportato, restituendo`null`. + + * **honorificSuffix**: non supportato, restituendo`null`. + +### Capricci di FirefoxOS + + * **formattato**: parzialmente supportati e di sola lettura. Restituisce una concatenazione di `honorificPrefix` , `givenName` , `middleName` , `familyName` , e`honorificSuffix`. + +### iOS stranezze + + * **formattato**: parzialmente supportati. Restituisce il nome composito di iOS, ma è di sola lettura. + +### Stranezze di Windows 8 + + * **formattato**: questo è l'unico nome proprietà ed è identico a `displayName` , e`nickname` + + * **familyName**: non supportato + + * **givenName**: non supportato + + * **middleName**: non supportato + + * **honorificPrefix**: non supportato + + * **honorificSuffix**: non supportato + +### Stranezze di Windows + + * **formattato**: esso è identico al`displayName` + +## ContactOrganization + +L'oggetto `ContactOrganization` memorizza la proprietà di organizzazione di un contatto. Un oggetto `Contact` memorizza uno o più oggetti `ContactOrganization` in una matrice. + +### Proprietà + + * **pref**: impostare su `true` se questo `ContactOrganization` contiene il valore dell'utente preferito. *(booleano)* + + * **tipo**: una stringa che indica il tipo di campo è, *casa* ad esempio. _(DOMString) + + * **nome**: il nome dell'organizzazione. *(DOMString)* + + * **dipartimento**: contratto lavora per il dipartimento. *(DOMString)* + + * **titolo**: titolo del contatto presso l'organizzazione. *(DOMString)* + +### Piattaforme supportate + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows (solo dispositivi Windows 8.1 e 8.1 di Windows Phone) + +### Esempio + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Stranezze di Android 2. x + + * **pref**: non supportato dai dispositivi Android 2. x, restituendo`false`. + +### BlackBerry 10 capricci + + * **pref**: non supportato dai dispositivi BlackBerry, restituendo`false`. + + * **tipo**: non supportato dai dispositivi BlackBerry, restituendo`null`. + + * **nome**: parzialmente supportati. Il primo nome dell'organizzazione è memorizzato nel campo **azienda** BlackBerry. + + * **dipartimento**: non supportato, restituendo`null`. + + * **titolo**: parzialmente supportati. Il primo titolo di organizzazione è memorizzato nel campo **jobTitle** BlackBerry. + +### Firefox OS stranezze + + * **pref**: non supportato + + * **tipo**: non supportato + + * **dipartimento**: non supportato + + * Campi **nome** e **titolo** memorizzato in **org** e **jobTitle**. + +### iOS stranezze + + * **pref**: non è supportato sui dispositivi iOS, restituendo`false`. + + * **tipo**: non è supportato sui dispositivi iOS, restituendo`null`. + + * **nome**: parzialmente supportati. Il primo nome dell'organizzazione è memorizzato nel campo **kABPersonOrganizationProperty** iOS. + + * **dipartimento**: parzialmente supportati. Il primo nome del dipartimento è memorizzato nel campo **kABPersonDepartmentProperty** iOS. + + * **titolo**: parzialmente supportati. Il primo titolo è memorizzato nel campo **kABPersonJobTitleProperty** iOS. + +### Stranezze di Windows + + * **pref**: non supportato, restituendo`false`. + + * **tipo**: non supportato, restituendo`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/it/index.md b/plugins/cordova-plugin-contacts/doc/it/index.md new file mode 100644 index 0000000..b7405de --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/it/index.md @@ -0,0 +1,719 @@ + + +# cordova-plugin-contacts + +Questo plugin definisce un oggetto globale `navigator.contacts`, che fornisce l'accesso al database di contatti del dispositivo. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**Avviso**: raccolta e utilizzo dei dati di contatto solleva questioni di privacy importante. Politica sulla privacy dell'app dovrebbe discutere come app utilizza i dati di contatto e se è condiviso con altre parti. Informazioni di contatto sono considerate sensibile perché rivela le persone con cui una persona comunica. Pertanto, oltre alla politica di privacy dell'app, è fortemente consigliabile fornendo un preavviso di just-in-time prima app accede o utilizza i dati di contatto, se il sistema operativo del dispositivo non farlo già. Tale comunicazione deve fornire le informazioni stesse notate sopra, oltre ad ottenere l'autorizzazione (ad esempio, presentando scelte per **OK** e **No grazie**). Si noti che alcuni mercati app possono richiedere l'app per fornire un preavviso di just-in-time e ottenere l'autorizzazione dell'utente prima di accedere ai dati di contatto. Un'esperienza utente chiara e facile--capisce che circonda l'uso del contatto dati aiuta a evitare la confusione dell'utente e percepito un uso improprio dei dati di contatto. Per ulteriori informazioni, vedere la guida sulla Privacy. + +## Installazione + + cordova plugin add cordova-plugin-contacts + + +### Firefox OS stranezze + +Creare **www/manifest.webapp** come descritto nel [Manifest Docs][1]. Aggiungi permisions rilevanti. C'è anche la necessità di modificare il tipo di webapp in "privilegiato" - [Manifest Docs][2]. **AVVERTENZA**: tutte le apps privilegiato applicare [Content Security Policy][3] che vieta script inline. Inizializzare l'applicazione in un altro modo. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Stranezze di Windows + +Eventuali contatti restituiti dai metodi `find` e `pickContact` sono readonly, quindi l'applicazione non può modificarli. Metodo `find` disponibile solo sui dispositivi Windows Phone 8.1. + +### Stranezze di Windows 8 + +Windows 8 contatti sono readonly. Tramite i contatti di Cordova API non sono queryable/ricerche, si dovrebbe informare l'utente di scegliere un contatto come una chiamata a contacts.pickContact che aprirà l'app 'Persone' dove l'utente deve scegliere un contatto. Eventuali contatti restituiti sono readonly, quindi l'applicazione non può modificarli. + +## Navigator.contacts + +### Metodi + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### Oggetti + +* Contact +* ContactName +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## navigator.contacts.create + +Il metodo `navigator.contacts.create` è sincrono e restituisce un nuovo oggetto di `Contact`. + +Questo metodo non mantiene l'oggetto contatto nel database contatti dispositivo, per cui è necessario richiamare il metodo `Contact.save`. + +### Piattaforme supportate + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 + +### Esempio + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Il metodo `navigator.contacts.find` in modo asincrono, esegue una query sul database di contatti del dispositivo e restituisce una matrice di oggetti `Contact`. Gli oggetti risultanti vengono passati alla funzione di callback `contactSuccess` specificata dal parametro **contactSuccess**. + +Il parametro **contactFields** specifica i campi per essere utilizzato come un qualificatore di ricerca. Un parametro di lunghezza zero, **contactFields** non è valido e si traduce in `ContactError.INVALID_ARGUMENT_ERROR`. Un valore di **contactFields** di `"*"` ricerche campi tutti i contatti. + +La stringa di **contactFindOptions.filter** può essere utilizzata come un filtro di ricerca quando una query sul database di contatti. Se fornito, una distinzione, corrispondenza parziale valore viene applicato a ogni campo specificato nel parametro **contactFields**. Se esiste una corrispondenza per *qualsiasi* dei campi specificati, viene restituito il contatto. Uso **contactFindOptions.desiredFields** parametro di controllo quale contattare la proprietà deve essere rispedito indietro. + +### Parametri + +* **contactFields**: contattare campi da utilizzare come un qualificatore di ricerca. *(DOMString[])* [Required] + +* **contactSuccess**: funzione di callback successo richiamato con la matrice di oggetti contatto restituiti dal database. [Required] + +* **contactError**: funzione di callback di errore, viene richiamato quando si verifica un errore. [Facoltativo] + +* **contactFindOptions**: opzioni per filtrare navigator.contacts di ricerca. [Optional] + + I tasti sono: + + * **filter**: la stringa di ricerca utilizzata per trovare navigator.contacts. *(DOMString)* (Default: `""`) + + * **multiple**: determina se l'operazione di ricerca restituisce più navigator.contacts. *(Boolean)* (Default: `false`) + + * **desiredFields**: contattare i campi per essere tornato indietro. Se specificato, il risultante `contatto` oggetto solo caratteristiche valori per questi campi. *(DOMString[])* [Optional] + +### Piattaforme supportate + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows (solo per dispositivi Windows Phone 8.1) + +### Esempio + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Stranezze di Windows + +* `__contactFields__`non è supportato, verrà ignorato. `find`metodo cercherà sempre di abbinare il nome, indirizzo email o numero di telefono di un contatto. + +## navigator.contacts.pickContact + +Il metodo `navigator.contacts.pickContact` lancia il contatto selettore per selezionare un singolo contatto. L'oggetto risultante viene passato alla funzione di callback `contactSuccess` specificata dal parametro **contactSuccess**. + +### Parametri + +* **contactSuccess**: funzione di callback di successo viene richiamato con il singolo oggetto di contatto. [Richiesto] + +* **contactError**: funzione di callback di errore, viene richiamato quando si verifica un errore. [Facoltativo] + +### Piattaforme supportate + +* Android +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### Esempio + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Contact + +L'oggetto `Contact` rappresenta il contatto di un utente. Contatti possono essere creati, memorizzati o rimossi dal database di contatti dispositivo. Contatti possono anche essere estratto (singolarmente o in blocco) dal database richiamando il metodo `navigator.contacts.find`. + +**Nota**: non tutti i campi di contatto sopra elencati sono supportati su ogni piattaforma del dispositivo. Consultare la sezione di *stranezze* su ogni piattaforma per dettagli. + +### Proprietà + +* **ID**: un identificatore univoco globale. *(DOMString)* + +* **displayName**: il nome di questo contatto, adatto per la visualizzazione a utenti finali. *(DOMString)* + +* **nome**: un oggetto che contiene tutti i componenti di un nome di persone. *(ContactName)* + +* **Nickname**: un nome informale con cui affrontare il contatto. *(DOMString)* + +* **phoneNumbers**: una matrice di numeri di telefono del contatto. *(ContactField[])* + +* **email**: una matrice di indirizzi di posta elettronica del contatto. *(ContactField[])* + +* **indirizzi**: una matrice di indirizzi di contatto. *(ContactAddress[])* + +* **IMS**: una matrice di indirizzi IM tutto il contatto. *(ContactField[])* + +* **organizzazioni**: una matrice di organizzazioni di tutto il contatto. *(ContactOrganization[])* + +* **compleanno**: il compleanno del contatto. *(Data)* + +* **Nota**: una nota sul contatto. *(DOMString)* + +* **foto**: una matrice di foto del contatto. *(ContactField[])* + +* **categorie**: matrice di tutte le categorie definite dall'utente connesso con il contatto. *(ContactField[])* + +* **URL**: matrice di pagine web connesso con il contatto. *(ContactField[])* + +### Metodi + +* **clone**: restituisce una nuova `Contact` oggetto che è una copia completa dell'oggetto chiamante, con la `id` proprietà impostata`null`. + +* **rimuovere**: rimuove il contatto dal database contatti dispositivo, altrimenti esegue un callback di errore con un `ContactError` oggetto. + +* **Salva**: salva un nuovo contatto nel database di contatti del dispositivo, o aggiorna un contatto esistente se esiste già un contatto con lo stesso **id** . + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Esempio di salvare + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### Esempio di clone + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Rimuovere esempio + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Stranezze di Android 2. x + +* **categorie**: non è supportato sui dispositivi Android 2. x, restituendo`null`. + +### BlackBerry 10 capricci + +* **ID**: assegnate dal dispositivo quando si salva il contatto. + +### Capricci di FirefoxOS + +* **categorie**: parzialmente supportati. Campi **pref** e **tipo** stanno tornando`null` + +* **IMS**: non supportato + +* **foto**: non supportato + +### iOS stranezze + +* **displayName**: non supportata su iOS, tornando `null` se non c'è nessun `ContactName` specificato, nel qual caso restituisce il nome composito, **soprannome** o `""` , rispettivamente. + +* **compleanno**: deve essere inserito come un JavaScript `Date` oggetto, allo stesso modo viene restituito. + +* **foto**: restituisce un URL del File dell'immagine, che viene memorizzato nella directory temporanea dell'applicazione. Contenuto della directory temporanea vengono rimossi quando l'applicazione termina. + +* **categorie**: questa proprietà non è attualmente supportata, restituendo`null`. + +### Windows Phone 7 e 8 stranezze + +* **displayName**: quando si crea un contatto, il valore specificato per il parametro del nome di visualizzazione è diverso dal nome visualizzato Estratto quando trovare il contatto. + +* **URL**: quando si crea un contatto, gli utenti possono inserire e salvare più di un indirizzo web, ma solo uno è disponibile durante la ricerca del contatto. + +* **phoneNumbers**: non è supportata l'opzione *pref* . Il *tipo* non è supportato in un'operazione di *trovare* . Un solo `phoneNumber` è consentita per ogni *tipo*. + +* **email**: non è supportata l'opzione *pref* . Home e personal fa riferimento la stessa voce di posta elettronica. È consentito un solo ingresso per ogni *tipo*. + +* **indirizzi**: supporta solo lavoro e casa/personali *tipo*. Il riferimento principale e personale *tipo* la stessa voce di indirizzo. È consentito un solo ingresso per ogni *tipo*. + +* **organizzazioni**: solo uno è consentito e non supporta gli attributi *pref*, *tipo*e *dipartimento* . + +* **Nota**: non supportato, restituendo`null`. + +* **IMS**: non supportato, restituendo`null`. + +* **compleanni**: non supportato, restituendo`null`. + +* **categorie**: non supportato, restituendo`null`. + +### Stranezze di Windows + +* **foto**: restituisce un URL del File dell'immagine, che viene memorizzato nella directory temporanea dell'applicazione. + +* **compleanni**: non supportato, restituendo`null`. + +* **categorie**: non supportato, restituendo`null`. + +## ContactAddress + +L'oggetto `ContactAddress` memorizza le proprietà di un singolo indirizzo di un contatto. Un oggetto `Contact` può includere più di un indirizzo in una matrice `[] ContactAddress`. + +### Proprietà + +* **pref**: impostare su `true` se questo `ContactAddress` contiene il valore dell'utente preferito. *(booleano)* + +* **tipo**: una stringa che indica il tipo di campo è, *casa* ad esempio. *(DOMString)* + +* **formattato**: indirizzo completo formattato per la visualizzazione. *(DOMString)* + +* **streetAddress**: l'indirizzo completo. *(DOMString)* + +* **località**: la città o località. *(DOMString)* + +* **regione**: lo stato o la regione. *(DOMString)* + +* **postalCode**: il codice postale o il codice postale. *(DOMString)* + +* **paese**: il nome del paese. *(DOMString)* + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Esempio + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Stranezze di Android 2. x + +* **pref**: non supportato, tornando `false` su dispositivi Android 2. x. + +### BlackBerry 10 capricci + +* **pref**: non è supportato sui dispositivi BlackBerry, restituendo`false`. + +* **tipo**: parzialmente supportati. Solo uno di *lavoro* e *casa* tipo indirizzi può essere memorizzato per ciascun contatto. + +* **formattato**: parzialmente supportati. Restituisce una concatenazione di tutti i campi Indirizzo BlackBerry. + +* **streetAddress**: supportato. Restituisce una concatenazione di BlackBerry **Indirizzo1** e **Indirizzo2** campi indirizzo. + +* **località**: supportato. Memorizzato nel campo indirizzo **città** di BlackBerry. + +* **regione**: supportato. Memorizzato nel campo indirizzo di **stateProvince** BlackBerry. + +* **postalCode**: supportato. Memorizzato nel campo dell'indirizzo **zipPostal** BlackBerry. + +* **paese**: supportato. + +### Capricci di FirefoxOS + +* **formattato**: attualmente non supportato + +### iOS stranezze + +* **pref**: non è supportato sui dispositivi iOS, restituendo`false`. + +* **formattato**: attualmente non supportati. + +### Stranezze di Windows 8 + +* **pref**: non supportato + +### Stranezze di Windows + +* **pref**: non supportato + +## ContactError + +L'oggetto `ContactError` viene restituito all'utente tramite la funzione di callback `contactError` quando si verifica un errore. + +### Proprietà + +* **codice**: uno dei codici di errore predefiniti elencati di seguito. + +### Costanti + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +L'oggetto `ContactField` è un componente riutilizzabile che rappresenta Contatta campi genericamente. Ogni oggetto `ContactField` contiene una proprietà di `value`, `type` e `pref`. Un oggetto `Contact` memorizza diverse proprietà in matrici `[] ContactField`, come numeri di telefono e indirizzi email. + +Nella maggior parte dei casi, esistono pre-determinati valori per l'attributo `type` di un oggetto **ContactField**. Ad esempio, un numero di telefono può specificare valori di **type** di *casa*, *lavoro*, *mobile*, *iPhone* o qualsiasi altro valore che è supportato dal database dei contatti su una piattaforma particolare dispositivo. Tuttavia, per il campo di **photo** del `Contacto`, il campo **type** indica il formato dell'immagine restituita: **url** quando il **value** di attributo contiene un URL per l'immagine fotografica, o *base64*, quando il **value** contiene una stringa con codifica base64 immagine. + +### Proprietà + +* **tipo**: una stringa che indica il tipo di campo è, *casa* ad esempio. *(DOMString)* + +* **valore**: il valore del campo, ad esempio un telefono numero o indirizzo e-mail. *(DOMString)* + +* **pref**: impostare su `true` se questo `ContactField` contiene il valore dell'utente preferito. *(booleano)* + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Esempio + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Stranezze Android + +* **pref**: non supportato, restituendo`false`. + +### BlackBerry 10 capricci + +* **tipo**: parzialmente supportati. Usato per i numeri di telefono. + +* **valore**: supportato. + +* **pref**: non supportato, restituendo`false`. + +### iOS stranezze + +* **pref**: non supportato, restituendo`false`. + +### Stranezze di Windows8 + +* **pref**: non supportato, restituendo`false`. + +### Stranezze di Windows + +* **pref**: non supportato, restituendo`false`. + +## ContactName + +Contiene diversi tipi di informazioni sul nome di un oggetto `Contact`. + +### Proprietà + +* **formattato**: il nome completo del contatto. *(DOMString)* + +* **familyName**: cognome del contatto. *(DOMString)* + +* **givenName**: nome del contatto. *(DOMString)* + +* **middleName**: il nome del contatto medio. *(DOMString)* + +* **honorificPrefix**: prefisso del contatto (esempio *Mr* o *Dr*) *(DOMString)* + +* **honorificSuffix**: suffisso del contatto (esempio *Esq.*). *(DOMString)* + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Esempio + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Stranezze Android + +* **formattato**: parzialmente supportati e di sola lettura. Restituisce una concatenazione di `honorificPrefix` , `givenName` , `middleName` , `familyName` , e`honorificSuffix`. + +### BlackBerry 10 capricci + +* **formattato**: parzialmente supportati. Restituisce una concatenazione di campi **firstName** e **lastName** BlackBerry. + +* **familyName**: supportato. Archiviato in campo **lastName** BlackBerry. + +* **givenName**: supportato. Archiviato in campo **firstName** BlackBerry. + +* **middleName**: non supportato, restituendo`null`. + +* **honorificPrefix**: non supportato, restituendo`null`. + +* **honorificSuffix**: non supportato, restituendo`null`. + +### Capricci di FirefoxOS + +* **formattato**: parzialmente supportati e di sola lettura. Restituisce una concatenazione di `honorificPrefix` , `givenName` , `middleName` , `familyName` , e`honorificSuffix`. + +### iOS stranezze + +* **formattato**: parzialmente supportati. Restituisce il nome composito di iOS, ma è di sola lettura. + +### Stranezze di Windows 8 + +* **formattato**: questo è l'unico nome proprietà ed è identico a `displayName` , e`nickname` + +* **familyName**: non supportato + +* **givenName**: non supportato + +* **middleName**: non supportato + +* **honorificPrefix**: non supportato + +* **honorificSuffix**: non supportato + +### Stranezze di Windows + +* **formattato**: esso è identico al`displayName` + +## ContactOrganization + +L'oggetto `ContactOrganization` memorizza la proprietà di organizzazione di un contatto. Un oggetto `Contact` memorizza uno o più oggetti `ContactOrganization` in una matrice. + +### Proprietà + +* **pref**: impostare su `true` se questo `ContactOrganization` contiene il valore dell'utente preferito. *(booleano)* + +* **tipo**: una stringa che indica il tipo di campo è, *casa* ad esempio. _(DOMString) + +* **nome**: il nome dell'organizzazione. *(DOMString)* + +* **dipartimento**: contratto lavora per il dipartimento. *(DOMString)* + +* **titolo**: titolo del contatto presso l'organizzazione. *(DOMString)* + +### Piattaforme supportate + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows (solo dispositivi Windows 8.1 e 8.1 di Windows Phone) + +### Esempio + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Stranezze di Android 2. x + +* **pref**: non supportato dai dispositivi Android 2. x, restituendo`false`. + +### BlackBerry 10 capricci + +* **pref**: non supportato dai dispositivi BlackBerry, restituendo`false`. + +* **tipo**: non supportato dai dispositivi BlackBerry, restituendo`null`. + +* **nome**: parzialmente supportati. Il primo nome dell'organizzazione è memorizzato nel campo **azienda** BlackBerry. + +* **dipartimento**: non supportato, restituendo`null`. + +* **titolo**: parzialmente supportati. Il primo titolo di organizzazione è memorizzato nel campo **jobTitle** BlackBerry. + +### Firefox OS stranezze + +* **pref**: non supportato + +* **tipo**: non supportato + +* **dipartimento**: non supportato + +* Campi **nome** e **titolo** memorizzato in **org** e **jobTitle**. + +### iOS stranezze + +* **pref**: non è supportato sui dispositivi iOS, restituendo`false`. + +* **tipo**: non è supportato sui dispositivi iOS, restituendo`null`. + +* **nome**: parzialmente supportati. Il primo nome dell'organizzazione è memorizzato nel campo **kABPersonOrganizationProperty** iOS. + +* **dipartimento**: parzialmente supportati. Il primo nome del dipartimento è memorizzato nel campo **kABPersonDepartmentProperty** iOS. + +* **titolo**: parzialmente supportati. Il primo titolo è memorizzato nel campo **kABPersonJobTitleProperty** iOS. + +### Stranezze di Windows + +* **pref**: non supportato, restituendo`false`. + +* **tipo**: non supportato, restituendo`null`. diff --git a/plugins/cordova-plugin-contacts/doc/ja/README.md b/plugins/cordova-plugin-contacts/doc/ja/README.md new file mode 100644 index 0000000..5c80bb6 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/ja/README.md @@ -0,0 +1,735 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã™ã‚‹ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.contacts` オブジェクトを定義ã—ã¾ã™ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**警告**: 連絡先データã®åŽé›†ã¨åˆ©ç”¨ã‚’é‡è¦ãªãƒ—ライãƒã‚·ãƒ¼ã®å•題を発生ã•ã›ã¾ã™ã€‚ アプリã®ãƒ—ライãƒã‚·ãƒ¼ ãƒãƒªã‚·ãƒ¼ アプリãŒé€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ã‚’使用ã™ã‚‹æ–¹æ³•ã¨ã€ä»–ã®å½“事者ã§ã¯å…±æœ‰ã•れã¦ã„ã‚‹ã‹ã©ã†ã‹ã‚’話ã—åˆã†å¿…è¦ãŒã‚りã¾ã™ã€‚ 人誰ã¨é€šä¿¡ã™ã‚‹äººã€… を明らã‹ã«ã™ã‚‹ãŸã‚ã«ã€é€£çµ¡å…ˆæƒ…å ±ãŒæ©Ÿå¯†ã¨è¦‹ãªã•れã¾ã™ã€‚ ã—ãŸãŒã£ã¦ã€ã‚¢ãƒ—リã®ãƒ—ライãƒã‚·ãƒ¼ ãƒãƒªã‚·ãƒ¼ã«åŠ ãˆã¦ã€å¼·ããŠå‹§ã‚デãƒã‚¤ã‚¹ã®ã‚ªãƒšãƒ¬ãƒ¼ãƒ†ã‚£ãƒ³ã‚° ã‚·ã‚¹ãƒ†ãƒ ãŒæ—¢ã«ã—ãªã„å ´åˆã‚¢ãƒ—リã«ã‚¢ã‚¯ã‚»ã‚¹ã¾ãŸã¯é€£çµ¡å…ˆã®ãƒ‡ãƒ¼ã‚¿ã‚’使用ã™ã‚‹å‰ã« - 時間ã®ãŠçŸ¥ã‚‰ã›ã‚’æä¾›ã—ã¾ã™ã€‚ ãã®é€šçŸ¥ã¯ã€ä¸Šè¨˜ã® (例ãˆã°ã€**[ok]** ã‚’ **ãŠã‹ã’ã§** é¸æŠžè‚¢ã‚’æç¤ºã™ã‚‹) ã«ã‚ˆã£ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’å–å¾—ã™ã‚‹ã ã‘ã§ãªãã€åŒã˜æƒ…報をæä¾›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ ã„ãã¤ã‹ã®ã‚¢ãƒ—リã®ãƒžãƒ¼ã‚±ãƒƒãƒˆãƒ— レース - 時間ã®é€šçŸ¥ã‚’æä¾›ã—ã¦ã€é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹å‰ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®è¨±å¯ã‚’å–å¾—ã™ã‚‹ã‚¢ãƒ—リをã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã«æ³¨æ„ã—ã¦ãã ã•ã„。 連絡先データã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ··ä¹±ã‚’é¿ã‘ã‚‹ã®ã«å½¹ç«‹ã¡ã¾ã™ã®ä½¿ç”¨ãŠã‚ˆã³é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ã®çŸ¥è¦šã®èª¤ç”¨ã‚’囲む明確ã§ã‚ã‹ã‚Šã‚„ã™ã„ユーザー エクスペリエンス。 詳細ã«ã¤ã„ã¦ã¯ã€ãƒ—ライãƒã‚·ãƒ¼ã«é–¢ã™ã‚‹ã‚¬ã‚¤ãƒ‰ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + +## インストール + +ã“れã¯ã‚³ãƒ«ãƒ‰ãƒ 5.0 + (ç¾åœ¨å®‰å®š v1.0.0) ã‚’å¿…è¦ã¨ã—ã¾ã™ã€‚ + + cordova plugin add cordova-plugin-contacts + + +コルドãƒã®å¤ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã‚‚**éžæŽ¨å¥¨**id (å¤ã„ v0.2.16) 経由ã§ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ãã¾ã™ã€‚ + + cordova plugin add org.apache.cordova.contacts + + +ã¾ãŸã€ãƒ¬ãƒã® url 経由ã§ç›´æŽ¥ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã™ã‚‹ã“ã¨ãŒå¯èƒ½ã  (ä¸å®‰å®š) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS ç™– + +[マニフェストã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ](https://developer.mozilla.org/en-US/Apps/Developing/Manifest) ã§èª¬æ˜Žã•れã¦ã„るよã†ã«ã€**www/manifest.webapp** を作æˆã—ã¾ã™ã€‚ 関連ã™ã‚‹æ¨©é™ã‚’追加ã—ã¾ã™ã€‚ [マニフェストã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type)「特権ã€- ã« web アプリケーションã®ç¨®é¡žã‚’変更ã™ã‚‹å¿…è¦ã‚‚。 **警告**: ã™ã¹ã¦ã®ç‰¹æ¨©ã‚’æŒã¤ã‚¢ãƒ—リケーション インライン ã‚¹ã‚¯ãƒªãƒ—ãƒˆã‚’ç¦æ­¢ã—ã¦ã„ã‚‹ [コンテンツã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ ãƒãƒªã‚·ãƒ¼](https://developer.mozilla.org/en-US/Apps/CSP) ã‚’é©ç”¨ã—ã¾ã™ã€‚ åˆ¥ã®æ–¹æ³•ã§ã€ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã‚’åˆæœŸåŒ–ã—ã¾ã™ã€‚ + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows ã®ç™– + +**Windows 10 å‰ã«:**ã™ã¹ã¦ã®é€£çµ¡å…ˆã‚’`検索`ã—〠`pickContact`メソッドã‹ã‚‰è¿”ã•れるアプリケーションãŒå¤‰æ›´ã§ããªã„よã†ã«èª­ã¿å–り専用ã§ã‚りã¾ã™ã€‚ `find` メソッド 8.1 ã® Windows Phone デãƒã‚¤ã‚¹ã§ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚ + +**Windows 10 以上:**連絡先ã¯ã€ä¿å­˜å¯èƒ½æ€§ãŒã‚りã¾ã™ã€é€£çµ¡å…ˆã‚¢ãƒ—リ ローカル記憶域ã«ä¿å­˜ã•れã¾ã™ã€‚ 連絡先も削除ã•れã¾ã™ã€‚ + +### Windows 8 ã®ç™– + +Windows 8 ã®é€£çµ¡å…ˆã¯ã€èª­ã¿å–り専用ã§ã™ã€‚ コルドムAPI コンタクトを介ã—ã¦ã•れã¾ã›ã‚“クエリ/検索å¯èƒ½ã§ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«é€šçŸ¥ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™é€£çµ¡å…ˆã‚’é¸æŠž '人' アプリを開ãã“ã¨ãŒ contacts.pickContact ã¸ã®å‘¼ã³å‡ºã—ã¨ã—ã¦ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒé€£çµ¡å…ˆã‚’é¸æŠžã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 戻ã•れる連絡先ã¯èª­ã¿å–り専用ã€ã‚¢ãƒ—リケーションを変更ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + +## navigator.contacts + +### メソッド + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### オブジェクト + + * ãŠå•ã„åˆã‚ã› + * ContactName + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +`navigator.contacts.create` メソッドã¯åŒæœŸçš„ã«ã€æ–°ã—ã„ `連絡先` オブジェクトを返ã—ã¾ã™ã€‚ + +ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¯ã€`Contact.save` メソッドを呼ã³å‡ºã™å¿…è¦ãŒã‚るデãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«é€£çµ¡å…ˆã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’ä¿æŒã—ã¾ã›ã‚“。 + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + +### 例 + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +デãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’照会ã—ã¦ã€`Contact` オブジェクトã®é…列を返㙠`navigator.contacts.find` メソッドをéžåŒæœŸçš„ã«å®Ÿè¡Œã—ã¾ã™ã€‚ çµæžœã¨ã—ã¦å¾—られるオブジェクトã¯ã€**contactSuccess** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã§æŒ‡å®šã•れ㟠`contactSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«æ¸¡ã•れã¾ã™ã€‚ + +**contactFields** パラメーター検索ã®ä¿®é£¾å­ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãƒ•ィールドを指定ã—ã¾ã™ã€‚ ゼロ長㕠**contactFields** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã¯æœ‰åйã§ã€`ContactError.INVALID_ARGUMENT_ERROR` ã®çµæžœã—ã¾ã™ã€‚ **contactFields** 値 `"*"` ã™ã¹ã¦ã®é€£çµ¡å…ˆãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ãŒæ¤œç´¢ã•れã¾ã™ã€‚ + +**contactFindOptions.filter** 文字列ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’照会ã™ã‚‹ã¨ãã«æ¤œç´¢ãƒ•ィルターã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã™ã€‚ 指定ã—ãŸå ´åˆã€å¤§æ–‡å­—ã¨å°æ–‡å­—ã€éƒ¨åˆ†çš„ãªå€¤ã®ä¸€è‡´ã™ã‚‹ **contactFields** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã§æŒ‡å®šã•れãŸãƒ•ィールドã”ã¨ã«é©ç”¨ã•れã¾ã™ã€‚ 一致ã™ã‚‹ *ä»»æ„* 指定ã®ãƒ•ィールドãŒã‚ã‚‹å ´åˆã¯ã€é€£çµ¡å…ˆãŒè¿”ã•れã¾ã™ã€‚ ãƒãƒƒã‚¯é€£çµ¡å…ˆãƒ—ロパティを制御ã™ã‚‹ **contactFindOptions.desiredFields** パラメーターを使用ã—ã¾ã™ãŒè¿”ã•れる必è¦ãŒã‚りã¾ã™ã€‚ + +### パラメーター + + * **contactFields**: 連絡先検索修飾å­ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãƒ•ィールド。*(DOMString[])* [Required] + + * **contactSuccess**: Contact オブジェクトã®é…列ã«å‘¼ã³å‡ºã•れるæˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯é–¢æ•°ã¯ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰è¿”ã•れã¾ã™ã€‚[Required] + + * **contactError**: エラー コールãƒãƒƒã‚¯é–¢æ•°ã¯ã€ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã«å‘¼ã³å‡ºã•れã¾ã™ã€‚[オプション] + + * **contactFindOptions**: navigator.contacts をフィルターã™ã‚‹ã‚ªãƒ—ションを検索ã—ã¾ã™ã€‚[Optional] + + ã‚­ãƒ¼ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚ + + * **filter**: navigator.contacts ã®æ¤œç´¢ã«ä½¿ç”¨ã™ã‚‹æ¤œç´¢æ–‡å­—列。*(,)*(デフォルト: `""`) + + * **multiple**: 複数 navigator.contacts ã‹ã©ã†ã‹ã¯ã€æ¤œç´¢æ“作ã«è¿”ã™ã‚’決定ã—ã¾ã™ã€‚*(ブール値)*(デフォルト: `false`) + + * **desiredFields**: 戻ã£ã¦è¿”ã•れるフィールドã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。指定ã—ãŸå ´åˆã€çµæžœ `Contact` オブジェクトã“れらã®ãƒ•ィールドã®å€¤ã§ã®ã¿æ©Ÿèƒ½ã—ã¾ã™ã€‚*(DOMString[])* [Optional] + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows (Windows Phone 8.1 ãŠã‚ˆã³ Windows 10) + +### 例 + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows ã®ç™– + + * `__contactFields__`サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã¨ç„¡è¦–ã•れã¾ã™ã€‚`find`メソッドã¯ã€å¸¸ã«åå‰ã€é›»å­ãƒ¡ãƒ¼ãƒ« アドレスã€ã¾ãŸã¯é€£çµ¡å…ˆã®é›»è©±ç•ªå·ã«ä¸€è‡´ã¾ã—ょã†ã€‚ + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact` メソッドã¯å˜ä¸€ã®é€£çµ¡å…ˆã‚’é¸æŠžã™ã‚‹é€£çµ¡å…ˆãƒ”ッカーを起動ã—ã¾ã™ã€‚ çµæžœã¨ã—ã¦å¾—られるオブジェクトã¯ã€**contactSuccess** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã§æŒ‡å®šã•れ㟠`contactSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«æ¸¡ã•れã¾ã™ã€‚ + +### パラメーター + + * **contactSuccess**: 1 ã¤ã®é€£çµ¡å…ˆã‚ªãƒ–ジェクトã«å‘¼ã³å‡ºã•れるæˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯é–¢æ•°ã€‚[å¿…é ˆ] + + * **contactError**: エラー コールãƒãƒƒã‚¯é–¢æ•°ã¯ã€ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã«å‘¼ã³å‡ºã•れã¾ã™ã€‚[オプション] + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### 例 + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## ãŠå•ã„åˆã‚ã› + +`Contact` オブジェクトã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®é€£çµ¡å…ˆã‚’表ã—ã¾ã™ã€‚ 連絡先ã®ä½œæˆã€æ ¼ç´ã€ã¾ãŸã¯ãƒ‡ãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰å‰Šé™¤ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ 連絡先もå–å¾—ã§ãã¾ã™ (個別ã«ã¾ãŸã¯ä¸€æ‹¬ã§) データベースã‹ã‚‰ `navigator.contacts.find` メソッドを呼ã³å‡ºã™ã“ã¨ã«ã‚ˆã£ã¦ã€‚ + +**注**: ã™ã¹ã¦ä¸Šè¨˜ã®é€£çµ¡å…ˆãƒ•ィールドã®ã™ã¹ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ プラットフォームã§ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚詳細ã«ã¤ã„ã¦ã¯å„プラットフォーム㮠*互æ›* セクションを確èªã—ã¦ãã ã•ã„。 + +### プロパティ + + * **id**: グローãƒãƒ«ã«ä¸€æ„ã®è­˜åˆ¥å­ã€‚*(,)* + + * **displayName**: エンド ・ ユーザーã¸ã®è¡¨ç¤ºã«é©ã—ãŸã€ã“ã®é€£çµ¡å…ˆã®åå‰ã€‚*(,)* + + * **åå‰**: 人ã®åå‰ã®ã™ã¹ã¦ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚’æ ¼ç´ã™ã‚‹ã‚ªãƒ–ジェクト。*(ContactName)* + + * **ニックãƒãƒ¼ãƒ **: 連絡先ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ä½¿ç”¨ã™ã‚‹ã‚«ã‚¸ãƒ¥ã‚¢ãƒ«ãªåå‰ã€‚*(,)* + + * **電話番å·**: 連絡先ã®é›»è©±ç•ªå·ã®é…列。*(ContactField[])* + + * **メール**: 連絡先ã®é›»å­ãƒ¡ãƒ¼ãƒ« アドレスã®é…列。*(ContactField[])* + + * **アドレス**: 連絡先ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã®é…列。*(ContactAddress[])* + + * **ims**: 連絡先㮠IM アドレスã®é…列。*(ContactField[])* + + * **組織**: 連絡先ã®çµ„ç¹”ã®é…列。*(ContactOrganization[])* + + * **誕生日**: 連絡先ã®èª•生日。*(日)* + + * **注**: 連絡先ã«ã¤ã„ã¦ã®ãƒ¡ãƒ¢ã€‚*(,)* + + * **写真**: 連絡先ã®å†™çœŸã®é…列。*(ContactField[])* + + * **カテゴリ**: å–引先担当者ã«é–¢é€£ä»˜ã‘られã¦ã„ã‚‹ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã‚«ãƒ†ã‚´ãƒªã®é…列。*(ContactField[])* + + * **url**: å–引先担当者ã«é–¢é€£ä»˜ã‘られã¦ã„ã‚‹ web ページã®é…列。*(ContactField[])* + +### メソッド + + * **クローン**: æ–°ã—ã„ã‚’è¿”ã—ã¾ã™ `Contact` ã¨å‘¼ã³å‡ºã—å…ƒã®ã‚ªãƒ–ジェクトã®ãƒ‡ã‚£ãƒ¼ãƒ— コピーã§ã‚るオブジェクト㮠`id` プロパティã«è¨­å®š`null`. + + * **削除**: デãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰é€£çµ¡å…ˆã‚’削除ã—ã¾ã™ã€ãれ以外ã®å ´åˆã®ãŸã‚ã®ã‚¨ãƒ©ãƒ¼ コールãƒãƒƒã‚¯ã‚’実行ã™ã‚‹ã€ `ContactError` オブジェクト。 + + * **ä¿å­˜**: デãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«æ–°ã—ã„連絡先をä¿å­˜ã¾ãŸã¯åŒã˜**id**ã‚’æŒã¤é€£çµ¡å…ˆãŒæ—¢ã«å­˜åœ¨ã™ã‚‹å ´åˆã€æ—¢å­˜ã®é€£çµ¡å…ˆã‚’æ›´æ–°ã—ã¾ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### ä¿å­˜ã®ä¾‹ + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### クローンã®ä¾‹ + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### 削除ã®ä¾‹ + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### アンドロイド 2.X ç™– + + * **カテゴリ**: 返㙠2.X ã® Android デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`null`. + +### ブラックベリー 10 ç™– + + * **id**: 連絡先をä¿å­˜ã™ã‚‹ã¨ãã«ã€ãƒ‡ãƒã‚¤ã‚¹ã«ã‚ˆã£ã¦å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¾ã™ã€‚ + +### FirefoxOS ã®ç™– + + * **カテゴリ**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚フィールド**県**・**タイプ**ã‚’è¿”ã™`null` + + * **ims**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **写真**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### iOS ã®ç™– + + * **displayName**: 返㙠iOS ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ `null` ãŒãªã„é™ã‚Šã€ãªã„ `ContactName` 指定ã™ã‚‹ã¨ã€ãã®å ´åˆã¯è¤‡åˆåã€**ニックãƒãƒ¼ãƒ **ã‚’è¿”ã—ã¾ã™ã¾ãŸã¯ `""` ã€ãれãžã‚Œã€‚ + + * **誕生日**: JavaScript ã¨ã—ã¦å…¥åŠ›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ `Date` オブジェクトã€åŒã˜æ–¹æ³•ãŒè¿”ã•れã¾ã™ã€‚ + + * **写真**: アプリケーションã®ä¸€æ™‚ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æ ¼ç´ã•れã¦ã„るイメージã¸ã®ãƒ•ァイル㮠URL ã‚’è¿”ã—ã¾ã™ã€‚一時ディレクトリã®å†…容ã¯ã€ã‚¢ãƒ—リケーションã®çµ‚了時ã«å‰Šé™¤ã•れã¾ã™ã€‚ + + * **カテゴリ**: ã“ã®ãƒ—ロパティã¯ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã€è¿”ã™`null`. + +### Windows Phone 7 㨠8 ç™– + + * **displayName**: 表示åパラメーターã®è¡¨ç¤ºåã¨ç•°ãªã‚‹ãŸã‚ã«æä¾›å€¤ã‚’å–å¾—ã€é€£çµ¡å…ˆã‚’検索ã™ã‚‹ã¨ãã€é€£çµ¡å…ˆã‚’作æˆã™ã‚‹ã¨ã。 + + * **url**: 連絡先を作æˆã™ã‚‹ã¨ãユーザー入力ä¿å­˜ã§ãã€1 ã¤ä»¥ä¸Šã® web アドレス㌠1 ã¤ã ã‘ã€é€£çµ¡å…ˆã‚’検索ã™ã‚‹ã¨ã。 + + * **電話番å·**:*県*オプションã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 *åž‹*ã¯ã€*検索*æ“作ã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 1 ã¤ã ã‘ `phoneNumber` ã¯å„*タイプ*ã®è¨±å¯. + + * **メール**:*県*オプションã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。家庭や個人的ãªãƒ¡ãƒ¼ãƒ«ã¨åŒã˜ã‚¨ãƒ³ãƒˆãƒªã‚’å‚ç…§ã—ã¾ã™ã€‚å„*タイプ*ã® 1 ã¤ã ã‘ã®ã‚¨ãƒ³ãƒˆãƒªãŒè¨±å¯ã•れã¦. + + * **アドレス**: 仕事ã¨ãƒ›ãƒ¼ãƒ /パーソナル*タイプ*ã®ã¿ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚家庭や個人*åž‹*å‚ç…§ã—ã¦åŒã˜ã‚¢ãƒ‰ãƒ¬ã‚¹ エントリ。å„*タイプ*ã® 1 ã¤ã ã‘ã®ã‚¨ãƒ³ãƒˆãƒªãŒè¨±å¯ã•れã¦. + + * **組織**: 1 ã¤ã ã‘ãŒè¨±å¯ã•れã€*県*ã€*タイプ*ã€ãŠã‚ˆã³*部門*ã®å±žæ€§ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。 + + * **注**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **ims**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **誕生日**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **カテゴリ**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **remove**: メソッドã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ + +### Windows ã®ç™– + + * **写真**: アプリケーションã®ä¸€æ™‚ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æ ¼ç´ã•れã¦ã„るイメージã¸ã®ãƒ•ァイル㮠URL ã‚’è¿”ã—ã¾ã™ã€‚ + + * **誕生日**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **カテゴリ**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **remove**: Windows 10 以上メソッドã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã®ã¿ã€‚ + +## ContactAddress + +`ContactAddress` オブジェクトã¯ã€é€£çµ¡å…ˆã® 1 ã¤ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã®ãƒ—ロパティを格ç´ã—ã¾ã™ã€‚ `Contact` オブジェクトã¯ã€`ContactAddress` é…列㮠1 ã¤ä»¥ä¸Šã®ã‚¢ãƒ‰ãƒ¬ã‚¹ãªã©ãŒã‚りã¾ã™ã€‚ + +### プロパティ + + * **県**: ã«è¨­å®šã•れã¦ã„ã‚‹ `true` å ´åˆã¯ã€ã“ã® `ContactAddress` ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŽ¨å¥¨å€¤ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚*(ブール値)* + + * **タイプ**: ãŸã¨ãˆã°ãƒ•ィールドã“れã¯*ホーム*ã®ç¨®é¡žã‚’ç¤ºã™æ–‡å­—列。*(,)* + + * **書å¼è¨­å®š**: 表示用ã«ãƒ•ォーマットã®å®Œå…¨ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã€‚*(,)* + + * **番地**: 完全ãªä½æ‰€ã€‚*(,)* + + * **局所性**: 都市ã¾ãŸã¯å ´æ‰€ã€‚*(,)* + + * **地域**: 状態ã¾ãŸã¯åœ°åŸŸã€‚*(,)* + + * **郵便番å·**: 郵便番å·ã¾ãŸã¯éƒµä¾¿ç•ªå·ã€‚*(,)* + + * **国**: 国ã®åå‰ã€‚*(,)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### 例 + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### アンドロイド 2.X ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„返㙠`false` 2.X ã® Android デãƒã‚¤ã‚¹ã§ã€‚ + +### ブラックベリー 10 ç™– + + * **県**: 戻るã€BlackBerry デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + + * **種類**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚連絡先ã”ã¨ã® 1 ã¤ã ã‘å„*仕事*ã¨*家庭*ã®ç¨®é¡žã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’æ ¼ç´ã§ãã¾ã™ã€‚ + + * **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚ã™ã¹ã¦ã® BlackBerry アドレス フィールドã®é€£çµã‚’è¿”ã—ã¾ã™ã€‚ + + * **番地**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリーã®**使‰€ 1** **使‰€ 2**ã®é€£çµã‚¢ãƒ‰ãƒ¬ã‚¹ フィールドを返ã—ã¾ã™ã€‚ + + * **局所性**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー**市**アドレス ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **地域**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー **stateProvince**アドレス ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **郵便番å·**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー **zipPostal**アドレス ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **国**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ + +### FirefoxOS ã®ç™– + + * **フォーマット**: ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ + +### iOS ã®ç™– + + * **県**: 戻る iOS デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + + * **フォーマット**: ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Windows 8 ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Windows ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +## ContactError + +`ContactError` オブジェクトã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã« `contactError` コールãƒãƒƒã‚¯é–¢æ•°ã‚’通ã˜ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«è¿”ã•れã¾ã™ã€‚ + +### プロパティ + + * **コード**: 次ã®ã„ãšã‚Œã‹ã®å®šç¾©æ¸ˆã¿ã®ã‚¨ãƒ©ãƒ¼ コード。 + +### 定数 + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +`ContactField` オブジェクトã¯é€£çµ¡å…ˆãƒ•ィールドをç·ç§°ã‚’表ã™å†åˆ©ç”¨å¯èƒ½ãªã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã§ã™ã€‚ å„ `ContactField` オブジェクトã«ã¯ `値` `åž‹`ã€ãŠã‚ˆã³ `県` プロパティãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ `連絡先` オブジェクトã¯ã€`ContactField` é…列ã«ã€é›»è©±ç•ªå·ã€ãƒ¡ãƒ¼ãƒ« アドレスãªã©ã®ã„ãã¤ã‹ã®ãƒ—ロパティを格ç´ã—ã¾ã™ã€‚ + +ã»ã¨ã‚“ã©ã®å ´åˆã€`ContactField` オブジェクト㮠**type** 属性ã®ã‚らã‹ã˜ã‚決ã‚られãŸå€¤ã¯ã‚りã¾ã›ã‚“。 ãŸã¨ãˆã°ã€é›»è©±ç•ªå·ãŒ *ホーム*ã€*仕事*ã€*モãƒã‚¤ãƒ«*ã€*iPhone*ã€ã¾ãŸã¯ç‰¹å®šã®ãƒ‡ãƒã‚¤ã‚¹ プラットフォームã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹ä»–ã®å€¤ã® **åž‹** ã®å€¤ã‚’指定ã§ãã¾ã™ã€‚ ãŸã ã—ã€`連絡先` **ã®å†™çœŸ**] ã®ãƒ•ィールド **ã®ç¨®é¡ž**] フィールドを示ã—ã¾ã™è¿”ã•れる画åƒã®å½¢å¼: **url** **値** 属性 **値** ã‚’ base64 ã§ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã•れãŸã‚¤ãƒ¡ãƒ¼ã‚¸ã®æ–‡å­—列ãŒå«ã¾ã‚Œã‚‹å ´åˆã«å†™çœŸã‚¤ãƒ¡ãƒ¼ã‚¸ã¾ãŸã¯ *base64* ã« URL ãŒå«ã¾ã‚Œã‚‹å ´åˆã€‚ + +### プロパティ + + * **タイプ**: ãŸã¨ãˆã°ãƒ•ィールドã“れã¯*ホーム*ã®ç¨®é¡žã‚’ç¤ºã™æ–‡å­—列。*(,)* + + * **値**: 電話番å·ã‚„é›»å­ãƒ¡ãƒ¼ãƒ« アドレスãªã©ã€ãƒ•ィールドã®å€¤ã€‚*(,)* + + * **県**: ã«è¨­å®šã•れã¦ã„ã‚‹ `true` å ´åˆã¯ã€ã“ã® `ContactField` ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŽ¨å¥¨å€¤ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚*(ブール値)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### 例 + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Android ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### ブラックベリー 10 ç™– + + * **種類**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚電話番å·ã‚’使用ã—ã¾ã™ã€‚ + + * **値**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### iOS ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### Windows8 ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### Windows ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +## ContactName + +ç•°ãªã‚‹ç¨®é¡ž `Contact` オブジェクトã®åå‰ã«ã¤ã„ã¦ã®æƒ…å ±ã«ã¯ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ + +### プロパティ + + * **フォーマット**: 連絡先ã®å®Œå…¨ãªåå‰ã€‚*(,)* + + * **familyName**: 連絡先ã®å§“。*(,)* + + * **givenName**: 連絡先ã®åå‰ã€‚*(,)* + + * **ミドル ãƒãƒ¼ãƒ **: 連絡先ã®ãƒŸãƒ‰ãƒ« ãƒãƒ¼ãƒ ã€‚*(,)* + + * **honorificPrefix**: 連絡先ã®ãƒ—レフィックス (例*æ°*ã¾ãŸã¯*åšå£«*) *(,)* + + * **honorificSuffix**: 連絡先ã®ã‚µãƒ•ィックス (*å¼è­·å£«*ã®ä¾‹)。*(,)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### 例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android ã®ç™– + + * **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¦ãŠã‚Šã€èª­ã¿å–り専用ã§ã™ã€‚ 連çµã‚’è¿”ã—ã¾ã™ `honorificPrefix` 〠`givenName` 〠`middleName` 〠`familyName` ã¨`honorificSuffix`. + +### ブラックベリー 10 ç™– + + * **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚ブラックベリー **firstName**ã¨**lastName**フィールドã®é€£çµã‚’è¿”ã—ã¾ã™ã€‚ + + * **familyName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー **[æ°å]**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **givenName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリーã®**firstName**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **ミドル ãƒãƒ¼ãƒ **: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **honorificPrefix**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **honorificSuffix**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +### FirefoxOS ã®ç™– + + * **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¦ãŠã‚Šã€èª­ã¿å–り専用ã§ã™ã€‚ 連çµã‚’è¿”ã—ã¾ã™ `honorificPrefix` 〠`givenName` 〠`middleName` 〠`familyName` ã¨`honorificSuffix`. + +### iOS ã®ç™– + + * **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚IOS 複åˆåã‚’è¿”ã—ã¾ã™ãŒã€èª­ã¿å–り専用ã§ã™ã€‚ + +### Windows 8 ã®ç™– + + * **フォーマット**: ã“れã¯ã€ãƒ—ロパティã®åå‰ã—ã€åŒã˜ã§ã™ `displayName` ã¨`nickname` + + * **familyName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **givenName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **ミドル ãƒãƒ¼ãƒ **: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **honorificPrefix**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **honorificSuffix**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Windows ã®ç™– + + * **フォーマット**: ã¨åŒã˜ã§ã™`displayName` + +## ContactOrganization + +`ContactOrganization` オブジェクトã¯ã€é€£çµ¡å…ˆã®çµ„ç¹”ã®ãƒ—ロパティを格ç´ã—ã¾ã™ã€‚`連絡先` オブジェクトã¯ã€é…列㮠1 ã¤ä»¥ä¸Šã® `ContactOrganization` オブジェクトを格ç´ã—ã¾ã™ã€‚ + +### プロパティ + + * **県**: ã«è¨­å®šã•れã¦ã„ã‚‹ `true` å ´åˆã¯ã€ã“ã® `ContactOrganization` ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŽ¨å¥¨å€¤ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚*(ブール値)* + + * **タイプ**: ãŸã¨ãˆã°ãƒ•ィールドã“れã¯*ホーム*ã®ç¨®é¡žã‚’ç¤ºã™æ–‡å­—列。_(DOMString) + + * **åå‰**: 組織ã®åå‰ã€‚*(,)* + + * **部門**: 契約ã«å‹¤ã‚ã¦ã„る部門。*(,)* + + * **タイトル**: 組織ã®é€£çµ¡å…ˆã®ã‚¿ã‚¤ãƒˆãƒ«ã€‚*(,)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows (Windows 8.1 ãŠã‚ˆã³ Windows Phone 8.1 装置ã®ã¿ï¼‰ + +### 例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### アンドロイド 2.X ç™– + + * **県**: 返㙠2.X ã® Android デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + +### ブラックベリー 10 ç™– + + * **県**: 戻るã€BlackBerry デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + + * **タイプ**: 戻るã€BlackBerry デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`null`. + + * **åå‰**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®çµ„ç¹”åã¯ã€**会社**ã®ãƒ–ラックベリーã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **部門**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + + * **タイトル**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚çµ„ç¹”ã®æœ€åˆã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ãƒ–ラックベリー**æ°å**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +### Firefox OS ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **タイプ**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * **部門**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + + * フィールドã®**åå‰**ã¨**組織**ã€**æ°å**ã«æ ¼ç´ã•れãŸ**タイトル**. + +### iOS ã®ç™– + + * **県**: 戻る iOS デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + + * **タイプ**: 戻る iOS デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`null`. + + * **åå‰**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®çµ„ç¹”åã¯ã€iOS **kABPersonOrganizationProperty**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **部門**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®éƒ¨ç½²å㯠iOS **kABPersonDepartmentProperty**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + + * **タイトル**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ã€iOS **kABPersonJobTitleProperty**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +### Windows ã®ç™– + + * **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + + * **タイプ**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/ja/index.md b/plugins/cordova-plugin-contacts/doc/ja/index.md new file mode 100644 index 0000000..3e85bbc --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/ja/index.md @@ -0,0 +1,719 @@ + + +# cordova-plugin-contacts + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã™ã‚‹ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.contacts` オブジェクトを定義ã—ã¾ã™ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**警告**: 連絡先データã®åŽé›†ã¨åˆ©ç”¨ã‚’é‡è¦ãªãƒ—ライãƒã‚·ãƒ¼ã®å•題を発生ã•ã›ã¾ã™ã€‚ アプリã®ãƒ—ライãƒã‚·ãƒ¼ ãƒãƒªã‚·ãƒ¼ アプリãŒé€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ã‚’使用ã™ã‚‹æ–¹æ³•ã¨ã€ä»–ã®å½“事者ã§ã¯å…±æœ‰ã•れã¦ã„ã‚‹ã‹ã©ã†ã‹ã‚’話ã—åˆã†å¿…è¦ãŒã‚りã¾ã™ã€‚ 人誰ã¨é€šä¿¡ã™ã‚‹äººã€… を明らã‹ã«ã™ã‚‹ãŸã‚ã«ã€é€£çµ¡å…ˆæƒ…å ±ãŒæ©Ÿå¯†ã¨è¦‹ãªã•れã¾ã™ã€‚ ã—ãŸãŒã£ã¦ã€ã‚¢ãƒ—リã®ãƒ—ライãƒã‚·ãƒ¼ ãƒãƒªã‚·ãƒ¼ã«åŠ ãˆã¦ã€å¼·ããŠå‹§ã‚デãƒã‚¤ã‚¹ã®ã‚ªãƒšãƒ¬ãƒ¼ãƒ†ã‚£ãƒ³ã‚° ã‚·ã‚¹ãƒ†ãƒ ãŒæ—¢ã«ã—ãªã„å ´åˆã‚¢ãƒ—リã«ã‚¢ã‚¯ã‚»ã‚¹ã¾ãŸã¯é€£çµ¡å…ˆã®ãƒ‡ãƒ¼ã‚¿ã‚’使用ã™ã‚‹å‰ã« - 時間ã®ãŠçŸ¥ã‚‰ã›ã‚’æä¾›ã—ã¾ã™ã€‚ ãã®é€šçŸ¥ã¯ã€ä¸Šè¨˜ã® (例ãˆã°ã€**[ok]** ã‚’ **ãŠã‹ã’ã§** é¸æŠžè‚¢ã‚’æç¤ºã™ã‚‹) ã«ã‚ˆã£ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’å–å¾—ã™ã‚‹ã ã‘ã§ãªãã€åŒã˜æƒ…報をæä¾›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ ã„ãã¤ã‹ã®ã‚¢ãƒ—リã®ãƒžãƒ¼ã‚±ãƒƒãƒˆãƒ— レース - 時間ã®é€šçŸ¥ã‚’æä¾›ã—ã¦ã€é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹å‰ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®è¨±å¯ã‚’å–å¾—ã™ã‚‹ã‚¢ãƒ—リをã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã«æ³¨æ„ã—ã¦ãã ã•ã„。 連絡先データã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ··ä¹±ã‚’é¿ã‘ã‚‹ã®ã«å½¹ç«‹ã¡ã¾ã™ã®ä½¿ç”¨ãŠã‚ˆã³é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ã®çŸ¥è¦šã®èª¤ç”¨ã‚’囲む明確ã§ã‚ã‹ã‚Šã‚„ã™ã„ユーザー エクスペリエンス。 詳細ã«ã¤ã„ã¦ã¯ã€ãƒ—ライãƒã‚·ãƒ¼ã«é–¢ã™ã‚‹ã‚¬ã‚¤ãƒ‰ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + +## インストール + + cordova plugin add cordova-plugin-contacts + + +### Firefox OS ç™– + +[マニフェストã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ][1] ã§èª¬æ˜Žã•れã¦ã„るよã†ã«ã€**www/manifest.webapp** を作æˆã—ã¾ã™ã€‚ 関連ã™ã‚‹æ¨©é™ã‚’追加ã—ã¾ã™ã€‚ [マニフェストã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ][2]「特権ã€- ã« web アプリケーションã®ç¨®é¡žã‚’変更ã™ã‚‹å¿…è¦ã‚‚。 **警告**: ã™ã¹ã¦ã®ç‰¹æ¨©ã‚’æŒã¤ã‚¢ãƒ—リケーション インライン ã‚¹ã‚¯ãƒªãƒ—ãƒˆã‚’ç¦æ­¢ã—ã¦ã„ã‚‹ [コンテンツã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ ãƒãƒªã‚·ãƒ¼][3] ã‚’é©ç”¨ã—ã¾ã™ã€‚ åˆ¥ã®æ–¹æ³•ã§ã€ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã‚’åˆæœŸåŒ–ã—ã¾ã™ã€‚ + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows ã®ç™– + +`find` ã—ã€`pickContact` メソッドã‹ã‚‰è¿”ã•れるã™ã¹ã¦ã®é€£çµ¡å…ˆã¯ readonlyã€ã‚¢ãƒ—リケーションを変更ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 `find` メソッド 8.1 ã® Windows Phone デãƒã‚¤ã‚¹ã§ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚ + +### Windows 8 ã®ç™– + +Windows 8 ã®é€£çµ¡å…ˆã¯ã€èª­ã¿å–り専用ã§ã™ã€‚ コルドムAPI コンタクトを介ã—ã¦ã•れã¾ã›ã‚“クエリ/検索å¯èƒ½ã§ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«é€šçŸ¥ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™é€£çµ¡å…ˆã‚’é¸æŠž '人' アプリを開ãã“ã¨ãŒ contacts.pickContact ã¸ã®å‘¼ã³å‡ºã—ã¨ã—ã¦ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒé€£çµ¡å…ˆã‚’é¸æŠžã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 戻ã•れる連絡先ã¯èª­ã¿å–り専用ã€ã‚¢ãƒ—リケーションを変更ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + +## navigator.contacts + +### メソッド + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### オブジェクト + +* ãŠå•ã„åˆã‚ã› +* å¾—æ„先コード +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## navigator.contacts.create + +`navigator.contacts.create` メソッドã¯åŒæœŸçš„ã«ã€æ–°ã—ã„ `連絡先` オブジェクトを返ã—ã¾ã™ã€‚ + +ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¯ã€`Contact.save` メソッドを呼ã³å‡ºã™å¿…è¦ãŒã‚るデãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«é€£çµ¡å…ˆã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’ä¿æŒã—ã¾ã›ã‚“。 + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 + +### 例 + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +デãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’照会ã—ã¦ã€`Contact` オブジェクトã®é…列を返㙠`navigator.contacts.find` メソッドをéžåŒæœŸçš„ã«å®Ÿè¡Œã—ã¾ã™ã€‚ çµæžœã¨ã—ã¦å¾—られるオブジェクトã¯ã€**contactSuccess** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã§æŒ‡å®šã•れ㟠`contactSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«æ¸¡ã•れã¾ã™ã€‚ + +**contactFields** パラメーター検索ã®ä¿®é£¾å­ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãƒ•ィールドを指定ã—ã¾ã™ã€‚ ゼロ長㕠**contactFields** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã¯æœ‰åйã§ã€`ContactError.INVALID_ARGUMENT_ERROR` ã®çµæžœã—ã¾ã™ã€‚ **contactFields** 値 `"*"` ã™ã¹ã¦ã®é€£çµ¡å…ˆãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ãŒæ¤œç´¢ã•れã¾ã™ã€‚ + +**contactFindOptions.filter** 文字列ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’照会ã™ã‚‹ã¨ãã«æ¤œç´¢ãƒ•ィルターã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã™ã€‚ 指定ã—ãŸå ´åˆã€å¤§æ–‡å­—ã¨å°æ–‡å­—ã€éƒ¨åˆ†çš„ãªå€¤ã®ä¸€è‡´ã™ã‚‹ **contactFields** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã§æŒ‡å®šã•れãŸãƒ•ィールドã”ã¨ã«é©ç”¨ã•れã¾ã™ã€‚ 一致ã™ã‚‹ *ä»»æ„* 指定ã®ãƒ•ィールドãŒã‚ã‚‹å ´åˆã¯ã€é€£çµ¡å…ˆãŒè¿”ã•れã¾ã™ã€‚ ãƒãƒƒã‚¯é€£çµ¡å…ˆãƒ—ロパティを制御ã™ã‚‹ **contactFindOptions.desiredFields** パラメーターを使用ã—ã¾ã™ãŒè¿”ã•れる必è¦ãŒã‚りã¾ã™ã€‚ + +### パラメーター + +* **contactFields**: 連絡先検索修飾å­ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãƒ•ィールド。*(DOMString[])* [Required] + +* **contactSuccess**: Contact オブジェクトã®é…列ã«å‘¼ã³å‡ºã•れるæˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯é–¢æ•°ã¯ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰è¿”ã•れã¾ã™ã€‚[Required] + +* **contactError**: エラー コールãƒãƒƒã‚¯é–¢æ•°ã¯ã€ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã«å‘¼ã³å‡ºã•れã¾ã™ã€‚[オプション] + +* **contactFindOptions**: navigator.contacts をフィルターã™ã‚‹ã‚ªãƒ—ションを検索ã—ã¾ã™ã€‚[Optional] + + ã‚­ãƒ¼ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚ + + * **filter**: navigator.contacts ã®æ¤œç´¢ã«ä½¿ç”¨ã™ã‚‹æ¤œç´¢æ–‡å­—列。*(,)*(デフォルト: `""`) + + * **multiple**: 複数 navigator.contacts ã‹ã©ã†ã‹ã¯ã€æ¤œç´¢æ“作ã«è¿”ã™ã‚’決定ã—ã¾ã™ã€‚*(ブール値)*(デフォルト: `false`) + + * **desiredFields**: 戻ã£ã¦è¿”ã•れるフィールドã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。指定ã—ãŸå ´åˆã€çµæžœ `Contact` オブジェクトã“れらã®ãƒ•ィールドã®å€¤ã§ã®ã¿æ©Ÿèƒ½ã—ã¾ã™ã€‚*(DOMString[])* [Optional] + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows (Windows Phone 8.1 装置ã®ã¿ï¼‰ + +### 例 + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows ã®ç™– + +* `__contactFields__`サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã¨ç„¡è¦–ã•れã¾ã™ã€‚`find`メソッドã¯ã€å¸¸ã«åå‰ã€é›»å­ãƒ¡ãƒ¼ãƒ« アドレスã€ã¾ãŸã¯é€£çµ¡å…ˆã®é›»è©±ç•ªå·ã«ä¸€è‡´ã¾ã—ょã†ã€‚ + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact` メソッドã¯å˜ä¸€ã®é€£çµ¡å…ˆã‚’é¸æŠžã™ã‚‹é€£çµ¡å…ˆãƒ”ッカーを起動ã—ã¾ã™ã€‚ çµæžœã¨ã—ã¦å¾—られるオブジェクトã¯ã€**contactSuccess** ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã§æŒ‡å®šã•れ㟠`contactSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«æ¸¡ã•れã¾ã™ã€‚ + +### パラメーター + +* **contactSuccess**: 1 ã¤ã®é€£çµ¡å…ˆã‚ªãƒ–ジェクトã«å‘¼ã³å‡ºã•れるæˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯é–¢æ•°ã€‚[å¿…é ˆ] + +* **contactError**: エラー コールãƒãƒƒã‚¯é–¢æ•°ã¯ã€ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã«å‘¼ã³å‡ºã•れã¾ã™ã€‚[オプション] + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### 例 + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## ãŠå•ã„åˆã‚ã› + +`Contact` オブジェクトã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®é€£çµ¡å…ˆã‚’表ã—ã¾ã™ã€‚ 連絡先ã®ä½œæˆã€æ ¼ç´ã€ã¾ãŸã¯ãƒ‡ãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰å‰Šé™¤ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ 連絡先もå–å¾—ã§ãã¾ã™ (個別ã«ã¾ãŸã¯ä¸€æ‹¬ã§) データベースã‹ã‚‰ `navigator.contacts.find` メソッドを呼ã³å‡ºã™ã“ã¨ã«ã‚ˆã£ã¦ã€‚ + +**注**: ã™ã¹ã¦ä¸Šè¨˜ã®é€£çµ¡å…ˆãƒ•ィールドã®ã™ã¹ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ プラットフォームã§ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚詳細ã«ã¤ã„ã¦ã¯å„プラットフォーム㮠*互æ›* セクションを確èªã—ã¦ãã ã•ã„。 + +### プロパティ + +* **id**: グローãƒãƒ«ã«ä¸€æ„ã®è­˜åˆ¥å­ã€‚*(,)* + +* **displayName**: エンド ・ ユーザーã¸ã®è¡¨ç¤ºã«é©ã—ãŸã€ã“ã®é€£çµ¡å…ˆã®åå‰ã€‚*(,)* + +* **åå‰**: 人ã®åå‰ã®ã™ã¹ã¦ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚’æ ¼ç´ã™ã‚‹ã‚ªãƒ–ジェクト。*(ContactName)* + +* **ニックãƒãƒ¼ãƒ **: 連絡先ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ä½¿ç”¨ã™ã‚‹ã‚«ã‚¸ãƒ¥ã‚¢ãƒ«ãªåå‰ã€‚*(,)* + +* **電話番å·**: 連絡先ã®é›»è©±ç•ªå·ã®é…列。*(ContactField[])* + +* **メール**: 連絡先ã®é›»å­ãƒ¡ãƒ¼ãƒ« アドレスã®é…列。*(ContactField[])* + +* **アドレス**: 連絡先ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã®é…列。*(ContactAddress[])* + +* **ims**: 連絡先㮠IM アドレスã®é…列。*(ContactField[])* + +* **組織**: 連絡先ã®çµ„ç¹”ã®é…列。*(ContactOrganization[])* + +* **誕生日**: 連絡先ã®èª•生日。*(日)* + +* **注**: 連絡先ã«ã¤ã„ã¦ã®ãƒ¡ãƒ¢ã€‚*(,)* + +* **写真**: 連絡先ã®å†™çœŸã®é…列。*(ContactField[])* + +* **カテゴリ**: å–引先担当者ã«é–¢é€£ä»˜ã‘られã¦ã„ã‚‹ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã‚«ãƒ†ã‚´ãƒªã®é…列。*(ContactField[])* + +* **url**: å–引先担当者ã«é–¢é€£ä»˜ã‘られã¦ã„ã‚‹ web ページã®é…列。*(ContactField[])* + +### メソッド + +* **クローン**: æ–°ã—ã„ã‚’è¿”ã—ã¾ã™ `Contact` ã¨å‘¼ã³å‡ºã—å…ƒã®ã‚ªãƒ–ジェクトã®ãƒ‡ã‚£ãƒ¼ãƒ— コピーã§ã‚るオブジェクト㮠`id` プロパティã«è¨­å®š`null`. + +* **削除**: デãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰é€£çµ¡å…ˆã‚’削除ã—ã¾ã™ã€ãれ以外ã®å ´åˆã®ãŸã‚ã®ã‚¨ãƒ©ãƒ¼ コールãƒãƒƒã‚¯ã‚’実行ã™ã‚‹ã€ `ContactError` オブジェクト。 + +* **ä¿å­˜**: デãƒã‚¤ã‚¹ã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«æ–°ã—ã„連絡先をä¿å­˜ã¾ãŸã¯åŒã˜**id**ã‚’æŒã¤é€£çµ¡å…ˆãŒæ—¢ã«å­˜åœ¨ã™ã‚‹å ´åˆã€æ—¢å­˜ã®é€£çµ¡å…ˆã‚’æ›´æ–°ã—ã¾ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### ä¿å­˜ã®ä¾‹ + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### クローンã®ä¾‹ + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### 削除ã®ä¾‹ + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### アンドロイド 2.X ç™– + +* **カテゴリ**: 返㙠2.X ã® Android デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`null`. + +### ブラックベリー 10 ç™– + +* **id**: 連絡先をä¿å­˜ã™ã‚‹ã¨ãã«ã€ãƒ‡ãƒã‚¤ã‚¹ã«ã‚ˆã£ã¦å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¾ã™ã€‚ + +### FirefoxOS ã®ç™– + +* **カテゴリ**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚フィールド**県**・**タイプ**ã‚’è¿”ã™`null` + +* **ims**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **写真**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### iOS ã®ç™– + +* **displayName**: 返㙠iOS ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ `null` ãŒãªã„é™ã‚Šã€ãªã„ `ContactName` 指定ã™ã‚‹ã¨ã€ãã®å ´åˆã¯è¤‡åˆåã€**ニックãƒãƒ¼ãƒ **ã‚’è¿”ã—ã¾ã™ã¾ãŸã¯ `""` ã€ãれãžã‚Œã€‚ + +* **誕生日**: JavaScript ã¨ã—ã¦å…¥åŠ›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ `Date` オブジェクトã€åŒã˜æ–¹æ³•ãŒè¿”ã•れã¾ã™ã€‚ + +* **写真**: アプリケーションã®ä¸€æ™‚ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æ ¼ç´ã•れã¦ã„るイメージã¸ã®ãƒ•ァイル㮠URL ã‚’è¿”ã—ã¾ã™ã€‚一時ディレクトリã®å†…容ã¯ã€ã‚¢ãƒ—リケーションã®çµ‚了時ã«å‰Šé™¤ã•れã¾ã™ã€‚ + +* **カテゴリ**: ã“ã®ãƒ—ロパティã¯ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã€è¿”ã™`null`. + +### Windows Phone 7 㨠8 ç™– + +* **displayName**: 表示åパラメーターã®è¡¨ç¤ºåã¨ç•°ãªã‚‹ãŸã‚ã«æä¾›å€¤ã‚’å–å¾—ã€é€£çµ¡å…ˆã‚’検索ã™ã‚‹ã¨ãã€é€£çµ¡å…ˆã‚’作æˆã™ã‚‹ã¨ã。 + +* **url**: 連絡先を作æˆã™ã‚‹ã¨ãユーザー入力ä¿å­˜ã§ãã€1 ã¤ä»¥ä¸Šã® web アドレス㌠1 ã¤ã ã‘ã€é€£çµ¡å…ˆã‚’検索ã™ã‚‹ã¨ã。 + +* **電話番å·**:*県*オプションã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 *åž‹*ã¯ã€*検索*æ“作ã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 1 ã¤ã ã‘ `phoneNumber` ã¯å„*タイプ*ã®è¨±å¯. + +* **メール**:*県*オプションã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。家庭や個人的ãªãƒ¡ãƒ¼ãƒ«ã¨åŒã˜ã‚¨ãƒ³ãƒˆãƒªã‚’å‚ç…§ã—ã¾ã™ã€‚å„*タイプ*ã® 1 ã¤ã ã‘ã®ã‚¨ãƒ³ãƒˆãƒªãŒè¨±å¯ã•れã¦. + +* **アドレス**: 仕事ã¨ãƒ›ãƒ¼ãƒ /パーソナル*タイプ*ã®ã¿ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚家庭や個人*åž‹*å‚ç…§ã—ã¦åŒã˜ã‚¢ãƒ‰ãƒ¬ã‚¹ エントリ。å„*タイプ*ã® 1 ã¤ã ã‘ã®ã‚¨ãƒ³ãƒˆãƒªãŒè¨±å¯ã•れã¦. + +* **組織**: 1 ã¤ã ã‘ãŒè¨±å¯ã•れã€*県*ã€*タイプ*ã€ãŠã‚ˆã³*部門*ã®å±žæ€§ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。 + +* **注**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **ims**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **誕生日**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **カテゴリ**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +### Windows ã®ç™– + +* **写真**: アプリケーションã®ä¸€æ™‚ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æ ¼ç´ã•れã¦ã„るイメージã¸ã®ãƒ•ァイル㮠URL ã‚’è¿”ã—ã¾ã™ã€‚ + +* **誕生日**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **カテゴリ**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +## ContactAddress + +`ContactAddress` オブジェクトã¯ã€é€£çµ¡å…ˆã® 1 ã¤ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã®ãƒ—ロパティを格ç´ã—ã¾ã™ã€‚ `Contact` オブジェクトã¯ã€`ContactAddress` é…列㮠1 ã¤ä»¥ä¸Šã®ã‚¢ãƒ‰ãƒ¬ã‚¹ãªã©ãŒã‚りã¾ã™ã€‚ + +### プロパティ + +* **県**: ã«è¨­å®šã•れã¦ã„ã‚‹ `true` å ´åˆã¯ã€ã“ã® `ContactAddress` ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŽ¨å¥¨å€¤ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚*(ブール値)* + +* **タイプ**: ãŸã¨ãˆã°ãƒ•ィールドã“れã¯*ホーム*ã®ç¨®é¡žã‚’ç¤ºã™æ–‡å­—列。*(,)* + +* **書å¼è¨­å®š**: 表示用ã«ãƒ•ォーマットã®å®Œå…¨ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã€‚*(,)* + +* **番地**: 完全ãªä½æ‰€ã€‚*(,)* + +* **局所性**: 都市ã¾ãŸã¯å ´æ‰€ã€‚*(,)* + +* **地域**: 状態ã¾ãŸã¯åœ°åŸŸã€‚*(,)* + +* **郵便番å·**: 郵便番å·ã¾ãŸã¯éƒµä¾¿ç•ªå·ã€‚*(,)* + +* **国**: 国ã®åå‰ã€‚*(,)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### 例 + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### アンドロイド 2.X ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„返㙠`false` 2.X ã® Android デãƒã‚¤ã‚¹ã§ã€‚ + +### ブラックベリー 10 ç™– + +* **県**: 戻るã€BlackBerry デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + +* **種類**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚連絡先ã”ã¨ã® 1 ã¤ã ã‘å„*仕事*ã¨*家庭*ã®ç¨®é¡žã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’æ ¼ç´ã§ãã¾ã™ã€‚ + +* **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚ã™ã¹ã¦ã® BlackBerry アドレス フィールドã®é€£çµã‚’è¿”ã—ã¾ã™ã€‚ + +* **番地**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリーã®**使‰€ 1** **使‰€ 2**ã®é€£çµã‚¢ãƒ‰ãƒ¬ã‚¹ フィールドを返ã—ã¾ã™ã€‚ + +* **局所性**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー**市**アドレス ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **地域**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー **stateProvince**アドレス ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **郵便番å·**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー **zipPostal**アドレス ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **国**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ + +### FirefoxOS ã®ç™– + +* **フォーマット**: ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ + +### iOS ã®ç™– + +* **県**: 戻る iOS デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + +* **フォーマット**: ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Windows 8 ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Windows ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +## ContactError + +`ContactError` オブジェクトã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã« `contactError` コールãƒãƒƒã‚¯é–¢æ•°ã‚’通ã˜ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«è¿”ã•れã¾ã™ã€‚ + +### プロパティ + +* **コード**: 次ã®ã„ãšã‚Œã‹ã®å®šç¾©æ¸ˆã¿ã®ã‚¨ãƒ©ãƒ¼ コード。 + +### 定数 + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +`ContactField` オブジェクトã¯é€£çµ¡å…ˆãƒ•ィールドをç·ç§°ã‚’表ã™å†åˆ©ç”¨å¯èƒ½ãªã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã§ã™ã€‚ å„ `ContactField` オブジェクトã«ã¯ `値` `åž‹`ã€ãŠã‚ˆã³ `県` プロパティãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ `連絡先` オブジェクトã¯ã€`ContactField` é…列ã«ã€é›»è©±ç•ªå·ã€ãƒ¡ãƒ¼ãƒ« アドレスãªã©ã®ã„ãã¤ã‹ã®ãƒ—ロパティを格ç´ã—ã¾ã™ã€‚ + +ã»ã¨ã‚“ã©ã®å ´åˆã€`ContactField` オブジェクト㮠**type** 属性ã®ã‚らã‹ã˜ã‚決ã‚られãŸå€¤ã¯ã‚りã¾ã›ã‚“。 ãŸã¨ãˆã°ã€é›»è©±ç•ªå·ãŒ *ホーム*ã€*仕事*ã€*モãƒã‚¤ãƒ«*ã€*iPhone*ã€ã¾ãŸã¯ç‰¹å®šã®ãƒ‡ãƒã‚¤ã‚¹ プラットフォームã®é€£çµ¡å…ˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹ä»–ã®å€¤ã® **åž‹** ã®å€¤ã‚’指定ã§ãã¾ã™ã€‚ ãŸã ã—ã€`連絡先` **ã®å†™çœŸ**] ã®ãƒ•ィールド **ã®ç¨®é¡ž**] フィールドを示ã—ã¾ã™è¿”ã•れる画åƒã®å½¢å¼: **url** **値** 属性 **値** ã‚’ base64 ã§ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã•れãŸã‚¤ãƒ¡ãƒ¼ã‚¸ã®æ–‡å­—列ãŒå«ã¾ã‚Œã‚‹å ´åˆã«å†™çœŸã‚¤ãƒ¡ãƒ¼ã‚¸ã¾ãŸã¯ *base64* ã« URL ãŒå«ã¾ã‚Œã‚‹å ´åˆã€‚ + +### プロパティ + +* **タイプ**: ãŸã¨ãˆã°ãƒ•ィールドã“れã¯*ホーム*ã®ç¨®é¡žã‚’ç¤ºã™æ–‡å­—列。*(,)* + +* **値**: 電話番å·ã‚„é›»å­ãƒ¡ãƒ¼ãƒ« アドレスãªã©ã€ãƒ•ィールドã®å€¤ã€‚*(,)* + +* **県**: ã«è¨­å®šã•れã¦ã„ã‚‹ `true` å ´åˆã¯ã€ã“ã® `ContactField` ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŽ¨å¥¨å€¤ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚*(ブール値)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### 例 + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Android ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### ブラックベリー 10 ç™– + +* **種類**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚電話番å·ã‚’使用ã—ã¾ã™ã€‚ + +* **値**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### iOS ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### Windows8 ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +### Windows ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +## å¾—æ„先コード + +ç•°ãªã‚‹ç¨®é¡ž `Contact` オブジェクトã®åå‰ã«ã¤ã„ã¦ã®æƒ…å ±ã«ã¯ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ + +### プロパティ + +* **フォーマット**: 連絡先ã®å®Œå…¨ãªåå‰ã€‚*(,)* + +* **familyName**: 連絡先ã®å§“。*(,)* + +* **givenName**: 連絡先ã®åå‰ã€‚*(,)* + +* **ミドル ãƒãƒ¼ãƒ **: 連絡先ã®ãƒŸãƒ‰ãƒ« ãƒãƒ¼ãƒ ã€‚*(,)* + +* **honorificPrefix**: 連絡先ã®ãƒ—レフィックス (例*æ°*ã¾ãŸã¯*åšå£«*) *(,)* + +* **honorificSuffix**: 連絡先ã®ã‚µãƒ•ィックス (*å¼è­·å£«*ã®ä¾‹)。*(,)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### 例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android ã®ç™– + +* **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¦ãŠã‚Šã€èª­ã¿å–り専用ã§ã™ã€‚ 連çµã‚’è¿”ã—ã¾ã™ `honorificPrefix` 〠`givenName` 〠`middleName` 〠`familyName` ã¨`honorificSuffix`. + +### ブラックベリー 10 ç™– + +* **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚ブラックベリー **firstName**ã¨**lastName**フィールドã®é€£çµã‚’è¿”ã—ã¾ã™ã€‚ + +* **familyName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリー **[æ°å]**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **givenName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ブラックベリーã®**firstName**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **ミドル ãƒãƒ¼ãƒ **: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **honorificPrefix**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **honorificSuffix**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +### FirefoxOS ã®ç™– + +* **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¦ãŠã‚Šã€èª­ã¿å–り専用ã§ã™ã€‚ 連çµã‚’è¿”ã—ã¾ã™ `honorificPrefix` 〠`givenName` 〠`middleName` 〠`familyName` ã¨`honorificSuffix`. + +### iOS ã®ç™– + +* **フォーマット**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚IOS 複åˆåã‚’è¿”ã—ã¾ã™ãŒã€èª­ã¿å–り専用ã§ã™ã€‚ + +### Windows 8 ã®ç™– + +* **フォーマット**: ã“れã¯ã€ãƒ—ロパティã®åå‰ã—ã€åŒã˜ã§ã™ `displayName` ã¨`nickname` + +* **familyName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **givenName**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **ミドル ãƒãƒ¼ãƒ **: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **honorificPrefix**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **honorificSuffix**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Windows ã®ç™– + +* **フォーマット**: ã¨åŒã˜ã§ã™`displayName` + +## ContactOrganization + +`ContactOrganization` オブジェクトã¯ã€é€£çµ¡å…ˆã®çµ„ç¹”ã®ãƒ—ロパティを格ç´ã—ã¾ã™ã€‚`連絡先` オブジェクトã¯ã€é…列㮠1 ã¤ä»¥ä¸Šã® `ContactOrganization` オブジェクトを格ç´ã—ã¾ã™ã€‚ + +### プロパティ + +* **県**: ã«è¨­å®šã•れã¦ã„ã‚‹ `true` å ´åˆã¯ã€ã“ã® `ContactOrganization` ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŽ¨å¥¨å€¤ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚*(ブール値)* + +* **タイプ**: ãŸã¨ãˆã°ãƒ•ィールドã“れã¯*ホーム*ã®ç¨®é¡žã‚’ç¤ºã™æ–‡å­—列。_(DOMString) + +* **åå‰**: 組織ã®åå‰ã€‚*(,)* + +* **部門**: 契約ã«å‹¤ã‚ã¦ã„る部門。*(,)* + +* **タイトル**: 組織ã®é€£çµ¡å…ˆã®ã‚¿ã‚¤ãƒˆãƒ«ã€‚*(,)* + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows (Windows 8.1 ãŠã‚ˆã³ Windows Phone 8.1 装置ã®ã¿ï¼‰ + +### 例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### アンドロイド 2.X ç™– + +* **県**: 返㙠2.X ã® Android デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + +### ブラックベリー 10 ç™– + +* **県**: 戻るã€BlackBerry デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + +* **タイプ**: 戻るã€BlackBerry デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`null`. + +* **åå‰**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®çµ„ç¹”åã¯ã€**会社**ã®ãƒ–ラックベリーã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **部門**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. + +* **タイトル**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚çµ„ç¹”ã®æœ€åˆã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ãƒ–ラックベリー**æ°å**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +### Firefox OS ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **タイプ**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* **部門**: サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +* フィールドã®**åå‰**ã¨**組織**ã€**æ°å**ã«æ ¼ç´ã•れãŸ**タイトル**. + +### iOS ã®ç™– + +* **県**: 戻る iOS デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`false`. + +* **タイプ**: 戻る iOS デãƒã‚¤ã‚¹ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`null`. + +* **åå‰**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®çµ„ç¹”åã¯ã€iOS **kABPersonOrganizationProperty**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **部門**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®éƒ¨ç½²å㯠iOS **kABPersonDepartmentProperty**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +* **タイトル**: 部分的ã«ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚最åˆã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ã€iOS **kABPersonJobTitleProperty**ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«æ ¼ç´ã•れã¾ã™ã€‚ + +### Windows ã®ç™– + +* **県**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`false`. + +* **タイプ**: サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã‚’è¿”ã™`null`. diff --git a/plugins/cordova-plugin-contacts/doc/ko/README.md b/plugins/cordova-plugin-contacts/doc/ko/README.md new file mode 100644 index 0000000..53e51db --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/ko/README.md @@ -0,0 +1,735 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +ì´ í”ŒëŸ¬ê·¸ì¸ ìž¥ì¹˜ ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ì— ëŒ€ 한 액세스를 제공 하는 글로벌 `navigator.contacts` 개체를 ì •ì˜ í•©ë‹ˆë‹¤. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**경고**: 중요 한 ê°œì¸ ì •ë³´ 보호 문제를 제기 하는 ì—°ë½ì²˜ ë°ì´í„°ì˜ 수집 ë° ì‚¬ìš© 합니다. ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ê°œì¸ ì •ë³´ 보호 ì •ì±… ì‘ìš© 프로그램 ì—°ë½ì²˜ ë°ì´í„°ë¥¼ 사용 하는 방법 ë° ë‹¤ë¥¸ 당사ìžì™€ 함께 공유 하는 여부를 토론 해야 한다. ì—°ë½ì²˜ ì •ë³´ 누구와 통신 하는 ì‚¬ëžŒì´ ì‚¬ëžŒë“¤ ë³´ì—¬ ë•Œë¬¸ì— ë¯¼ê°í•œìœ¼ë¡œ 간주 ë©ë‹ˆë‹¤. ë”°ë¼ì„œ, 애플 리 ì¼€ì´ ì…˜ì˜ ê°œì¸ ì •ë³´ 보호 ì •ì±… ë¿ë§Œ ì•„ë‹ˆë¼ ê°•ë ¥ 하 게 고려해 야 장치 ìš´ì˜ ì²´ì œëŠ” ì´ë ‡ê²Œ ì´ë¯¸ 하지 않는 경우 ì‘ìš© 프로그램 액세스 ë˜ëŠ” ì—°ë½ì²˜ ë°ì´í„°ë¥¼ 사용 하기 ì „ì— ê·¸ëƒ¥--시간 통지를 제공 합니다. ê·¸ 통지는 (예를 들어, **확ì¸** ë° **아니오** ì„ íƒ ì œì‹œ) 하 ì—¬ 사용ìžì˜ 허가 ì·¨ë“ ë¿ë§Œ 아니ë¼, 위ì—서 언급 ëœ ë™ì¼í•œ 정보를 제공 해야 합니다. Note ì¼ë¶€ 애플 리 ì¼€ì´ ì…˜ 장 í„° 그냥--시간 공지 ë° ì—°ë½ì²˜ ë°ì´í„°ì— 액세스 하기 ì „ì— ì‚¬ìš©ìžì˜ 허가 받아야 ì‘ìš© í”„ë¡œê·¸ëž¨ì— í•„ìš”í•  수 있습니다. ì—°ë½ì²˜ ë°ì´í„°ëŠ” 사용ìžì˜ 혼ë™ì„ 방지할 ìˆ˜ì˜ ì‚¬ìš© ë° ì—°ë½ì²˜ ë°ì´í„°ì˜ ì¸ì‹ëœ 오용 명확 하 ê³  ì´í•´ 하기 쉬운 ì‚¬ìš©ìž ê²½í—˜. ìžì„¸í•œ ë‚´ìš©ì€ ê°œì¸ ì •ë³´ 보호 ê°€ì´ë“œë¥¼ 참조 하십시오. + +## 설치 + +코르ë„ë°” 5.0 + (현재 안정ì ì¸ v1.0.0) í•„ìš” + + cordova plugin add cordova-plugin-contacts + + +코르ë„ë°”ì˜ ì´ì „ 버전 **사용** id (오래 ëœ v0.2.16)를 통해 설치할 수 있습니다. + + cordova plugin add org.apache.cordova.contacts + + +ê·¸ê²ƒì€ ë˜í•œ ë°°ìƒ ê³„ì•½ urlì„ í†µí•´ ì§ì ‘ 설치할 수 (불안정) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + +[참고 문서](https://developer.mozilla.org/en-US/Apps/Developing/Manifest)ì— ì„¤ëª… ëœ ëŒ€ë¡œ **www/manifest.webapp**를 만듭니다. 관련 부여할 추가 합니다. [참고 문서](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type)ì— "privileged"-webapp ìœ í˜•ì„ ë³€ê²½ 하려면 필요가 하다. **경고**: 모든 훌륭한 애플 리 ì¼€ì´ ì…˜ ì¸ë¼ì¸ 스í¬ë¦½íŠ¸ë¥¼ 금지 하는 [콘í…츠 보안 ì •ì±…](https://developer.mozilla.org/en-US/Apps/CSP)ì„ ì ìš© 합니다. 다른 방법으로 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì´ˆê¸°í™” 합니다. + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### 윈ë„ìš° 특수 + +**ì´ì „ì— ì°½ 10:** `찾기` ë° `pickContact` 메서드ì—서 반환 하는 모든 ì—°ë½ì²˜ëŠ” ì½ê¸° ì „ìš© ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ìˆ˜ì •í•  수 없습니다. `ì°¾ì„` ë°©ë²•ì€ Windows Phone 8.1 장치ì—ë§Œ 사용할 수 있습니다. + +**윈ë„ìš° 10 ì´ìƒ:** ì—°ë½ì²˜ 저장 ë˜ ê³  ì‘ìš© 프로그램 로컬 ì—°ë½ì²˜ ì €ìž¥ì†Œì— ì €ìž¥ ë©ë‹ˆë‹¤. ì—°ë½ì²˜ë„ ì‚­ì œ ë  ìˆ˜ 있습니다. + +### 윈ë„ìš° 8 ë‹¨ì  + +윈ë„ìš° 8 ì—°ë½ì²˜ëŠ” ì½ê¸° 전용입니다. 코르 ë„ìš° ë°” API ì—°ë½ì²˜ë¥¼ 통해 하지 쿼리/검색할 수 있습니다, ì‚¬ìš©ìž ì•Œë ¤ '사람' 애플 리 ì¼€ì´ ì…˜ì„ ì—´ 것 ì´ë‹¤ contacts.pickContactì— ëŒ€ 한 호출으로 ì—°ë½ì²˜ë¥¼ ì„ íƒ í•˜ ì—¬ ì‚¬ìš©ìž ì—°ë½ì²˜ë¥¼ ì„ íƒ í•´ì•¼ 합니다. 반환 ëœ ì—°ë½ì²˜ëŠ” ì½ê¸° ì „ìš© ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ìˆ˜ì •í•  수 없습니다. + +## navigator.contacts + +### 메서드 + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### 개체 + + * ì—°ë½ì²˜ + * ë‹´ë‹¹ìž ì´ë¦„ + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +`navigator.contacts.create` 메서드는 ë™ê¸°ì  ë° ìƒˆ `ì—°ë½ì²˜` 개체를 반환 합니다. + +ì´ ë©”ì„œë“œëŠ” `Contact.save` 메서드를 호출 해야 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ì— ì—°ë½ì²˜ 개체를 유지 하지 않습니다. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + +### 예를 들어 + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФ 쿼리 ë° `Contact` ê°œì²´ì˜ ë°°ì—´ì„ ë°˜í™˜ `navigator.contacts.find` 메서드를 비ë™ê¸°ì ìœ¼ë¡œ 실행 합니다. ê²°ê³¼ 개체는 **contactSuccess** 매개 변수ì—서 지정한 `contactSuccess` 콜백 í•¨ìˆ˜ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +**contactFields** 매개 변수는 검색 한정ìžë¡œ 사용할 필드를 지정 합니다. 길ì´ê°€ 0 ì¸ **contactFields** 매개 변수가 유효 하 ê³  `ContactError.INVALID_ARGUMENT_ERROR`ì—서 ê²°ê³¼. **contactFields** ê°’ `"*"` 모든 ì—°ë½ì²˜ 필드를 검색 합니다. + +**contactFindOptions.filter** 문ìžì—´ ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ë¥¼ 쿼리할 때 검색 필터로 사용할 수 있습니다. 제공 ëœ, 대/소문ìž, 부분 ê°’ ì¼ì¹˜ **contactFields** 매개 ë³€ìˆ˜ì— ì§€ì • ëœ ê° í•„ë“œì— ì ìš© ë©ë‹ˆë‹¤. *모든* 지정 ëœ í•„ë“œì˜ ì¼ì¹˜ 하는 경우 ì—°ë½ì²˜ 반환 ë©ë‹ˆë‹¤. 사용 하 ì—¬ **contactFindOptions.desiredFields** 매개 변수 ì†ì„± ë¬¸ì˜ ì œì–´ë¥¼ 다시 반환 해야 합니다. + +### 매개 변수 + + * **contactFields**: 검색 한정ìžë¡œ 사용 하는 í•„ë“œì— ë¬¸ì˜. *(DOMString[])* [Required] + + * **contactSuccess**: ì—°ë½ì²˜ ê°œì²´ì˜ ë°°ì—´ì— í‘œì‹œ ë˜ëŠ” 성공 콜백 함수는 ë°ì´í„°ë² ì´ìФì—서 반환 ëœ. [Required] + + * **contactError**: 오류 콜백 í•¨ìˆ˜ì— ì˜¤ë¥˜ê°€ ë°œìƒí•  때 호출 ë©ë‹ˆë‹¤. [ì„ íƒ ì‚¬í•­] + + * **contactFindOptions**: navigator.contacts í•„í„°ë§ ì˜µì…˜ì„ ê²€ìƒ‰ 합니다. [Optional] + + 키 다ìŒê³¼ 같습니다. + + * **filter**: 검색 문ìžì—´ navigator.contacts를 찾는 ë° ì‚¬ìš© 합니다. *(DOMString)* (기본: `""`) + + * **multiple**: 여러 navigator.contacts 찾기 ìž‘ì—…ì„ ë°˜í™˜ 합니다 경우 ê²°ì • 합니다. *(부울)* (기본값: `false`) + + * **desiredFields**: ì—°ë½ì²˜ 필드를 다시 반환 합니다. 지정 ëœ ê²½ìš° ê²°ê³¼ `Contact` 기능 ê°’ë§Œì´ í•„ë“œ 개체입니다. *(DOMString[])* [Optional] + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * ìœˆë„ (Windows Phone 8.1, ìœˆë„ 10) + +### 예를 들어 + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### 윈ë„ìš° 특수 + + * `__contactFields__`ì§€ì› ë˜ì§€ 않으며 무시 ë©ë‹ˆë‹¤. `find`메서드가 í•­ìƒ ì´ë¦„, ì´ë©”ì¼ ì£¼ì†Œ ë˜ëŠ” ì—°ë½ì²˜ì˜ ì „í™” 번호를 ì¼ì¹˜ 하ë„ë¡ ì‹œë„ í•©ë‹ˆë‹¤. + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact` 메서드를 사용 하면 ë‹¨ì¼ ì—°ë½ì²˜ ì„ íƒ ë¬¸ì˜ ì„ íƒ ì‹œìž‘ 합니다. ê²°ê³¼ 개체는 **contactSuccess** 매개 변수ì—서 지정한 `contactSuccess` 콜백 í•¨ìˆ˜ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +### 매개 변수 + + * **contactSuccess**: ë‹¨ì¼ ì—°ë½ì²˜ 개체와 호출 ë˜ëŠ” 성공 콜백 함수. [필수] + + * **contactError**: 오류 콜백 í•¨ìˆ˜ì— ì˜¤ë¥˜ê°€ ë°œìƒí•  때 호출 ë©ë‹ˆë‹¤. [ì„ íƒ ì‚¬í•­] + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * iOS + * Windows Phone 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### 예를 들어 + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## ì—°ë½ì²˜ + +`Contact` 개체는 사용ìžì˜ ì—°ë½ì²˜ë¥¼ 나타냅니다. ì—°ë½ì²˜ ìƒì„± 수, 저장 ë˜ëŠ” 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФì—서 제거 합니다. ì—°ë½ì²˜ë„ 검색할 수 있습니다 (개별ì ìœ¼ë¡œ ë˜ëŠ” ì¼ê´„ì ìœ¼ë¡œ) ë°ì´í„°ë² ì´ìФì—서 `navigator.contacts.find` 메서드를 호출 하 ì—¬. + +**참고**: 모든 ì—°ë½ì²˜ 필드 ìœ„ì— ë‚˜ì—´ ëœ ëª¨ë“  장치 플랫í¼ì—서 ì§€ì› ë©ë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ ê° í”Œëž«í¼ì˜ *단ì ì´* ì„¹ì…˜ì„ í™•ì¸ í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤. + +### ì†ì„± + + * **id**: 글로벌 고유 ì‹ë³„ìž. *(DOMString)* + + * **displayName**: 최종 사용ìžì— 게 í‘œì‹œì— ì í•©ì´ ì—°ë½ì²˜ì˜ ì´ë¦„. *(DOMString)* + + * **ì´ë¦„**: 사람 ì´ë¦„ì˜ ëª¨ë“  구성 요소를 í¬í•¨ 하는 개체. *(ë‹´ë‹¹ìž ì´ë¦„)* + + * **별명**: ìºì£¼ì–¼ ì´ë¦„ ì—°ë½ì²˜ 주소입니다. *(DOMString)* + + * **phoneNumbers**: 모든 ì—°ë½ì²˜ì˜ ì „í™” ë²ˆí˜¸ì˜ ë°°ì—´. *(ContactField[])* + + * **ì´ë©”ì¼**: 모든 ì—°ë½ì²˜ì˜ ì „ìž ë©”ì¼ ì£¼ì†Œì˜ ë°°ì—´. *(ContactField[])* + + * **주소**: 모든 ì—°ë½ì²˜ì˜ 주소 ë°°ì—´. *(ContactAddress[])* + + * **ims**: 모든 ì—°ë½ì²˜ì˜ IM 주소 ë°°ì—´. *(ContactField[])* + + * **ì¡°ì§**: 다양 한 모든 ì—°ë½ì²˜ì˜ ì¡°ì§. *(ContactOrganization[])* + + * **ìƒì¼**: ì—°ë½ì²˜ì˜ ìƒì¼. *(ë‚ ì§œ)* + + * **참고**: ì—°ë½ì²˜ì— 대 한 참고. *(DOMString)* + + * **사진**: ì—°ë½ì²˜ì˜ ì‚¬ì§„ì„ ë°°ì—´. *(ContactField[])* + + * **카테고리**: 모든 ì‚¬ìš©ìž ì •ì˜ ë²”ì£¼ ì—°ë½ì²˜ì— ì—°ê²° ëœ ë°°ì—´. *(ContactField[])* + + * **url**: ì—°ë½ì²˜ì— ì—°ê²° ëœ ì›¹ 페ì´ì§€ì˜ ë°°ì—´. *(ContactField[])* + +### 메서드 + + * **복제**: 새로운 반환 합니다 `Contact` 으로 호출 하는 ê°œì²´ì˜ ì „ì²´ ë³µì‚¬ë³¸ì€ ê°œì²´ëŠ” `id` ì†ì„±ìœ¼ë¡œ 설정`null`. + + * **제거**: 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФì—서 ì—°ë½ì²˜ë¥¼ 제거 합니다, 그렇지 않으면와 오류 ì½œë°±ì„ ì‹¤í–‰ 한 `ContactError` 개체. + + * **저장**: 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ë¥¼ 새 ì—°ë½ì²˜ë¥¼ 저장 ë˜ëŠ” ë™ì¼í•œ **id** 를 가진 ì—°ë½ì²˜ê°€ ì´ë¯¸ 있는 경우 기존 ì—°ë½ì²˜ë¥¼ ì—…ë°ì´íЏ 합니다. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### 예를 들어 저장 + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### 복제 예제 + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### 예제 제거 + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### 안 드 로ì´ë“œ 2.X ë‹¨ì  + + * **카테고리**: 안 드 로ì´ë“œ 2.X 장치, 반환ì—서 ì§€ì› ë˜ì§€ 않습니다`null`. + +### 블랙베리 10 ë‹¨ì  + + * **id**: ì—°ë½ì²˜ë¥¼ 저장 하면 ìž¥ì¹˜ì— í• ë‹¹ 합니다. + +### FirefoxOS 특수 + + * **카테고리**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 필드 **pref** ë° **형ì‹** 반환`null` + + * **ims**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **사진**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +### iOS ë‹¨ì  + + * **displayName**: 반환 iOSì—서 ì§€ì› ë˜ì§€ 않는 `null` ê°€ 아무 `ContactName` 지정 ëœ,ì´ ê²½ìš° 복합 ì´ë¦„, **닉네임** ì„ ë°˜í™˜ 합니다 ë˜ëŠ” `""` , ê°ê°. + + * **ìƒì¼**: ìžë°” 스í¬ë¦½íŠ¸ë¡œ ìž…ë ¥ 해야 합니다 `Date` 개체를 ê°™ì€ ë°©ì‹ìœ¼ë¡œ 반환 ë©ë‹ˆë‹¤. + + * **사진**: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìž„ì‹œ ë””ë ‰í„°ë¦¬ì— ì €ìž¥ ëœ ì´ë¯¸ì§€ íŒŒì¼ URLì„ ë°˜í™˜ 합니다. ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì¢…ë£Œ ë  ë•Œ 임시 ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì€ ì œê±° ë©ë‹ˆë‹¤. + + * **카테고리**:ì´ ì†ì„±ì€ 현재 ì§€ì› ë˜ì§€ 않습니다, 반환`null`. + +### Windows Phone 7, 8 특수 + + * **displayName**: ì—°ë½ì²˜ë¥¼ 만들 때 표시 ì´ë¦„ì—서 표시 ì´ë¦„ 매개 변수 다릅니다 제공 ê°’ 검색 ì—°ë½ì²˜ë¥¼ ì°¾ì„ ë•Œ. + + * **url**: ì—°ë½ì²˜ë¥¼ 만들 때 사용ìžê°€ ìž…ë ¥ì„ í•˜ë‚˜ ì´ìƒì˜ 웹 주소를 저장 하지만 하나만 사용할 수 있는 ì—°ë½ì²˜ë¥¼ 검색할 때. + + * **phoneNumbers**: *pref* ì˜µì…˜ì´ ì§€ì› ë˜ì§€ 않습니다. *형ì‹* *찾기* 작업ì—서 ì§€ì› ë˜ì§€ 않습니다. 단 하나 `phoneNumber` ê° *형ì‹* ì— ëŒ€ 한 허용. + + * **ì´ë©”ì¼**: *pref* ì˜µì…˜ì´ ì§€ì› ë˜ì§€ 않습니다. 가정 ë° ê°œì¸ ë™ì¼í•œ ì´ë©”ì¼ í•­ëª© 참조. ê° *형ì‹* ì— ëŒ€ 한 í•­ëª©ì´ í•˜ë‚˜ë§Œ 허용. + + * **주소**: ì§ìž¥, ë° ê°€ì •/ê°œì¸ *유형*ì„ ì§€ì› í•©ë‹ˆë‹¤. 가정 ë° ê°œì¸ *유형* ë™ì¼í•œ 주소 항목 참조. ê° *형ì‹* ì— ëŒ€ 한 í•­ëª©ì´ í•˜ë‚˜ë§Œ 허용. + + * **ì¡°ì§**: 하나만 허용 ë˜ ê³  *pref*, *유형*ë° *부서* íŠ¹ì„±ì„ ì§€ì› í•˜ì§€ 않습니다. + + * **참고**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **ims**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **ìƒì¼**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **카테고리**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **제거**: 메서드가 ì§€ì› ë˜ì§€ 않습니다 + +### 윈ë„ìš° 특수 + + * **사진**: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìž„ì‹œ ë””ë ‰í„°ë¦¬ì— ì €ìž¥ ëœ ì´ë¯¸ì§€ íŒŒì¼ URLì„ ë°˜í™˜ 합니다. + + * **ìƒì¼**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **카테고리**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **제거**: 메서드는 Windows 10 ì´ìƒ ì—서만 ì§€ì› ë©ë‹ˆë‹¤. + +## ContactAddress + +`ContactAddress` 개체는 ì—°ë½ì²˜ì˜ ë‹¨ì¼ ì£¼ì†Œ ì†ì„±ì„ 저장합니다. `ì—°ë½ì²˜` 개체는 `ContactAddress` ë°°ì—´ì— ìžˆëŠ” 하나 ì´ìƒì˜ 주소를 í¬í•¨í•  수 있습니다. + +### ì†ì„± + + * **pref**: 설정 `true` ì´ ê²½ìš° `ContactAddress` 사용ìžì˜ 기본 ì„¤ì •ëœ ê°’ì´ í¬í•¨ ë©ë‹ˆë‹¤. *(부울)* + + * **유형**: 예를 들어 필드, *홈* ì˜ ì–´ë–¤ 종류를 나타내는 문ìžì—´. *(DOMString)* + + * **í¬ë§·**: ì „ì²´ 주소 표시를 위해 서ì‹ì´ 지정 ëœ. *(DOMString)* + + * **streetAddress**: ì „ì²´ 주소. *(DOMString)* + + * **지역**: 구, êµ° ë˜ëŠ” ë„시. *(DOMString)* + + * **지역**: ìƒíƒœ ë˜ëŠ” 지역. *(DOMString)* + + * **postalCode**: 우편 번호 ë˜ëŠ” 우편 번호. *(DOMString)* + + * **êµ­ê°€**: êµ­ê°€ ì´ë¦„. *(DOMString)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### 예를 들어 + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### 안 드 로ì´ë“œ 2.X ë‹¨ì  + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환 `false` 안 드 로ì´ë“œ 2.X 장치ì—. + +### 블랙베리 10 ë‹¨ì  + + * **pref**: 반환 BlackBerry 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`false`. + + * **유형**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. *작업* ë° *홈* í˜•ì‹ ì£¼ì†Œ ê° ë‹¨ 하나 ì ‘ì´‰ 당 저장할 수 있습니다. + + * **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 모든 ê²€ì€ ë”¸ê¸° 주소 í•„ë“œì˜ ì—°ê²°ì„ ë°˜í™˜í•©ë‹ˆë‹¤. + + * **streetAddress**: ì§€ì›. 블랙베리 **address1** **주소 2** ì˜ ì—°ê²° 주소 필드를 반환합니다. + + * **지역**: ì§€ì›. 블랙베리 **시** 주소 í•„ë“œì— ì €ìž¥ 합니다. + + * **지역**: ì§€ì›. 블랙베리 **stateProvince** 주소 í•„ë“œì— ì €ìž¥ 합니다. + + * **postalCode**: ì§€ì›. 블랙베리 **zipPostal** 주소 í•„ë“œì— ì €ìž¥ 합니다. + + * **êµ­ê°€**: ì§€ì›. + +### FirefoxOS 특수 + + * **í¬ë§·**: 현재 ì§€ì› ë˜ì§€ 않습니다 + +### iOS ë‹¨ì  + + * **pref**: 반환 하는 iOS 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`false`. + + * **í¬ë§·**: 현재 ì§€ì› ë˜ì§€ 않습니다. + +### 윈ë„ìš° 8 ë‹¨ì  + + * **pref**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +### 윈ë„ìš° 특수 + + * **pref**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +## ContactError + +`ContactError` 개체는 오류가 ë°œìƒ í•˜ë©´ `contactError` 콜백 함수를 통해 사용ìžì— 게 반환 ë©ë‹ˆë‹¤. + +### ì†ì„± + + * **코드**: 미리 ì •ì˜ ëœ ì˜¤ë¥˜ 코드 중 하나가 ì•„ëž˜ì— ë‚˜ì—´ ëœ. + +### ìƒìˆ˜ + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +`ContactField` 개체는 재사용 가능한 구성 요소를 나타내는 필드를 ì¼ë°˜ì ìœ¼ë¡œ 문ì˜ìž…니다. ê° `ContactField` 개체ì—는 `value`, `type` ë° `pref` ì†ì„±ì„ í¬í•¨ ë˜ì–´ 있습니다. `Contact` 개체는 ì „í™” 번호 ë° ì´ë©”ì¼ ì£¼ì†Œì™€ ê°™ì€ `ContactField` ë°°ì—´ì—서 몇 가지 ì†ì„±ì„ 저장합니다. + +ëŒ€ë¶€ë¶„ì˜ ê²½ìš°ì—서는 `ContactField` ê°œì²´ì˜ **type** ì†ì„±ì— 대 한 미리 ì •í•´ì§„ëœ ê°’ì´ ì—†ìŠµë‹ˆë‹¤. 예를 들어 ì „í™” 번호 *홈*, *작품*, *모바ì¼*, *ì•„ì´í°* ë˜ëŠ” 특정 장치 플랫í¼ì˜ ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФì—서 ì§€ì› ë˜ëŠ” 다른 ê°’ì˜ **유형** ê°’ì„ ì§€ì •í•  수 있습니다. 그러나, `ì—°ë½ì²˜` **사진** 필드 **유형** 필드 나타냅니다 반환 ëœ ì´ë¯¸ì§€ 형ì‹: **url** **ê°’** 특성 **ê°’** ì´ë¯¸ì§€ base64 ì¸ì½”ë”©ëœ ë¬¸ìžì—´ì„ í¬í•¨ 하는 경우 사진 ì´ë¯¸ì§€ ë˜ëŠ” *base64* URLì´ í¬í•¨ ëœ ê²½ìš°. + +### ì†ì„± + + * **유형**: 예를 들어 필드, *홈* ì˜ ì–´ë–¤ 종류를 나타내는 문ìžì—´ìž…니다. *(DOMString)* + + * **ê°’**: ì „í™” 번호 ë˜ëŠ” ì´ë©”ì¼ ì£¼ì†Œì™€ ê°™ì€ í•„ë“œ ê°’. *(DOMString)* + + * **pref**: 설정 `true` ì´ ê²½ìš° `ContactField` 사용ìžì˜ 기본 ì„¤ì •ëœ ê°’ì´ í¬í•¨ ë©ë‹ˆë‹¤. *(부울)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### 예를 들어 + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### 안 드 로ì´ë“œ ë‹¨ì  + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### 블랙베리 10 ë‹¨ì  + + * **유형**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. ì „í™” ë²ˆí˜¸ì— ëŒ€ 한 사용. + + * **ê°’**: ì§€ì›. + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### iOS ë‹¨ì  + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### Windows8 ë‹¨ì  + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### 윈ë„ìš° 특수 + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +## ë‹´ë‹¹ìž ì´ë¦„ + +여러 ì¢…ë¥˜ì˜ `Contact` ê°œì²´ì˜ ì´ë¦„ì— ëŒ€ 한 정보를 í¬í•¨í•©ë‹ˆë‹¤. + +### ì†ì„± + + * **í¬ë§·**: ì—°ë½ì²˜ì˜ ì „ì²´ ì´ë¦„. *(DOMString)* + + * **familyName**: ì—°ë½ì²˜ì˜ 성. *(DOMString)* + + * **givenName**: ì—°ë½ì²˜ì˜ ì´ë¦„. *(DOMString)* + + * **middleName**: ì—°ë½ì²˜ì˜ 중간 ì´ë¦„ì„. *(DOMString)* + + * **honorificPrefix**: ì—°ë½ì²˜ì˜ ì ‘ë‘사 (예: *미스터* ë˜ëŠ” *닥터*) *(DOMString)* + + * **honorificSuffix**: ì—°ë½ì²˜ì˜ 접미사 ( *esq.*예). *(DOMString)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### 예를 들어 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### 안 드 로ì´ë“œ ë‹¨ì  + + * **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› ë˜ ê³  ì½ê¸° ì „ìš©. ì—°ê²°ì„ ë°˜í™˜ 합니다 `honorificPrefix` , `givenName` , `middleName` , `familyName` , 그리고`honorificSuffix`. + +### 블랙베리 10 ë‹¨ì  + + * **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 블랙베리 **firstName** ë° **lastName** í•„ë“œì˜ ì—°ê²°ì„ ë°˜í™˜í•©ë‹ˆë‹¤. + + * **familyName**: ì§€ì›. 블랙베리 **lastName** í•„ë“œì— ì €ìž¥ 합니다. + + * **givenName**: ì§€ì›. 블랙베리 **firstName** í•„ë“œì— ì €ìž¥ 합니다. + + * **middleName**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **honorificPrefix**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **honorificSuffix**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +### FirefoxOS 특수 + + * **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› ë˜ ê³  ì½ê¸° ì „ìš©. ì—°ê²°ì„ ë°˜í™˜ 합니다 `honorificPrefix` , `givenName` , `middleName` , `familyName` , 그리고`honorificSuffix`. + +### iOS ë‹¨ì  + + * **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. IOS 복합 ì´ë¦„ 반환 하지만 ì½ê¸° 전용입니다. + +### 윈ë„ìš° 8 ë‹¨ì  + + * **형ì‹**: ì´ê²ƒì€ 유ì¼í•œ ì†ì„± ì´ë¦„ê³¼ ë™ì¼ 하다 `displayName` , ë°`nickname` + + * **familyName**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **givenName**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **middleName**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **honorificPrefix**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **honorificSuffix**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +### 윈ë„ìš° 특수 + + * **형ì‹**: ê·¸ê²ƒì€ ë™ì¼`displayName` + +## ContactOrganization + +`ContactOrganization` 개체는 ì—°ë½ì²˜ì˜ ì¡°ì§ ì†ì„±ì„ 저장합니다. `Contact` 개체 ë°°ì—´ì— í•˜ë‚˜ ì´ìƒì˜ `ContactOrganization` 개체를 저장합니다. + +### ì†ì„± + + * **pref**: 설정 `true` ì´ ê²½ìš° `ContactOrganization` 사용ìžì˜ 기본 ì„¤ì •ëœ ê°’ì´ í¬í•¨ ë©ë‹ˆë‹¤. *(부울)* + + * **유형**: 예를 들어 필드, *홈* ì˜ ì–´ë–¤ 종류를 나타내는 문ìžì—´ìž…니다. _(DOMString) + + * **ì´ë¦„**: ì¡°ì§ ì´ë¦„. *(DOMString)* + + * **부서**: ê³„ì•½ì„ ìœ„í•´ ì¼ í•˜ëŠ” 부서. *(DOMString)* + + * **제목**: ì¡°ì§ì—서 ì—°ë½ì²˜ì˜ 제목. *(DOMString)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° (Windows 8.1와 Windows Phone 8.1 소ìžë§Œ 해당) + +### 예를 들어 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### 안 드 로ì´ë“œ 2.X ë‹¨ì  + + * **pref**: 반환 안 드 로ì´ë“œ 2.X ìž¥ì¹˜ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다`false`. + +### 블랙베리 10 ë‹¨ì  + + * **pref**: 반환 블랙베리 ìž¥ì¹˜ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다`false`. + + * **유형**: 반환 블랙베리 ìž¥ì¹˜ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다`null`. + + * **ì´ë¦„**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 ì¡°ì§ ì´ë¦„ 블랙베리 **회사** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + + * **부서**: ì§€ì› ë˜ì§€ 않는 반환`null`. + + * **제목**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 ì¡°ì§ ì œëª© 블랙베리 **jobTitle** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + + * **pref**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **형ì‹**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * **부서**: ì§€ì› ë˜ì§€ ì•ŠìŒ + + * 필드 **ì´ë¦„** ë° **제목** **org** ë° **jobTitle** ì— ì €ìž¥. + +### iOS ë‹¨ì  + + * **pref**: 반환 하는 iOS 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`false`. + + * **유형**: 반환 하는 iOS 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`null`. + + * **ì´ë¦„**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 ì¡°ì§ ì´ë¦„ì€ iOS **kABPersonOrganizationProperty** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + + * **부서**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 부서 ì´ë¦„ì€ iOS **kABPersonDepartmentProperty** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + + * **제목**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 타ì´í‹€ iOS **kABPersonJobTitleProperty** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +### 윈ë„ìš° 특수 + + * **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + + * **형ì‹**: ì§€ì› ë˜ì§€ 않는 반환`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/ko/index.md b/plugins/cordova-plugin-contacts/doc/ko/index.md new file mode 100644 index 0000000..fff221c --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/ko/index.md @@ -0,0 +1,719 @@ + + +# cordova-plugin-contacts + +ì´ í”ŒëŸ¬ê·¸ì¸ ìž¥ì¹˜ ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ì— ëŒ€ 한 액세스를 제공 하는 글로벌 `navigator.contacts` 개체를 ì •ì˜ í•©ë‹ˆë‹¤. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**경고**: 중요 한 ê°œì¸ ì •ë³´ 보호 문제를 제기 하는 ì—°ë½ì²˜ ë°ì´í„°ì˜ 수집 ë° ì‚¬ìš© 합니다. ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ê°œì¸ ì •ë³´ 보호 ì •ì±… ì‘ìš© 프로그램 ì—°ë½ì²˜ ë°ì´í„°ë¥¼ 사용 하는 방법 ë° ë‹¤ë¥¸ 당사ìžì™€ 함께 공유 하는 여부를 토론 해야 한다. ì—°ë½ì²˜ ì •ë³´ 누구와 통신 하는 ì‚¬ëžŒì´ ì‚¬ëžŒë“¤ ë³´ì—¬ ë•Œë¬¸ì— ë¯¼ê°í•œìœ¼ë¡œ 간주 ë©ë‹ˆë‹¤. ë”°ë¼ì„œ, 애플 리 ì¼€ì´ ì…˜ì˜ ê°œì¸ ì •ë³´ 보호 ì •ì±… ë¿ë§Œ ì•„ë‹ˆë¼ ê°•ë ¥ 하 게 고려해 야 장치 ìš´ì˜ ì²´ì œëŠ” ì´ë ‡ê²Œ ì´ë¯¸ 하지 않는 경우 ì‘ìš© 프로그램 액세스 ë˜ëŠ” ì—°ë½ì²˜ ë°ì´í„°ë¥¼ 사용 하기 ì „ì— ê·¸ëƒ¥--시간 통지를 제공 합니다. ê·¸ 통지는 (예를 들어, **확ì¸** ë° **아니오** ì„ íƒ ì œì‹œ) 하 ì—¬ 사용ìžì˜ 허가 ì·¨ë“ ë¿ë§Œ 아니ë¼, 위ì—서 언급 ëœ ë™ì¼í•œ 정보를 제공 해야 합니다. Note ì¼ë¶€ 애플 리 ì¼€ì´ ì…˜ 장 í„° 그냥--시간 공지 ë° ì—°ë½ì²˜ ë°ì´í„°ì— 액세스 하기 ì „ì— ì‚¬ìš©ìžì˜ 허가 받아야 ì‘ìš© í”„ë¡œê·¸ëž¨ì— í•„ìš”í•  수 있습니다. ì—°ë½ì²˜ ë°ì´í„°ëŠ” 사용ìžì˜ 혼ë™ì„ 방지할 ìˆ˜ì˜ ì‚¬ìš© ë° ì—°ë½ì²˜ ë°ì´í„°ì˜ ì¸ì‹ëœ 오용 명확 하 ê³  ì´í•´ 하기 쉬운 ì‚¬ìš©ìž ê²½í—˜. ìžì„¸í•œ ë‚´ìš©ì€ ê°œì¸ ì •ë³´ 보호 ê°€ì´ë“œë¥¼ 참조 하십시오. + +## 설치 + + cordova plugin add cordova-plugin-contacts + + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + +[참고 문서][1]ì— ì„¤ëª… ëœ ëŒ€ë¡œ **www/manifest.webapp**를 만듭니다. 관련 부여할 추가 합니다. [참고 문서][2]ì— "privileged"-webapp ìœ í˜•ì„ ë³€ê²½ 하려면 필요가 하다. **경고**: 모든 훌륭한 애플 리 ì¼€ì´ ì…˜ ì¸ë¼ì¸ 스í¬ë¦½íŠ¸ë¥¼ 금지 하는 [콘í…츠 보안 ì •ì±…][3]ì„ ì ìš© 합니다. 다른 방법으로 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì´ˆê¸°í™” 합니다. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### 윈ë„ìš° 특수 + +`find` ë° `pickContact` 메서드ì—서 반환 하는 모든 ì—°ë½ì²˜ëŠ” ì½ê¸° ì „ìš© ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ìˆ˜ì •í•  수 없습니다. `ì°¾ì„` ë°©ë²•ì€ Windows Phone 8.1 장치ì—ë§Œ 사용할 수 있습니다. + +### 윈ë„ìš° 8 ë‹¨ì  + +윈ë„ìš° 8 ì—°ë½ì²˜ëŠ” ì½ê¸° 전용입니다. 코르 ë„ìš° ë°” API ì—°ë½ì²˜ë¥¼ 통해 하지 쿼리/검색할 수 있습니다, ì‚¬ìš©ìž ì•Œë ¤ '사람' 애플 리 ì¼€ì´ ì…˜ì„ ì—´ 것 ì´ë‹¤ contacts.pickContactì— ëŒ€ 한 호출으로 ì—°ë½ì²˜ë¥¼ ì„ íƒ í•˜ ì—¬ ì‚¬ìš©ìž ì—°ë½ì²˜ë¥¼ ì„ íƒ í•´ì•¼ 합니다. 반환 ëœ ì—°ë½ì²˜ëŠ” ì½ê¸° ì „ìš© ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ìˆ˜ì •í•  수 없습니다. + +## navigator.contacts + +### 메서드 + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### 개체 + +* ì—°ë½ì²˜ +* ë‹´ë‹¹ìž ì´ë¦„ +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## navigator.contacts.create + +`navigator.contacts.create` 메서드는 ë™ê¸°ì  ë° ìƒˆ `ì—°ë½ì²˜` 개체를 반환 합니다. + +ì´ ë©”ì„œë“œëŠ” `Contact.save` 메서드를 호출 해야 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ì— ì—°ë½ì²˜ 개체를 유지 하지 않습니다. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 + +### 예를 들어 + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФ 쿼리 ë° `Contact` ê°œì²´ì˜ ë°°ì—´ì„ ë°˜í™˜ `navigator.contacts.find` 메서드를 비ë™ê¸°ì ìœ¼ë¡œ 실행 합니다. ê²°ê³¼ 개체는 **contactSuccess** 매개 변수ì—서 지정한 `contactSuccess` 콜백 í•¨ìˆ˜ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +**contactFields** 매개 변수는 검색 한정ìžë¡œ 사용할 필드를 지정 합니다. 길ì´ê°€ 0 ì¸ **contactFields** 매개 변수가 유효 하 ê³  `ContactError.INVALID_ARGUMENT_ERROR`ì—서 ê²°ê³¼. **contactFields** ê°’ `"*"` 모든 ì—°ë½ì²˜ 필드를 검색 합니다. + +**contactFindOptions.filter** 문ìžì—´ ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ë¥¼ 쿼리할 때 검색 필터로 사용할 수 있습니다. 제공 ëœ, 대/소문ìž, 부분 ê°’ ì¼ì¹˜ **contactFields** 매개 ë³€ìˆ˜ì— ì§€ì • ëœ ê° í•„ë“œì— ì ìš© ë©ë‹ˆë‹¤. *모든* 지정 ëœ í•„ë“œì˜ ì¼ì¹˜ 하는 경우 ì—°ë½ì²˜ 반환 ë©ë‹ˆë‹¤. 사용 하 ì—¬ **contactFindOptions.desiredFields** 매개 변수 ì†ì„± ë¬¸ì˜ ì œì–´ë¥¼ 다시 반환 해야 합니다. + +### 매개 변수 + +* **contactFields**: 검색 한정ìžë¡œ 사용 하는 í•„ë“œì— ë¬¸ì˜. *(DOMString[])* [Required] + +* **contactSuccess**: ì—°ë½ì²˜ ê°œì²´ì˜ ë°°ì—´ì— í‘œì‹œ ë˜ëŠ” 성공 콜백 함수는 ë°ì´í„°ë² ì´ìФì—서 반환 ëœ. [Required] + +* **contactError**: 오류 콜백 í•¨ìˆ˜ì— ì˜¤ë¥˜ê°€ ë°œìƒí•  때 호출 ë©ë‹ˆë‹¤. [ì„ íƒ ì‚¬í•­] + +* **contactFindOptions**: navigator.contacts í•„í„°ë§ ì˜µì…˜ì„ ê²€ìƒ‰ 합니다. [Optional] + + 키 다ìŒê³¼ 같습니다. + + * **filter**: 검색 문ìžì—´ navigator.contacts를 찾는 ë° ì‚¬ìš© 합니다. *(DOMString)* (기본: `""`) + + * **multiple**: 여러 navigator.contacts 찾기 ìž‘ì—…ì„ ë°˜í™˜ 합니다 경우 ê²°ì • 합니다. *(부울)* (기본값: `false`) + + * **desiredFields**: ì—°ë½ì²˜ 필드를 다시 반환 합니다. 지정 ëœ ê²½ìš° ê²°ê³¼ `Contact` 기능 ê°’ë§Œì´ í•„ë“œ 개체입니다. *(DOMString[])* [Optional] + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° (Windows Phone 8.1 소ìžë§Œ 해당) + +### 예를 들어 + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### 윈ë„ìš° 특수 + +* `__contactFields__`ì§€ì› ë˜ì§€ 않으며 무시 ë©ë‹ˆë‹¤. `find`메서드가 í•­ìƒ ì´ë¦„, ì´ë©”ì¼ ì£¼ì†Œ ë˜ëŠ” ì—°ë½ì²˜ì˜ ì „í™” 번호를 ì¼ì¹˜ 하ë„ë¡ ì‹œë„ í•©ë‹ˆë‹¤. + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact` 메서드를 사용 하면 ë‹¨ì¼ ì—°ë½ì²˜ ì„ íƒ ë¬¸ì˜ ì„ íƒ ì‹œìž‘ 합니다. ê²°ê³¼ 개체는 **contactSuccess** 매개 변수ì—서 지정한 `contactSuccess` 콜백 í•¨ìˆ˜ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +### 매개 변수 + +* **contactSuccess**: ë‹¨ì¼ ì—°ë½ì²˜ 개체와 호출 ë˜ëŠ” 성공 콜백 함수. [필수] + +* **contactError**: 오류 콜백 í•¨ìˆ˜ì— ì˜¤ë¥˜ê°€ ë°œìƒí•  때 호출 ë©ë‹ˆë‹¤. [ì„ íƒ ì‚¬í•­] + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* iOS +* Windows Phone 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### 예를 들어 + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## ì—°ë½ì²˜ + +`Contact` 개체는 사용ìžì˜ ì—°ë½ì²˜ë¥¼ 나타냅니다. ì—°ë½ì²˜ ìƒì„± 수, 저장 ë˜ëŠ” 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФì—서 제거 합니다. ì—°ë½ì²˜ë„ 검색할 수 있습니다 (개별ì ìœ¼ë¡œ ë˜ëŠ” ì¼ê´„ì ìœ¼ë¡œ) ë°ì´í„°ë² ì´ìФì—서 `navigator.contacts.find` 메서드를 호출 하 ì—¬. + +**참고**: 모든 ì—°ë½ì²˜ 필드 ìœ„ì— ë‚˜ì—´ ëœ ëª¨ë“  장치 플랫í¼ì—서 ì§€ì› ë©ë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ ê° í”Œëž«í¼ì˜ *단ì ì´* ì„¹ì…˜ì„ í™•ì¸ í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤. + +### ì†ì„± + +* **id**: 글로벌 고유 ì‹ë³„ìž. *(DOMString)* + +* **displayName**: 최종 사용ìžì— 게 í‘œì‹œì— ì í•©ì´ ì—°ë½ì²˜ì˜ ì´ë¦„. *(DOMString)* + +* **ì´ë¦„**: 사람 ì´ë¦„ì˜ ëª¨ë“  구성 요소를 í¬í•¨ 하는 개체. *(ë‹´ë‹¹ìž ì´ë¦„)* + +* **별명**: ìºì£¼ì–¼ ì´ë¦„ ì—°ë½ì²˜ 주소입니다. *(DOMString)* + +* **phoneNumbers**: 모든 ì—°ë½ì²˜ì˜ ì „í™” ë²ˆí˜¸ì˜ ë°°ì—´. *(ContactField[])* + +* **ì´ë©”ì¼**: 모든 ì—°ë½ì²˜ì˜ ì „ìž ë©”ì¼ ì£¼ì†Œì˜ ë°°ì—´. *(ContactField[])* + +* **주소**: 모든 ì—°ë½ì²˜ì˜ 주소 ë°°ì—´. *(ContactAddress[])* + +* **ims**: 모든 ì—°ë½ì²˜ì˜ IM 주소 ë°°ì—´. *(ContactField[])* + +* **ì¡°ì§**: 다양 한 모든 ì—°ë½ì²˜ì˜ ì¡°ì§. *(ContactOrganization[])* + +* **ìƒì¼**: ì—°ë½ì²˜ì˜ ìƒì¼. *(ë‚ ì§œ)* + +* **참고**: ì—°ë½ì²˜ì— 대 한 참고. *(DOMString)* + +* **사진**: ì—°ë½ì²˜ì˜ ì‚¬ì§„ì„ ë°°ì—´. *(ContactField[])* + +* **카테고리**: 모든 ì‚¬ìš©ìž ì •ì˜ ë²”ì£¼ ì—°ë½ì²˜ì— ì—°ê²° ëœ ë°°ì—´. *(ContactField[])* + +* **url**: ì—°ë½ì²˜ì— ì—°ê²° ëœ ì›¹ 페ì´ì§€ì˜ ë°°ì—´. *(ContactField[])* + +### 메서드 + +* **복제**: 새로운 반환 합니다 `Contact` 으로 호출 하는 ê°œì²´ì˜ ì „ì²´ ë³µì‚¬ë³¸ì€ ê°œì²´ëŠ” `id` ì†ì„±ìœ¼ë¡œ 설정`null`. + +* **제거**: 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФì—서 ì—°ë½ì²˜ë¥¼ 제거 합니다, 그렇지 않으면와 오류 ì½œë°±ì„ ì‹¤í–‰ 한 `ContactError` 개체. + +* **저장**: 장치 ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìŠ¤ë¥¼ 새 ì—°ë½ì²˜ë¥¼ 저장 ë˜ëŠ” ë™ì¼í•œ **id** 를 가진 ì—°ë½ì²˜ê°€ ì´ë¯¸ 있는 경우 기존 ì—°ë½ì²˜ë¥¼ ì—…ë°ì´íЏ 합니다. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### 예를 들어 저장 + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### 복제 예제 + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### 예제 제거 + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### 안 드 로ì´ë“œ 2.X ë‹¨ì  + +* **카테고리**: 안 드 로ì´ë“œ 2.X 장치, 반환ì—서 ì§€ì› ë˜ì§€ 않습니다`null`. + +### 블랙베리 10 ë‹¨ì  + +* **id**: ì—°ë½ì²˜ë¥¼ 저장 하면 ìž¥ì¹˜ì— í• ë‹¹ 합니다. + +### FirefoxOS 특수 + +* **카테고리**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 필드 **pref** ë° **형ì‹** 반환`null` + +* **ims**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **사진**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +### iOS ë‹¨ì  + +* **displayName**: 반환 iOSì—서 ì§€ì› ë˜ì§€ 않는 `null` ê°€ 아무 `ContactName` 지정 ëœ,ì´ ê²½ìš° 복합 ì´ë¦„, **닉네임** ì„ ë°˜í™˜ 합니다 ë˜ëŠ” `""` , ê°ê°. + +* **ìƒì¼**: ìžë°” 스í¬ë¦½íŠ¸ë¡œ ìž…ë ¥ 해야 합니다 `Date` 개체를 ê°™ì€ ë°©ì‹ìœ¼ë¡œ 반환 ë©ë‹ˆë‹¤. + +* **사진**: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìž„ì‹œ ë””ë ‰í„°ë¦¬ì— ì €ìž¥ ëœ ì´ë¯¸ì§€ íŒŒì¼ URLì„ ë°˜í™˜ 합니다. ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì¢…ë£Œ ë  ë•Œ 임시 ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì€ ì œê±° ë©ë‹ˆë‹¤. + +* **카테고리**:ì´ ì†ì„±ì€ 현재 ì§€ì› ë˜ì§€ 않습니다, 반환`null`. + +### Windows Phone 7, 8 특수 + +* **displayName**: ì—°ë½ì²˜ë¥¼ 만들 때 표시 ì´ë¦„ì—서 표시 ì´ë¦„ 매개 변수 다릅니다 제공 ê°’ 검색 ì—°ë½ì²˜ë¥¼ ì°¾ì„ ë•Œ. + +* **url**: ì—°ë½ì²˜ë¥¼ 만들 때 사용ìžê°€ ìž…ë ¥ì„ í•˜ë‚˜ ì´ìƒì˜ 웹 주소를 저장 하지만 하나만 사용할 수 있는 ì—°ë½ì²˜ë¥¼ 검색할 때. + +* **phoneNumbers**: *pref* ì˜µì…˜ì´ ì§€ì› ë˜ì§€ 않습니다. *형ì‹* *찾기* 작업ì—서 ì§€ì› ë˜ì§€ 않습니다. 단 하나 `phoneNumber` ê° *형ì‹* ì— ëŒ€ 한 허용. + +* **ì´ë©”ì¼**: *pref* ì˜µì…˜ì´ ì§€ì› ë˜ì§€ 않습니다. 가정 ë° ê°œì¸ ë™ì¼í•œ ì´ë©”ì¼ í•­ëª© 참조. ê° *형ì‹* ì— ëŒ€ 한 í•­ëª©ì´ í•˜ë‚˜ë§Œ 허용. + +* **주소**: ì§ìž¥, ë° ê°€ì •/ê°œì¸ *유형*ì„ ì§€ì› í•©ë‹ˆë‹¤. 가정 ë° ê°œì¸ *유형* ë™ì¼í•œ 주소 항목 참조. ê° *형ì‹* ì— ëŒ€ 한 í•­ëª©ì´ í•˜ë‚˜ë§Œ 허용. + +* **ì¡°ì§**: 하나만 허용 ë˜ ê³  *pref*, *유형*ë° *부서* íŠ¹ì„±ì„ ì§€ì› í•˜ì§€ 않습니다. + +* **참고**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **ims**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **ìƒì¼**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **카테고리**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +### 윈ë„ìš° 특수 + +* **사진**: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìž„ì‹œ ë””ë ‰í„°ë¦¬ì— ì €ìž¥ ëœ ì´ë¯¸ì§€ íŒŒì¼ URLì„ ë°˜í™˜ 합니다. + +* **ìƒì¼**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **카테고리**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +## ContactAddress + +`ContactAddress` 개체는 ì—°ë½ì²˜ì˜ ë‹¨ì¼ ì£¼ì†Œ ì†ì„±ì„ 저장합니다. `ì—°ë½ì²˜` 개체는 `ContactAddress` ë°°ì—´ì— ìžˆëŠ” 하나 ì´ìƒì˜ 주소를 í¬í•¨í•  수 있습니다. + +### ì†ì„± + +* **pref**: 설정 `true` ì´ ê²½ìš° `ContactAddress` 사용ìžì˜ 기본 ì„¤ì •ëœ ê°’ì´ í¬í•¨ ë©ë‹ˆë‹¤. *(부울)* + +* **유형**: 예를 들어 필드, *홈* ì˜ ì–´ë–¤ 종류를 나타내는 문ìžì—´. *(DOMString)* + +* **í¬ë§·**: ì „ì²´ 주소 표시를 위해 서ì‹ì´ 지정 ëœ. *(DOMString)* + +* **streetAddress**: ì „ì²´ 주소. *(DOMString)* + +* **지역**: 구, êµ° ë˜ëŠ” ë„시. *(DOMString)* + +* **지역**: ìƒíƒœ ë˜ëŠ” 지역. *(DOMString)* + +* **postalCode**: 우편 번호 ë˜ëŠ” 우편 번호. *(DOMString)* + +* **êµ­ê°€**: êµ­ê°€ ì´ë¦„. *(DOMString)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### 예를 들어 + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### 안 드 로ì´ë“œ 2.X ë‹¨ì  + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환 `false` 안 드 로ì´ë“œ 2.X 장치ì—. + +### 블랙베리 10 ë‹¨ì  + +* **pref**: 반환 BlackBerry 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`false`. + +* **유형**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. *작업* ë° *홈* í˜•ì‹ ì£¼ì†Œ ê° ë‹¨ 하나 ì ‘ì´‰ 당 저장할 수 있습니다. + +* **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 모든 ê²€ì€ ë”¸ê¸° 주소 í•„ë“œì˜ ì—°ê²°ì„ ë°˜í™˜í•©ë‹ˆë‹¤. + +* **streetAddress**: ì§€ì›. 블랙베리 **address1** **주소 2** ì˜ ì—°ê²° 주소 필드를 반환합니다. + +* **지역**: ì§€ì›. 블랙베리 **시** 주소 í•„ë“œì— ì €ìž¥ 합니다. + +* **지역**: ì§€ì›. 블랙베리 **stateProvince** 주소 í•„ë“œì— ì €ìž¥ 합니다. + +* **postalCode**: ì§€ì›. 블랙베리 **zipPostal** 주소 í•„ë“œì— ì €ìž¥ 합니다. + +* **êµ­ê°€**: ì§€ì›. + +### FirefoxOS 특수 + +* **í¬ë§·**: 현재 ì§€ì› ë˜ì§€ 않습니다 + +### iOS ë‹¨ì  + +* **pref**: 반환 하는 iOS 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`false`. + +* **í¬ë§·**: 현재 ì§€ì› ë˜ì§€ 않습니다. + +### 윈ë„ìš° 8 ë‹¨ì  + +* **pref**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +### 윈ë„ìš° 특수 + +* **pref**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +## ContactError + +`ContactError` 개체는 오류가 ë°œìƒ í•˜ë©´ `contactError` 콜백 함수를 통해 사용ìžì— 게 반환 ë©ë‹ˆë‹¤. + +### ì†ì„± + +* **코드**: 미리 ì •ì˜ ëœ ì˜¤ë¥˜ 코드 중 하나가 ì•„ëž˜ì— ë‚˜ì—´ ëœ. + +### ìƒìˆ˜ + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +`ContactField` 개체는 재사용 가능한 구성 요소를 나타내는 필드를 ì¼ë°˜ì ìœ¼ë¡œ 문ì˜ìž…니다. ê° `ContactField` 개체ì—는 `value`, `type` ë° `pref` ì†ì„±ì„ í¬í•¨ ë˜ì–´ 있습니다. `Contact` 개체는 ì „í™” 번호 ë° ì´ë©”ì¼ ì£¼ì†Œì™€ ê°™ì€ `ContactField` ë°°ì—´ì—서 몇 가지 ì†ì„±ì„ 저장합니다. + +ëŒ€ë¶€ë¶„ì˜ ê²½ìš°ì—서는 `ContactField` ê°œì²´ì˜ **type** ì†ì„±ì— 대 한 미리 ì •í•´ì§„ëœ ê°’ì´ ì—†ìŠµë‹ˆë‹¤. 예를 들어 ì „í™” 번호 *홈*, *작품*, *모바ì¼*, *ì•„ì´í°* ë˜ëŠ” 특정 장치 플랫í¼ì˜ ì—°ë½ì²˜ ë°ì´í„°ë² ì´ìФì—서 ì§€ì› ë˜ëŠ” 다른 ê°’ì˜ **유형** ê°’ì„ ì§€ì •í•  수 있습니다. 그러나, `ì—°ë½ì²˜` **사진** 필드 **유형** 필드 나타냅니다 반환 ëœ ì´ë¯¸ì§€ 형ì‹: **url** **ê°’** 특성 **ê°’** ì´ë¯¸ì§€ base64 ì¸ì½”ë”©ëœ ë¬¸ìžì—´ì„ í¬í•¨ 하는 경우 사진 ì´ë¯¸ì§€ ë˜ëŠ” *base64* URLì´ í¬í•¨ ëœ ê²½ìš°. + +### ì†ì„± + +* **유형**: 예를 들어 필드, *홈* ì˜ ì–´ë–¤ 종류를 나타내는 문ìžì—´ìž…니다. *(DOMString)* + +* **ê°’**: ì „í™” 번호 ë˜ëŠ” ì´ë©”ì¼ ì£¼ì†Œì™€ ê°™ì€ í•„ë“œ ê°’. *(DOMString)* + +* **pref**: 설정 `true` ì´ ê²½ìš° `ContactField` 사용ìžì˜ 기본 ì„¤ì •ëœ ê°’ì´ í¬í•¨ ë©ë‹ˆë‹¤. *(부울)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### 예를 들어 + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### 안 드 로ì´ë“œ ë‹¨ì  + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### 블랙베리 10 ë‹¨ì  + +* **유형**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. ì „í™” ë²ˆí˜¸ì— ëŒ€ 한 사용. + +* **ê°’**: ì§€ì›. + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### iOS ë‹¨ì  + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### Windows8 ë‹¨ì  + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +### 윈ë„ìš° 특수 + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +## ë‹´ë‹¹ìž ì´ë¦„ + +여러 ì¢…ë¥˜ì˜ `Contact` ê°œì²´ì˜ ì´ë¦„ì— ëŒ€ 한 정보를 í¬í•¨í•©ë‹ˆë‹¤. + +### ì†ì„± + +* **í¬ë§·**: ì—°ë½ì²˜ì˜ ì „ì²´ ì´ë¦„. *(DOMString)* + +* **familyName**: ì—°ë½ì²˜ì˜ 성. *(DOMString)* + +* **givenName**: ì—°ë½ì²˜ì˜ ì´ë¦„. *(DOMString)* + +* **middleName**: ì—°ë½ì²˜ì˜ 중간 ì´ë¦„ì„. *(DOMString)* + +* **honorificPrefix**: ì—°ë½ì²˜ì˜ ì ‘ë‘사 (예: *미스터* ë˜ëŠ” *닥터*) *(DOMString)* + +* **honorificSuffix**: ì—°ë½ì²˜ì˜ 접미사 ( *esq.*예). *(DOMString)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### 예를 들어 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### 안 드 로ì´ë“œ ë‹¨ì  + +* **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› ë˜ ê³  ì½ê¸° ì „ìš©. ì—°ê²°ì„ ë°˜í™˜ 합니다 `honorificPrefix` , `givenName` , `middleName` , `familyName` , 그리고`honorificSuffix`. + +### 블랙베리 10 ë‹¨ì  + +* **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 블랙베리 **firstName** ë° **lastName** í•„ë“œì˜ ì—°ê²°ì„ ë°˜í™˜í•©ë‹ˆë‹¤. + +* **familyName**: ì§€ì›. 블랙베리 **lastName** í•„ë“œì— ì €ìž¥ 합니다. + +* **givenName**: ì§€ì›. 블랙베리 **firstName** í•„ë“œì— ì €ìž¥ 합니다. + +* **middleName**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **honorificPrefix**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **honorificSuffix**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +### FirefoxOS 특수 + +* **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› ë˜ ê³  ì½ê¸° ì „ìš©. ì—°ê²°ì„ ë°˜í™˜ 합니다 `honorificPrefix` , `givenName` , `middleName` , `familyName` , 그리고`honorificSuffix`. + +### iOS ë‹¨ì  + +* **í¬ë§·**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. IOS 복합 ì´ë¦„ 반환 하지만 ì½ê¸° 전용입니다. + +### 윈ë„ìš° 8 ë‹¨ì  + +* **형ì‹**: ì´ê²ƒì€ 유ì¼í•œ ì†ì„± ì´ë¦„ê³¼ ë™ì¼ 하다 `displayName` , ë°`nickname` + +* **familyName**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **givenName**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **middleName**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **honorificPrefix**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **honorificSuffix**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +### 윈ë„ìš° 특수 + +* **형ì‹**: ê·¸ê²ƒì€ ë™ì¼`displayName` + +## ContactOrganization + +`ContactOrganization` 개체는 ì—°ë½ì²˜ì˜ ì¡°ì§ ì†ì„±ì„ 저장합니다. `Contact` 개체 ë°°ì—´ì— í•˜ë‚˜ ì´ìƒì˜ `ContactOrganization` 개체를 저장합니다. + +### ì†ì„± + +* **pref**: 설정 `true` ì´ ê²½ìš° `ContactOrganization` 사용ìžì˜ 기본 ì„¤ì •ëœ ê°’ì´ í¬í•¨ ë©ë‹ˆë‹¤. *(부울)* + +* **유형**: 예를 들어 필드, *홈* ì˜ ì–´ë–¤ 종류를 나타내는 문ìžì—´ìž…니다. _(DOMString) + +* **ì´ë¦„**: ì¡°ì§ ì´ë¦„. *(DOMString)* + +* **부서**: ê³„ì•½ì„ ìœ„í•´ ì¼ í•˜ëŠ” 부서. *(DOMString)* + +* **제목**: ì¡°ì§ì—서 ì—°ë½ì²˜ì˜ 제목. *(DOMString)* + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° (Windows 8.1와 Windows Phone 8.1 소ìžë§Œ 해당) + +### 예를 들어 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### 안 드 로ì´ë“œ 2.X ë‹¨ì  + +* **pref**: 반환 안 드 로ì´ë“œ 2.X ìž¥ì¹˜ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다`false`. + +### 블랙베리 10 ë‹¨ì  + +* **pref**: 반환 블랙베리 ìž¥ì¹˜ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다`false`. + +* **유형**: 반환 블랙베리 ìž¥ì¹˜ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다`null`. + +* **ì´ë¦„**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 ì¡°ì§ ì´ë¦„ 블랙베리 **회사** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +* **부서**: ì§€ì› ë˜ì§€ 않는 반환`null`. + +* **제목**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 ì¡°ì§ ì œëª© 블랙베리 **jobTitle** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + +* **pref**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **형ì‹**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* **부서**: ì§€ì› ë˜ì§€ ì•ŠìŒ + +* 필드 **ì´ë¦„** ë° **제목** **org** ë° **jobTitle** ì— ì €ìž¥. + +### iOS ë‹¨ì  + +* **pref**: 반환 하는 iOS 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`false`. + +* **유형**: 반환 하는 iOS 장치ì—서 ì§€ì› ë˜ì§€ 않습니다`null`. + +* **ì´ë¦„**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 ì¡°ì§ ì´ë¦„ì€ iOS **kABPersonOrganizationProperty** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +* **부서**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 부서 ì´ë¦„ì€ iOS **kABPersonDepartmentProperty** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +* **제목**: 부분ì ìœ¼ë¡œ ì§€ì› í•©ë‹ˆë‹¤. 첫 번째 타ì´í‹€ iOS **kABPersonJobTitleProperty** í•„ë“œì— ì €ìž¥ ë©ë‹ˆë‹¤. + +### 윈ë„ìš° 특수 + +* **pref**: ì§€ì› ë˜ì§€ 않는 반환`false`. + +* **형ì‹**: ì§€ì› ë˜ì§€ 않는 반환`null`. diff --git a/plugins/cordova-plugin-contacts/doc/pl/README.md b/plugins/cordova-plugin-contacts/doc/pl/README.md new file mode 100644 index 0000000..43111ca --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/pl/README.md @@ -0,0 +1,735 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +Ten plugin definiuje obiekt globalny `navigator.contacts`, która zapewnia dostÄ™p do bazy danych kontaktów urzÄ…dzenia. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**Ostrzeżenie**: zbierania i wykorzystywania danych kontaktowych podnosi kwestie prywatnoÅ›ci ważne. Polityka prywatnoÅ›ci danej aplikacji należy Dyskutować, jak aplikacja używa danych kontaktowych i czy jest on dzielony z innymi stronami. Informacje kontaktowe uznaje wrażliwych, ponieważ ukazuje ludzi, z którymi osoba komunikuje siÄ™. W zwiÄ…zku z tym oprócz aplikacji prywatnoÅ›ci, zdecydowanie zaleca siÄ™ zapewnienie just-in czas wypowiedzenia zanim aplikacja uzyskuje dostÄ™p do lub używa danych kontaktowych, jeÅ›li system operacyjny urzÄ…dzenia nie robi już. Å»e ogÅ‚oszenie powinno zawierać te same informacje, o których wspomniano powyżej, jak również uzyskanie uprawnienia użytkownika (np. poprzez przedstawianie wyborów **OK** i **Nie dziÄ™ki**). Należy pamiÄ™tać, że niektóre platformy aplikacji może wymagać aplikacji powiadomienia just-in czas i uzyskać uprawnienia użytkownika przed uzyskaniem dostÄ™pu do danych kontaktowych. Jasne i Å‚atwe do zrozumienia użytkownika doÅ›wiadczenie, wykorzystanie kontaktów danych pomaga uniknąć nieporozumieÅ„ użytkownik i postrzegane nadużycia danych kontaktowych. Aby uzyskać wiÄ™cej informacji zobacz przewodnik prywatnoÅ›ci. + +## Instalacja + +Wymaga to cordova 5.0 + (obecny v1.0.0 stabilne) + + cordova plugin add cordova-plugin-contacts + + +Starsze wersje cordova nadal można zainstalować za pomocÄ… identyfikatora **przestarzaÅ‚e** (starych v0.2.16) + + cordova plugin add org.apache.cordova.contacts + + +Jest również możliwość instalacji za poÅ›rednictwem repo url bezpoÅ›rednio (niestabilny) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### Firefox OS dziwactwa + +Tworzenie **www/manifest.webapp**, jak opisano w [Dokumentach Manifest](https://developer.mozilla.org/en-US/Apps/Developing/Manifest). Dodaj odpowiednie permisions. Istnieje również potrzeba zmienić typ webapp do "privileged" - [Manifest dokumenty](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type). **Ostrzeżenie**: wszystkie uprzywilejowany apps egzekwowania [TreÅ›ci polityki bezpieczeÅ„stwa](https://developer.mozilla.org/en-US/Apps/CSP), który zabrania skrypt. Zainicjować aplikacji w inny sposób. + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows dziwactwa + +**Przed Windows 10:** Wszelkie kontakty wróciÅ‚ z metody `znaleźć` i `pickContact` sÄ… tylko do odczytu, wiÄ™c aplikacja nie mogÄ… ich modyfikować. `find` metody dostÄ™pne tylko na urzÄ…dzenia Windows Phone 8.1. + +**Windows 10 i powyżej:** Kontakty mogÄ… być zapisane i zostanÄ… zapisane do pamiÄ™ci lokalnej aplikacji Kontakty. Kontakty mogÄ… również zostanÄ… usuniÄ™te. + +### Windows 8 dziwactwa + +Windows 8 kontaktów sÄ… tylko do odczytu. Poprzez kontakty Cordova API sÄ… nie queryable/wyszukiwania, należy poinformować użytkownika wybrać kontakt jako wezwanie do contacts.pickContact, która zostanie otwarta aplikacja 'Ludzie', gdzie użytkownik musi wybrać kontakt. Wszelkie kontakty, zwracane sÄ… tylko do odczytu, wiÄ™c aplikacja nie mogÄ… ich modyfikować. + +## Navigator.Contacts + +### Metody + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### Obiekty + + * Contact + * ContactName + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +Metoda `navigator.contacts.create` jest synchroniczna i zwraca nowy obiekt `Contact`. + +Ta metoda nie zachowuje kontakt obiektu bazy danych kontaktów urzÄ…dzenie, dla którego należy wywoÅ‚ać metodÄ™ `Contact.save`. + +### ObsÅ‚ugiwane platformy + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + +### PrzykÅ‚ad + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Metoda `navigator.contacts.find` asynchronicznie, wykonuje kwerendy bazy danych kontaktów urzÄ…dzenia i tablicÄ™ obiektów `kontaktów`. Wynikowe obiekty sÄ… przekazywane do funkcji wywoÅ‚ania zwrotnego `contactSuccess`, okreÅ›lony przez parametr **contactSuccess**. + +Parametr **contactFields** OkreÅ›la pola, które majÄ… być używane jako kwalifikator Szukaj. Zerowej dÅ‚ugoÅ›ci **contactFields** parametr jest nieprawidÅ‚owy i wyniki w `ContactError.INVALID_ARGUMENT_ERROR`. **ContactFields** wartość `"*"` przeszukuje wszystkie kontakt z pola. + +CiÄ…g **contactFindOptions.filter** może sÅ‚użyć jako filtr wyszukiwania, gdy kwerenda bazy danych kontaktów. JeÅ›li dostarczone, przypadek-niewrażliwe, częściowej wartoÅ›ci mecz jest stosowane do każdego pola okreÅ›lony w parametrze **contactFields**. JeÅ›li ma odpowiednika dla *każdego* pola okreÅ›lony, zwracany jest kontakt. Użycie **contactFindOptions.desiredFields** parametr do kontroli, które kontakt wÅ‚aÅ›ciwoÅ›ci muszÄ… zostać zwrócone ponownie. + +### Parametry + + * **contactFields**: kontakt z pól do wykorzystania jako kwalifikator Szukaj. *(DOMString[])* [Required] + + * **contactSuccess**: sukcesu funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywane z tablicy obiektów kontaktów zwracane z bazy danych. [Required] + + * **contactError**: Błąd funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywana, gdy wystÄ…pi błąd. [Opcjonalnie] + + * **contactFindOptions**: Szukaj opcji filtrowania navigator.contacts. [Optional] + + Klucze obejmuje: + + * **filter**: ciÄ…g wyszukiwania umożliwia znalezienie navigator.contacts. *(DOMString)* (DomyÅ›lnie: `""`) + + * **multiple**: okreÅ›la, czy operacja Znajdź zwraca wiele navigator.contacts. *(Wartość logiczna)* (DomyÅ›lnie: `false`) + + * **desiredFields**: kontakt z pola, aby być zwrócona. JeÅ›li okreÅ›lony, wynikowy `kontakt` obiekt tylko funkcje wartoÅ›ci tych pól. *(DOMString[])* [Optional] + +### ObsÅ‚ugiwane platformy + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Systemu Windows (Windows Phone 8.1 i Windows 10) + +### PrzykÅ‚ad + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows dziwactwa + + * `__contactFields__`nie jest obsÅ‚ugiwane i zostanie zignorowana. `find`Metoda zawsze bÄ™dzie próbowaÅ‚ dopasować nazwÄ™, adres e-mail lub numer telefonu kontaktu. + +## navigator.contacts.pickContact + +Metoda `navigator.contacts.pickContact` uruchamia próbnika kontakt, wybierz jeden kontaktem. Wynikowy obiekt jest przekazywany do funkcji wywoÅ‚ania zwrotnego `contactSuccess`, okreÅ›lony przez parametr **contactSuccess**. + +### Parametry + + * **contactSuccess**: sukcesu funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywane z jednego obiektu kontakt. [Wymagane] + + * **contactError**: Błąd funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywana, gdy wystÄ…pi błąd. [Opcjonalnie] + +### ObsÅ‚ugiwane platformy + + * Android + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### PrzykÅ‚ad + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Contact + +`Contact` obiekt reprezentuje informacje kontaktowe. Kontakty mogÄ… być tworzone, przechowywane lub usuniÄ™te z bazy danych kontaktów urzÄ…dzenia. Kontakty można również pobrać (pojedynczo lub zbiorczo) bazy danych przez wywoÅ‚anie metody `navigator.contacts.find`. + +**Uwaga**: nie wszystkie pola kontaktowe wymienione powyżej sÄ… obsÅ‚ugiwane na każdej platformie urzÄ…dzenia. ProszÄ™ sprawdzić każdej platformy *dziwactw* sekcji szczegółów. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Identyfikator**: unikatowy identyfikator globalny. *(DOMString)* + + * **displayName**: Nazwa tego kontaktu, nadaje siÄ™ do wyÅ›wietlania użytkownikom koÅ„cowym. *(DOMString)* + + * **Nazwa**: obiekt zawierajÄ…cy wszystkie skÅ‚adniki nazwy osób. *(Przedstawiciel)* + + * **nick**: dorywczo nazwy na adres kontakt. *(DOMString)* + + * **numery telefon≤w**: tablica numerów telefonów kontaktowych. *(ContactField[])* + + * **e-maile**: tablica adresów e-mail kontakt. *(ContactField[])* + + * **adresy**: tablica wszystkie adresy. *(ContactAddress[])* + + * **IMS**: tablica kontakt IM adresy. *(ContactField[])* + + * **organizacje**: tablicy wszystkie kontakty organizacji. *(ContactOrganization[])* + + * **urodziny**: urodziny kontakt. *(Data)* + + * **Uwaga**: Uwaga o kontakt. *(DOMString)* + + * **zdjÄ™cia**: tablica zdjÄ™cia kontaktu. *(ContactField[])* + + * **Kategorie**: tablica wszystkie zdefiniowane przez użytkownika kategorie zwiÄ…zane z kontaktem. *(ContactField[])* + + * **adresy URL**: tablicy stron internetowych zwiÄ…zanych z kontaktem. *(ContactField[])* + +### Metody + + * **klon**: Zwraca nowy `Contact` obiekt, który jest kopiÄ… głęboko obiektu wywoÅ‚ujÄ…cego, z `id` WÅ‚aÅ›ciwość zestaw`null`. + + * **UsuÅ„**: usuwa kontakt z bazy danych kontaktów urzÄ…dzenia, w przeciwnym razie wykonuje błąd wywoÅ‚ania zwrotnego z `ContactError` obiektu. + + * **Zapisz**: zapisuje nowy kontakt do bazy kontaktów urzÄ…dzenia, lub aktualizacje już istniejÄ…cy kontakt, jeÅ›li istnieje już kontakt o tym samym **identyfikatorze** . + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### Zapisz przykÅ‚ad + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### PrzykÅ‚ad klon + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Remove przykÅ‚ad + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X dziwactwa + + * **Kategorie**: nie obsÅ‚ugiwane na urzÄ…dzeniach Android 2.X, powrót`null`. + +### Jeżyna 10 dziwactwa + + * **Identyfikator**: przypisany przez urzÄ…dzenie podczas zapisywania kontaktu. + +### OsobliwoÅ›ci FirefoxOS + + * **Kategorie**: częściowo obsÅ‚ugiwane. Pola **pref** i **Typ** wracajÄ…`null` + + * **IMS**: nie obsÅ‚ugiwane + + * **zdjÄ™cia**: nie obsÅ‚ugiwane + +### Dziwactwa iOS + + * **displayName**: nie obsÅ‚ugiwane na iOS, powrót `null` chyba jest nie `ContactName` okreÅ›lony, w którym to przypadku zwraca nazwÄ™ kompozytowe, **nick** lub `""` , odpowiednio. + + * **urodziny**: należy wpisać jako JavaScript `Date` obiektu, tak samo jest zwracany. + + * **zdjÄ™cia**: zwraca adres URL pliku obrazu, który jest przechowywany w katalogu tymczasowego stosowania. Zawartość katalogu tymczasowe sÄ… usuwane, kiedy koÅ„czy pracÄ™ aplikacji. + + * **Kategorie**: Ta wÅ‚aÅ›ciwość obecnie jest nie obsÅ‚ugiwane, powrót`null`. + +### Windows Phone 7 i 8 dziwactwa + + * **displayName**: podczas tworzenia kontaktu, ŹródÅ‚o wartość podana dla parametru nazwy wyÅ›wietlania różni siÄ™ od nazwy wyÅ›wietlanej, gdy znalezienie kontaktu. + + * **adresy URL**: podczas tworzenia kontaktu, użytkownicy mogÄ… wpisać i zapisać wiÄ™cej niż jeden adres sieci web, ale tylko jeden jest dostÄ™pne podczas wyszukiwania kontaktów. + + * **numery telefon≤w**: *pref* opcja nie jest obsÅ‚ugiwana. *Typ* nie jest obsÅ‚ugiwany w operacji *znaleźć* . Jedynym `phoneNumber` jest dozwolone dla każdego *typu*. + + * **e-maile**: *pref* opcja nie jest obsÅ‚ugiwana. Domu i osobiste odwoÅ‚uje siÄ™ w tym samym wpisu email. Dla każdego *typu* dozwolone jest tylko jeden wpis. + + * **adresy**: obsÅ‚uguje tylko pracy i domu/osobisty *typu*. Domowych i osobistych *typu* odwoÅ‚ania tej samej pozycji adres. Dla każdego *typu* dozwolone jest tylko jeden wpis. + + * **organizacje**: tylko jeden jest dozwolone, a nie obsÅ‚uguje * *pref*, *Typ*i* atrybuty. + + * **Uwaga**: nie obsÅ‚ugiwane, powrót`null`. + + * **IMS**: nie obsÅ‚ugiwane, powrót`null`. + + * **urodziny**: nie obsÅ‚ugiwane, powrót`null`. + + * **Kategorie**: nie obsÅ‚ugiwane, powrót`null`. + + * **remove**: Metoda nie jest obsÅ‚ugiwana + +### Windows dziwactwa + + * **zdjÄ™cia**: zwraca adres URL pliku obrazu, który jest przechowywany w katalogu tymczasowego stosowania. + + * **urodziny**: nie obsÅ‚ugiwane, powrót`null`. + + * **Kategorie**: nie obsÅ‚ugiwane, powrót`null`. + + * **remove**: Metoda jest obsÅ‚ugiwana tylko w systemie Windows 10 lub wyżej. + +## ContactAddress + +Obiekt `ContactAddress` przechowuje wÅ‚aÅ›ciwoÅ›ci pojedynczego adresu kontaktu. Obiekt `Contact` może zawierać wiÄ™cej niż jeden adres w tablicy `[ContactAddress]`. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Pref**: zestaw `true` JeÅ›li `ContactAddress` zawiera wartoÅ›ci preferowanych użytkownika. *(wartość logiczna)* + + * **Typ**: string wskazujÄ…cÄ… typ pola, *do domu* na przykÅ‚ad. *(DOMString)* + + * **w formacie**: peÅ‚ny adres w formacie wyÅ›wietlania. *(DOMString)* + + * **adres**: peÅ‚ny adres. *(DOMString)* + + * **miejscowoÅ›ci**: miasta lub miejscowoÅ›ci. *(DOMString)* + + * **region**: PaÅ„stwo lub region. *(DOMString)* + + * **Kod pocztowy**: kod pocztowy lub kod pocztowy. *(DOMString)* + + * **kraj**: nazwÄ™ kraju. *(DOMString)* + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### PrzykÅ‚ad + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X dziwactwa + + * **Pref**: nie obsÅ‚ugiwane, powrót `false` na urzÄ…dzeniach Android 2.X. + +### Jeżyna 10 dziwactwa + + * **Pref**: nie obsÅ‚ugiwane na urzÄ…dzenia BlackBerry, powrót`false`. + + * **Typ**: częściowo obsÅ‚ugiwane. Tylko jeden z *pracy* i *Strona główna* wpisz adresy mogÄ… być przechowywane na kontakt. + + * **w formacie**: częściowo obsÅ‚ugiwane. Zwraca łączenie wszystkich pól adres BlackBerry. + + * **adres**: obsÅ‚ugiwane. Zwraca łączenie BlackBerry **address1** i **Adres2** pola adresu. + + * **miejscowoÅ›ci**: obsÅ‚ugiwane. Przechowywane w polu adres **miasto** BlackBerry. + + * **region**: obsÅ‚ugiwane. Przechowywane w polu adres **stateProvince** BlackBerry. + + * **Kod pocztowy**: obsÅ‚ugiwane. Przechowywane w polu adres **zipPostal** BlackBerry. + + * **kraj**: obsÅ‚ugiwane. + +### OsobliwoÅ›ci FirefoxOS + + * **w formacie**: aktualnie nieobsÅ‚ugiwane + +### Dziwactwa iOS + + * **Pref**: nie obsÅ‚ugiwane urzÄ…dzenia iOS, powrót`false`. + + * **w formacie**: obecnie nie jest obsÅ‚ugiwane. + +### Windows 8 dziwactwa + + * **Pref**: nie obsÅ‚ugiwane + +### Windows dziwactwa + + * **Pref**: nie obsÅ‚ugiwane + +## ContactError + +`ContactError` obiekt jest zwracany użytkownikowi za poÅ›rednictwem funkcji wywoÅ‚ania zwrotnego `contactError`, gdy wystÄ…pi błąd. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Kod**: jeden z kodów błędów wstÄ™pnie zdefiniowanych poniżej. + +### StaÅ‚e + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Obiekt `ContactField` jest skÅ‚adnikiem wielokrotnego użytku, że reprezentuje kontakt pola ogólnie. Każdy obiekt `ContactField` zawiera `wartość`, `Typ` i `pref` WÅ‚aÅ›ciwość. Obiekt `Contact` sklepy kilku wÅ‚aÅ›ciwoÅ›ci w tablicach `[ContactField]`, takich jak numery telefonów i adresy e-mail. + +W wiÄ™kszoÅ›ci przypadków sÄ… nie wczeÅ›niej ustalonych wartoÅ›ci atrybutu **type** obiektu `ContactField`. Na przykÅ‚ad numer telefonu można okreÅ›lić **type** wartoÅ›ci *home*, *work*, *mobile*, *iPhone*, lub jakÄ…kolwiek innÄ… wartość, który jest obsÅ‚ugiwany przez platformÄ™ danego urzÄ…dzenia bazy danych kontaktów. Jednak `Contact` **photos** pola, pole **type** wskazuje format zwrócone obrazu: **url**, gdy **value** atrybut zawiera adres URL, do zdjęć, lub *base64*, gdy **value** zawiera ciÄ…g zakodowany base64 obrazu. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Typ**: ciÄ…g, który wskazuje typ pola, *do domu* , np. *(DOMString)* + + * **wartoÅ›ci**: wartość pola, na przykÅ‚ad adresu e-mail lub numer telefonu. *(DOMString)* + + * **Pref**: zestaw `true` JeÅ›li `ContactField` zawiera wartoÅ›ci preferowanych użytkownika. *(wartość logiczna)* + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### PrzykÅ‚ad + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Dziwactwa Androida + + * **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### Jeżyna 10 dziwactwa + + * **Typ**: częściowo obsÅ‚ugiwane. Używane numery telefonów. + + * **wartość**: obsÅ‚ugiwane. + + * **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### Dziwactwa iOS + + * **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### OsobliwoÅ›ci Windows8 + + * **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### Windows dziwactwa + + * **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +## ContactName + +Zawiera różne rodzaje informacji o nazwÄ™ obiektu `Contact`. + +### WÅ‚aÅ›ciwoÅ›ci + + * **w formacie**: peÅ‚nÄ… nazwÄ™ kontaktu. *(DOMString)* + + * **danych**: nazwisko kontaktu. *(DOMString)* + + * **imiÄ™**: imiÄ™ kontaktu. *(DOMString)* + + * **middleName**: nazwy bliskiego kontaktu. *(DOMString)* + + * **honorificPrefix**: kontakt prefiks (przykÅ‚ad *Pan* lub *Dr*) *(DOMString)* + + * **honorificSuffix**: kontakt sufiks (przykÅ‚ad *Esq.*). *(DOMString)* + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### PrzykÅ‚ad + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Dziwactwa Androida + + * **w formacie**: częściowo obsÅ‚ugiwane i tylko do odczytu. Zwraca skÅ‚ada siÄ™ z `honorificPrefix` , `givenName` , `middleName` , `familyName` , i`honorificSuffix`. + +### Jeżyna 10 dziwactwa + + * **w formacie**: częściowo obsÅ‚ugiwane. Zwraca łączenie pól **imiÄ™** i **nazwisko** BlackBerry. + + * **danych**: obsÅ‚ugiwane. Przechowywane w BlackBerry pola **nazwisko** . + + * **imiÄ™**: obsÅ‚ugiwane. Przechowywane w polu **imiÄ™** BlackBerry. + + * **middleName**: nie obsÅ‚ugiwane, powrót`null`. + + * **honorificPrefix**: nie obsÅ‚ugiwane, powrót`null`. + + * **honorificSuffix**: nie obsÅ‚ugiwane, powrót`null`. + +### OsobliwoÅ›ci FirefoxOS + + * **w formacie**: częściowo obsÅ‚ugiwane i tylko do odczytu. Zwraca skÅ‚ada siÄ™ z `honorificPrefix` , `givenName` , `middleName` , `familyName` , i`honorificSuffix`. + +### Dziwactwa iOS + + * **w formacie**: częściowo obsÅ‚ugiwane. Zwraca iOS nazwy, ale jest tylko do odczytu. + +### Windows 8 dziwactwa + + * **w formacie**: to jest tylko nazwa wÅ‚aÅ›ciwość i jest taka sama, jak `displayName` , i`nickname` + + * **danych**: nie obsÅ‚ugiwane + + * **imiÄ™**: nie obsÅ‚ugiwane + + * **middleName**: nie obsÅ‚ugiwane + + * **honorificPrefix**: nie obsÅ‚ugiwane + + * **honorificSuffix**: nie obsÅ‚ugiwane + +### Windows dziwactwa + + * **w formacie**: jest identyczny z`displayName` + +## ContactOrganization + +Obiekt `ContactOrganization` przechowuje wÅ‚aÅ›ciwoÅ›ci organizacji kontaktu. Obiekt `Contact` sklepy jeden lub wiÄ™cej obiektów `ContactOrganization` w tablicy. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Pref**: zestaw `true` JeÅ›li `ContactOrganization` zawiera wartoÅ›ci preferowanych użytkownika. *(wartość logiczna)* + + * **Typ**: ciÄ…g, który wskazuje typ pola, *do domu* , np. _(DOMString) + + * **Nazwa**: nazwa organizacji. *(DOMString)* + + * **w departamencie**: dziale umowy dziaÅ‚a. *(DOMString)* + + * **tytuÅ‚**: tytuÅ‚ kontaktu w organizacji. *(DOMString)* + +### ObsÅ‚ugiwane platformy + + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Windows (tylko urzÄ…dzenia Windows 8.1 i Windows Phone 8.1) + +### PrzykÅ‚ad + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X dziwactwa + + * **Pref**: nie obsÅ‚ugiwane przez urzÄ…dzenia Android 2.X, powrót`false`. + +### Jeżyna 10 dziwactwa + + * **Pref**: nie obsÅ‚ugiwane przez urzÄ…dzenia BlackBerry, powrót`false`. + + * **Typ**: nie obsÅ‚ugiwane przez urzÄ…dzenia BlackBerry, powrót`null`. + + * **Nazwa**: częściowo obsÅ‚ugiwane. Pierwsza nazwa organizacji sÄ… przechowywane w polu **firma** BlackBerry. + + * **w departamencie**: nie obsÅ‚ugiwane, powrót`null`. + + * **tytuÅ‚**: częściowo obsÅ‚ugiwane. Pierwszy tytuÅ‚ organizacji sÄ… przechowywane w polu **jobTitle** BlackBerry. + +### Firefox OS dziwactwa + + * **Pref**: nie obsÅ‚ugiwane + + * **Typ**: nie obsÅ‚ugiwane + + * **w departamencie**: nie obsÅ‚ugiwane + + * Pola **Nazwa** i **tytuÅ‚** przechowywane w **org** i **jobTitle**. + +### Dziwactwa iOS + + * **Pref**: nie obsÅ‚ugiwane urzÄ…dzenia iOS, powrót`false`. + + * **Typ**: nie obsÅ‚ugiwane urzÄ…dzenia iOS, powrót`null`. + + * **Nazwa**: częściowo obsÅ‚ugiwane. Pierwsza nazwa organizacji sÄ… przechowywane w polu **kABPersonOrganizationProperty** iOS. + + * **w departamencie**: częściowo obsÅ‚ugiwane. Pierwsza nazwa jest przechowywana w polu **kABPersonDepartmentProperty** iOS. + + * **tytuÅ‚**: częściowo obsÅ‚ugiwane. Pierwszy tytuÅ‚ jest przechowywany w polu **kABPersonJobTitleProperty** iOS. + +### Windows dziwactwa + + * **Pref**: nie obsÅ‚ugiwane, powrót`false`. + + * **Typ**: nie obsÅ‚ugiwane, powrót`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/pl/index.md b/plugins/cordova-plugin-contacts/doc/pl/index.md new file mode 100644 index 0000000..49890c7 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/pl/index.md @@ -0,0 +1,719 @@ + + +# cordova-plugin-contacts + +Ten plugin definiuje obiekt globalny `navigator.contacts`, która zapewnia dostÄ™p do bazy danych kontaktów urzÄ…dzenia. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**Ostrzeżenie**: zbierania i wykorzystywania danych kontaktowych podnosi kwestie prywatnoÅ›ci ważne. Polityka prywatnoÅ›ci danej aplikacji należy Dyskutować, jak aplikacja używa danych kontaktowych i czy jest on dzielony z innymi stronami. Informacje kontaktowe uznaje wrażliwych, ponieważ ukazuje ludzi, z którymi osoba komunikuje siÄ™. W zwiÄ…zku z tym oprócz aplikacji prywatnoÅ›ci, zdecydowanie zaleca siÄ™ zapewnienie just-in czas wypowiedzenia zanim aplikacja uzyskuje dostÄ™p do lub używa danych kontaktowych, jeÅ›li system operacyjny urzÄ…dzenia nie robi już. Å»e ogÅ‚oszenie powinno zawierać te same informacje, o których wspomniano powyżej, jak również uzyskanie uprawnienia użytkownika (np. poprzez przedstawianie wyborów **OK** i **Nie dziÄ™ki**). Należy pamiÄ™tać, że niektóre platformy aplikacji może wymagać aplikacji powiadomienia just-in czas i uzyskać uprawnienia użytkownika przed uzyskaniem dostÄ™pu do danych kontaktowych. Jasne i Å‚atwe do zrozumienia użytkownika doÅ›wiadczenie, wykorzystanie kontaktów danych pomaga uniknąć nieporozumieÅ„ użytkownik i postrzegane nadużycia danych kontaktowych. Aby uzyskać wiÄ™cej informacji zobacz przewodnik prywatnoÅ›ci. + +## Instalacja + + cordova plugin add cordova-plugin-contacts + + +### Firefox OS dziwactwa + +Tworzenie **www/manifest.webapp**, jak opisano w [Dokumentach Manifest][1]. Dodaj odpowiednie permisions. Istnieje również potrzeba zmienić typ webapp do "privileged" - [Manifest dokumenty][2]. **Ostrzeżenie**: wszystkie uprzywilejowany apps egzekwowania [TreÅ›ci polityki bezpieczeÅ„stwa][3], który zabrania skrypt. Zainicjować aplikacji w inny sposób. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows dziwactwa + +Wszelkie kontakty wróciÅ‚ z `pickContact` i `find` metody sÄ… tylko do odczytu, wiÄ™c aplikacja nie mogÄ… ich modyfikować. `find` metody dostÄ™pne tylko na urzÄ…dzenia Windows Phone 8.1. + +### Windows 8 dziwactwa + +Windows 8 kontaktów sÄ… tylko do odczytu. Poprzez kontakty Cordova API sÄ… nie queryable/wyszukiwania, należy poinformować użytkownika wybrać kontakt jako wezwanie do contacts.pickContact, która zostanie otwarta aplikacja 'Ludzie', gdzie użytkownik musi wybrać kontakt. Wszelkie kontakty, zwracane sÄ… tylko do odczytu, wiÄ™c aplikacja nie mogÄ… ich modyfikować. + +## Navigator.Contacts + +### Metody + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### Obiekty + +* Kontakt +* Przedstawiciel +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## Navigator.Contacts.Create + +Metoda `navigator.contacts.create` jest synchroniczna i zwraca nowy obiekt `Contact`. + +Ta metoda nie zachowuje kontakt obiektu bazy danych kontaktów urzÄ…dzenie, dla którego należy wywoÅ‚ać metodÄ™ `Contact.save`. + +### ObsÅ‚ugiwane platformy + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 + +### PrzykÅ‚ad + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Metoda `navigator.contacts.find` asynchronicznie, wykonuje kwerendy bazy danych kontaktów urzÄ…dzenia i tablicÄ™ obiektów `kontaktów`. Wynikowe obiekty sÄ… przekazywane do funkcji wywoÅ‚ania zwrotnego `contactSuccess`, okreÅ›lony przez parametr **contactSuccess**. + +Parametr **contactFields** OkreÅ›la pola, które majÄ… być używane jako kwalifikator Szukaj. Zerowej dÅ‚ugoÅ›ci **contactFields** parametr jest nieprawidÅ‚owy i wyniki w `ContactError.INVALID_ARGUMENT_ERROR`. **ContactFields** wartość `"*"` przeszukuje wszystkie kontakt z pola. + +CiÄ…g **contactFindOptions.filter** może sÅ‚użyć jako filtr wyszukiwania, gdy kwerenda bazy danych kontaktów. JeÅ›li dostarczone, przypadek-niewrażliwe, częściowej wartoÅ›ci mecz jest stosowane do każdego pola okreÅ›lony w parametrze **contactFields**. JeÅ›li ma odpowiednika dla *każdego* pola okreÅ›lony, zwracany jest kontakt. Użycie **contactFindOptions.desiredFields** parametr do kontroli, które kontakt wÅ‚aÅ›ciwoÅ›ci muszÄ… zostać zwrócone ponownie. + +### Parametry + +* **contactFields**: kontakt z pól do wykorzystania jako kwalifikator Szukaj. *(DOMString[])* [Required] + +* **contactSuccess**: sukcesu funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywane z tablicy obiektów kontaktów zwracane z bazy danych. [Required] + +* **contactError**: Błąd funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywana, gdy wystÄ…pi błąd. [Opcjonalnie] + +* **contactFindOptions**: Szukaj opcji filtrowania navigator.contacts. [Optional] + + Klucze obejmuje: + + * **filter**: ciÄ…g wyszukiwania umożliwia znalezienie navigator.contacts. *(DOMString)* (DomyÅ›lnie: `""`) + + * **multiple**: okreÅ›la, czy operacja Znajdź zwraca wiele navigator.contacts. *(Wartość logiczna)* (DomyÅ›lnie: `false`) + + * **desiredFields**: kontakt z pola, aby być zwrócona. JeÅ›li okreÅ›lony, wynikowy `kontakt` obiekt tylko funkcje wartoÅ›ci tych pól. *(DOMString[])* [Optional] + +### ObsÅ‚ugiwane platformy + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows (tylko urzÄ…dzenia Windows Phone 8.1) + +### PrzykÅ‚ad + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows dziwactwa + +* `__contactFields__`nie jest obsÅ‚ugiwane i zostanie zignorowana. `find`Metoda zawsze bÄ™dzie próbowaÅ‚ dopasować nazwÄ™, adres e-mail lub numer telefonu kontaktu. + +## navigator.contacts.pickContact + +Metoda `navigator.contacts.pickContact` uruchamia próbnika kontakt, wybierz jeden kontaktem. Wynikowy obiekt jest przekazywany do funkcji wywoÅ‚ania zwrotnego `contactSuccess`, okreÅ›lony przez parametr **contactSuccess**. + +### Parametry + +* **contactSuccess**: sukcesu funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywane z jednego obiektu kontakt. [Wymagane] + +* **contactError**: Błąd funkcji wywoÅ‚ania zwrotnego, wywoÅ‚ywana, gdy wystÄ…pi błąd. [Opcjonalnie] + +### ObsÅ‚ugiwane platformy + +* Android +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### PrzykÅ‚ad + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Kontakt + +`Contact` obiekt reprezentuje informacje kontaktowe. Kontakty mogÄ… być tworzone, przechowywane lub usuniÄ™te z bazy danych kontaktów urzÄ…dzenia. Kontakty można również pobrać (pojedynczo lub zbiorczo) bazy danych przez wywoÅ‚anie metody `navigator.contacts.find`. + +**Uwaga**: nie wszystkie pola kontaktowe wymienione powyżej sÄ… obsÅ‚ugiwane na każdej platformie urzÄ…dzenia. ProszÄ™ sprawdzić każdej platformy *dziwactw* sekcji szczegółów. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Identyfikator**: unikatowy identyfikator globalny. *(DOMString)* + +* **displayName**: Nazwa tego kontaktu, nadaje siÄ™ do wyÅ›wietlania użytkownikom koÅ„cowym. *(DOMString)* + +* **Nazwa**: obiekt zawierajÄ…cy wszystkie skÅ‚adniki nazwy osób. *(Przedstawiciel)* + +* **nick**: dorywczo nazwy na adres kontakt. *(DOMString)* + +* **numery telefon≤w**: tablica numerów telefonów kontaktowych. *(ContactField[])* + +* **e-maile**: tablica adresów e-mail kontakt. *(ContactField[])* + +* **adresy**: tablica wszystkie adresy. *(ContactAddress[])* + +* **IMS**: tablica kontakt IM adresy. *(ContactField[])* + +* **organizacje**: tablicy wszystkie kontakty organizacji. *(ContactOrganization[])* + +* **urodziny**: urodziny kontakt. *(Data)* + +* **Uwaga**: Uwaga o kontakt. *(DOMString)* + +* **zdjÄ™cia**: tablica zdjÄ™cia kontaktu. *(ContactField[])* + +* **Kategorie**: tablica wszystkie zdefiniowane przez użytkownika kategorie zwiÄ…zane z kontaktem. *(ContactField[])* + +* **adresy URL**: tablicy stron internetowych zwiÄ…zanych z kontaktem. *(ContactField[])* + +### Metody + +* **klon**: Zwraca nowy `Contact` obiekt, który jest kopiÄ… głęboko obiektu wywoÅ‚ujÄ…cego, z `id` WÅ‚aÅ›ciwość zestaw`null`. + +* **UsuÅ„**: usuwa kontakt z bazy danych kontaktów urzÄ…dzenia, w przeciwnym razie wykonuje błąd wywoÅ‚ania zwrotnego z `ContactError` obiektu. + +* **Zapisz**: zapisuje nowy kontakt do bazy kontaktów urzÄ…dzenia, lub aktualizacje już istniejÄ…cy kontakt, jeÅ›li istnieje już kontakt o tym samym **identyfikatorze** . + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### Zapisz przykÅ‚ad + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### PrzykÅ‚ad klon + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Remove przykÅ‚ad + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X dziwactwa + +* **Kategorie**: nie obsÅ‚ugiwane na urzÄ…dzeniach Android 2.X, powrót`null`. + +### Jeżyna 10 dziwactwa + +* **Identyfikator**: przypisany przez urzÄ…dzenie podczas zapisywania kontaktu. + +### OsobliwoÅ›ci FirefoxOS + +* **Kategorie**: częściowo obsÅ‚ugiwane. Pola **pref** i **Typ** wracajÄ…`null` + +* **IMS**: nie obsÅ‚ugiwane + +* **zdjÄ™cia**: nie obsÅ‚ugiwane + +### Dziwactwa iOS + +* **displayName**: nie obsÅ‚ugiwane na iOS, powrót `null` chyba jest nie `ContactName` okreÅ›lony, w którym to przypadku zwraca nazwÄ™ kompozytowe, **nick** lub `""` , odpowiednio. + +* **urodziny**: należy wpisać jako JavaScript `Date` obiektu, tak samo jest zwracany. + +* **zdjÄ™cia**: zwraca adres URL pliku obrazu, który jest przechowywany w katalogu tymczasowego stosowania. Zawartość katalogu tymczasowe sÄ… usuwane, kiedy koÅ„czy pracÄ™ aplikacji. + +* **Kategorie**: Ta wÅ‚aÅ›ciwość obecnie jest nie obsÅ‚ugiwane, powrót`null`. + +### Windows Phone 7 i 8 dziwactwa + +* **displayName**: podczas tworzenia kontaktu, ŹródÅ‚o wartość podana dla parametru nazwy wyÅ›wietlania różni siÄ™ od nazwy wyÅ›wietlanej, gdy znalezienie kontaktu. + +* **adresy URL**: podczas tworzenia kontaktu, użytkownicy mogÄ… wpisać i zapisać wiÄ™cej niż jeden adres sieci web, ale tylko jeden jest dostÄ™pne podczas wyszukiwania kontaktów. + +* **numery telefon≤w**: *pref* opcja nie jest obsÅ‚ugiwana. *Typ* nie jest obsÅ‚ugiwany w operacji *znaleźć* . Jedynym `phoneNumber` jest dozwolone dla każdego *typu*. + +* **e-maile**: *pref* opcja nie jest obsÅ‚ugiwana. Domu i osobiste odwoÅ‚uje siÄ™ w tym samym wpisu email. Dla każdego *typu* dozwolone jest tylko jeden wpis. + +* **adresy**: obsÅ‚uguje tylko pracy i domu/osobisty *typu*. Domowych i osobistych *typu* odwoÅ‚ania tej samej pozycji adres. Dla każdego *typu* dozwolone jest tylko jeden wpis. + +* **organizacje**: tylko jeden jest dozwolone, a nie obsÅ‚uguje * *pref*, *Typ*i* atrybuty. + +* **Uwaga**: nie obsÅ‚ugiwane, powrót`null`. + +* **IMS**: nie obsÅ‚ugiwane, powrót`null`. + +* **urodziny**: nie obsÅ‚ugiwane, powrót`null`. + +* **Kategorie**: nie obsÅ‚ugiwane, powrót`null`. + +### Windows dziwactwa + +* **zdjÄ™cia**: zwraca adres URL pliku obrazu, który jest przechowywany w katalogu tymczasowego stosowania. + +* **urodziny**: nie obsÅ‚ugiwane, powrót`null`. + +* **Kategorie**: nie obsÅ‚ugiwane, powrót`null`. + +## ContactAddress + +Obiekt `ContactAddress` przechowuje wÅ‚aÅ›ciwoÅ›ci pojedynczego adresu kontaktu. Obiekt `Contact` może zawierać wiÄ™cej niż jeden adres w tablicy `[ContactAddress]`. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Pref**: zestaw `true` JeÅ›li `ContactAddress` zawiera wartoÅ›ci preferowanych użytkownika. *(wartość logiczna)* + +* **Typ**: string wskazujÄ…cÄ… typ pola, *do domu* na przykÅ‚ad. *(DOMString)* + +* **w formacie**: peÅ‚ny adres w formacie wyÅ›wietlania. *(DOMString)* + +* **adres**: peÅ‚ny adres. *(DOMString)* + +* **miejscowoÅ›ci**: miasta lub miejscowoÅ›ci. *(DOMString)* + +* **region**: PaÅ„stwo lub region. *(DOMString)* + +* **Kod pocztowy**: kod pocztowy lub kod pocztowy. *(DOMString)* + +* **kraj**: nazwÄ™ kraju. *(DOMString)* + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### PrzykÅ‚ad + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X dziwactwa + +* **Pref**: nie obsÅ‚ugiwane, powrót `false` na urzÄ…dzeniach Android 2.X. + +### Jeżyna 10 dziwactwa + +* **Pref**: nie obsÅ‚ugiwane na urzÄ…dzenia BlackBerry, powrót`false`. + +* **Typ**: częściowo obsÅ‚ugiwane. Tylko jeden z *pracy* i *Strona główna* wpisz adresy mogÄ… być przechowywane na kontakt. + +* **w formacie**: częściowo obsÅ‚ugiwane. Zwraca łączenie wszystkich pól adres BlackBerry. + +* **adres**: obsÅ‚ugiwane. Zwraca łączenie BlackBerry **address1** i **Adres2** pola adresu. + +* **miejscowoÅ›ci**: obsÅ‚ugiwane. Przechowywane w polu adres **miasto** BlackBerry. + +* **region**: obsÅ‚ugiwane. Przechowywane w polu adres **stateProvince** BlackBerry. + +* **Kod pocztowy**: obsÅ‚ugiwane. Przechowywane w polu adres **zipPostal** BlackBerry. + +* **kraj**: obsÅ‚ugiwane. + +### OsobliwoÅ›ci FirefoxOS + +* **w formacie**: aktualnie nieobsÅ‚ugiwane + +### Dziwactwa iOS + +* **Pref**: nie obsÅ‚ugiwane urzÄ…dzenia iOS, powrót`false`. + +* **w formacie**: obecnie nie jest obsÅ‚ugiwane. + +### Windows 8 dziwactwa + +* **Pref**: nie obsÅ‚ugiwane + +### Windows dziwactwa + +* **Pref**: nie obsÅ‚ugiwane + +## ContactError + +`ContactError` obiekt jest zwracany użytkownikowi za poÅ›rednictwem funkcji wywoÅ‚ania zwrotnego `contactError`, gdy wystÄ…pi błąd. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Kod**: jeden z kodów błędów wstÄ™pnie zdefiniowanych poniżej. + +### StaÅ‚e + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Obiekt `ContactField` jest skÅ‚adnikiem wielokrotnego użytku, że reprezentuje kontakt pola ogólnie. Każdy obiekt `ContactField` zawiera `wartość`, `Typ` i `pref` WÅ‚aÅ›ciwość. Obiekt `Contact` sklepy kilku wÅ‚aÅ›ciwoÅ›ci w tablicach `[ContactField]`, takich jak numery telefonów i adresy e-mail. + +W wiÄ™kszoÅ›ci przypadków sÄ… nie wczeÅ›niej ustalonych wartoÅ›ci atrybutu **type** obiektu `ContactField`. Na przykÅ‚ad numer telefonu można okreÅ›lić **type** wartoÅ›ci *home*, *work*, *mobile*, *iPhone*, lub jakÄ…kolwiek innÄ… wartość, który jest obsÅ‚ugiwany przez platformÄ™ danego urzÄ…dzenia bazy danych kontaktów. Jednak `Contact` **photos** pola, pole **type** wskazuje format zwrócone obrazu: **url**, gdy **value** atrybut zawiera adres URL, do zdjęć, lub *base64*, gdy **value** zawiera ciÄ…g zakodowany base64 obrazu. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Typ**: ciÄ…g, który wskazuje typ pola, *do domu* , np. *(DOMString)* + +* **wartoÅ›ci**: wartość pola, na przykÅ‚ad adresu e-mail lub numer telefonu. *(DOMString)* + +* **Pref**: zestaw `true` JeÅ›li `ContactField` zawiera wartoÅ›ci preferowanych użytkownika. *(wartość logiczna)* + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### PrzykÅ‚ad + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Dziwactwa Androida + +* **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### Jeżyna 10 dziwactwa + +* **Typ**: częściowo obsÅ‚ugiwane. Używane numery telefonów. + +* **wartość**: obsÅ‚ugiwane. + +* **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### Dziwactwa iOS + +* **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### OsobliwoÅ›ci Windows8 + +* **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +### Windows dziwactwa + +* **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +## Przedstawiciel + +Zawiera różne rodzaje informacji o nazwÄ™ obiektu `Contact`. + +### WÅ‚aÅ›ciwoÅ›ci + +* **w formacie**: peÅ‚nÄ… nazwÄ™ kontaktu. *(DOMString)* + +* **danych**: nazwisko kontaktu. *(DOMString)* + +* **imiÄ™**: imiÄ™ kontaktu. *(DOMString)* + +* **middleName**: nazwy bliskiego kontaktu. *(DOMString)* + +* **honorificPrefix**: kontakt prefiks (przykÅ‚ad *Pan* lub *Dr*) *(DOMString)* + +* **honorificSuffix**: kontakt sufiks (przykÅ‚ad *Esq.*). *(DOMString)* + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### PrzykÅ‚ad + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Dziwactwa Androida + +* **w formacie**: częściowo obsÅ‚ugiwane i tylko do odczytu. Zwraca skÅ‚ada siÄ™ z `honorificPrefix` , `givenName` , `middleName` , `familyName` , i`honorificSuffix`. + +### Jeżyna 10 dziwactwa + +* **w formacie**: częściowo obsÅ‚ugiwane. Zwraca łączenie pól **imiÄ™** i **nazwisko** BlackBerry. + +* **danych**: obsÅ‚ugiwane. Przechowywane w BlackBerry pola **nazwisko** . + +* **imiÄ™**: obsÅ‚ugiwane. Przechowywane w polu **imiÄ™** BlackBerry. + +* **middleName**: nie obsÅ‚ugiwane, powrót`null`. + +* **honorificPrefix**: nie obsÅ‚ugiwane, powrót`null`. + +* **honorificSuffix**: nie obsÅ‚ugiwane, powrót`null`. + +### OsobliwoÅ›ci FirefoxOS + +* **w formacie**: częściowo obsÅ‚ugiwane i tylko do odczytu. Zwraca skÅ‚ada siÄ™ z `honorificPrefix` , `givenName` , `middleName` , `familyName` , i`honorificSuffix`. + +### Dziwactwa iOS + +* **w formacie**: częściowo obsÅ‚ugiwane. Zwraca iOS nazwy, ale jest tylko do odczytu. + +### Windows 8 dziwactwa + +* **w formacie**: to jest tylko nazwa wÅ‚aÅ›ciwość i jest taka sama, jak `displayName` , i`nickname` + +* **danych**: nie obsÅ‚ugiwane + +* **imiÄ™**: nie obsÅ‚ugiwane + +* **middleName**: nie obsÅ‚ugiwane + +* **honorificPrefix**: nie obsÅ‚ugiwane + +* **honorificSuffix**: nie obsÅ‚ugiwane + +### Windows dziwactwa + +* **w formacie**: jest identyczny z`displayName` + +## ContactOrganization + +Obiekt `ContactOrganization` przechowuje wÅ‚aÅ›ciwoÅ›ci organizacji kontaktu. Obiekt `Contact` sklepy jeden lub wiÄ™cej obiektów `ContactOrganization` w tablicy. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Pref**: zestaw `true` JeÅ›li `ContactOrganization` zawiera wartoÅ›ci preferowanych użytkownika. *(wartość logiczna)* + +* **Typ**: ciÄ…g, który wskazuje typ pola, *do domu* , np. _(DOMString) + +* **Nazwa**: nazwa organizacji. *(DOMString)* + +* **w departamencie**: dziale umowy dziaÅ‚a. *(DOMString)* + +* **tytuÅ‚**: tytuÅ‚ kontaktu w organizacji. *(DOMString)* + +### ObsÅ‚ugiwane platformy + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows (tylko urzÄ…dzenia Windows 8.1 i Windows Phone 8.1) + +### PrzykÅ‚ad + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X dziwactwa + +* **Pref**: nie obsÅ‚ugiwane przez urzÄ…dzenia Android 2.X, powrót`false`. + +### Jeżyna 10 dziwactwa + +* **Pref**: nie obsÅ‚ugiwane przez urzÄ…dzenia BlackBerry, powrót`false`. + +* **Typ**: nie obsÅ‚ugiwane przez urzÄ…dzenia BlackBerry, powrót`null`. + +* **Nazwa**: częściowo obsÅ‚ugiwane. Pierwsza nazwa organizacji sÄ… przechowywane w polu **firma** BlackBerry. + +* **w departamencie**: nie obsÅ‚ugiwane, powrót`null`. + +* **tytuÅ‚**: częściowo obsÅ‚ugiwane. Pierwszy tytuÅ‚ organizacji sÄ… przechowywane w polu **jobTitle** BlackBerry. + +### Firefox OS dziwactwa + +* **Pref**: nie obsÅ‚ugiwane + +* **Typ**: nie obsÅ‚ugiwane + +* **w departamencie**: nie obsÅ‚ugiwane + +* Pola **Nazwa** i **tytuÅ‚** przechowywane w **org** i **jobTitle**. + +### Dziwactwa iOS + +* **Pref**: nie obsÅ‚ugiwane urzÄ…dzenia iOS, powrót`false`. + +* **Typ**: nie obsÅ‚ugiwane urzÄ…dzenia iOS, powrót`null`. + +* **Nazwa**: częściowo obsÅ‚ugiwane. Pierwsza nazwa organizacji sÄ… przechowywane w polu **kABPersonOrganizationProperty** iOS. + +* **w departamencie**: częściowo obsÅ‚ugiwane. Pierwsza nazwa jest przechowywana w polu **kABPersonDepartmentProperty** iOS. + +* **tytuÅ‚**: częściowo obsÅ‚ugiwane. Pierwszy tytuÅ‚ jest przechowywany w polu **kABPersonJobTitleProperty** iOS. + +### Windows dziwactwa + +* **Pref**: nie obsÅ‚ugiwane, powrót`false`. + +* **Typ**: nie obsÅ‚ugiwane, powrót`null`. diff --git a/plugins/cordova-plugin-contacts/doc/ru/index.md b/plugins/cordova-plugin-contacts/doc/ru/index.md new file mode 100644 index 0000000..424f6d9 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/ru/index.md @@ -0,0 +1,709 @@ + + +# cordova-plugin-contacts + +ОбеÑпечивает доÑтуп к базе данных контактов уÑтройÑтва. + +**Предупреждение**: Ñбор и иÑпользование данные контактов поднимает важные вопроÑÑ‹ конфиденциальноÑти. Политика конфиденциальноÑти вашего Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° объÑÑнÑть, как приложение иÑпользует контактные данные и передаетÑÑ Ð»Ð¸ она третьим лицам. ÐšÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ ÑчитаетÑÑ ÐºÐ¾Ð½Ñ„Ð¸Ð´ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾Ð¹, потому что он показывает людей, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼Ð¸ общаетÑÑ Ñ‡ÐµÐ»Ð¾Ð²ÐµÐº. Таким образом в дополнение к политике конфиденциальноÑти приложениÑ, вы должны раÑÑмотреть возможноÑть предоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð² момент времени перед тем как приложение обращаетÑÑ Ðº, или иÑпользует контактные данные, еÑли Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÑиÑтемы уÑтройÑтва не делает Ñтого. Это уведомление должно обеÑпечивать ту же информацию, указанную выше, а также получение Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (например, путем предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð±Ð¾Ñ€Ð° **OK** и **Ðет, ÑпаÑибо**). Обратите внимание, что некоторые магазины Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ требовать от Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð² момент доÑтупа к данным и получить разрешение Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¿ÐµÑ€ÐµÐ´ доÑтупом к контактным данным. Ð§ÐµÑ‚ÐºÐ°Ñ Ð¸ понÑÑ‚Ð½Ð°Ñ Ñргономика иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð½Ñ‹Ñ… данных помогает избежать недоразумений и ощущаемых злоупотреблений контактными данными. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации пожалуйÑта, Ñмотрите в руководÑтве конфиденциальноÑти. + +## УÑтановка + + cordova plugin add cordova-plugin-contacts + + +### ОÑобенноÑти Firefox OS + +Создайте **www/manifest.webapp** как опиÑано в [ОпиÑание МанифеÑта][1]. Добавление ÑоответÑтвующих разрешений. СущеÑтвует также необходимоÑть изменить тип веб-Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° «priviledged» - [ОпиÑание МанифеÑта][2]. **Ð’ÐИМÐÐИЕ**: Ð’Ñе привилегированные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÑÑŽÑ‚ [Политику безопаÑноÑти Ñодержимого][3] ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð·Ð°Ð¿Ñ€ÐµÑ‰Ð°ÐµÑ‚ вÑтроенные Ñкрипты. Инициализируйте приложение другим ÑпоÑобом. + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### СовмеÑтимоÑти Windows + +Любые контакты, вернулÑÑ Ð¸Ð· `find` и `pickContact` методы ÑвлÑÑŽÑ‚ÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, поÑтому приложение не может изменÑть их. `find`метод доÑтупен только на уÑтройÑтвах Windows Phone 8.1. + +### СовмеÑтимоÑти Windows 8 + +Windows 8 Контакты ÑвлÑÑŽÑ‚ÑÑ readonly. Через контакты Cordova API не ÑвлÑÑŽÑ‚ÑÑ queryable/Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка, вы должны Ñообщить пользователю выбрать контакт как вызов contacts.pickContact, который откроет приложение «Люди», где пользователь должен выбрать контакт. Любые контакты вернулÑÑ, readonly, поÑтому ваше приложение не может изменÑть их. + +## navigator.contacts + +### Методы + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### Объекты + +* Contact +* ContactName +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## navigator.contacts.create + +Метод `navigator.contacts.create` ÑвлÑетÑÑ Ñинхронным, и возвращает новый объект `Contact`. + +Этот метод не ÑохранÑет объект контакта в базе данных контактов уÑтройÑтва, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ необходимо вызвать метод `Contact.save`. + +### Поддерживаемые платформы + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 + +### Пример + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +Метод `navigator.contacts.find` выполнÑетÑÑ Ð°Ñинхронно, Ð·Ð°Ð¿Ñ€Ð°ÑˆÐ¸Ð²Ð°Ñ Ð±Ð°Ð·Ñƒ контактов уÑтройÑтва и Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ Ð¼Ð°ÑÑив объектов `Contact`. Полученные объекты передаютÑÑ Ð² функцию обратного вызова `contactSuccess`, указанную в параметре **contactSuccess** . + +Параметр **contactFields** указывает полÑ, чтобы иÑпользоватьÑÑ Ð² качеÑтве квалификатора ПоиÑк. Ðулевой длины **contactFields** параметр ÑвлÑетÑÑ Ð½ÐµÐ´Ð¾Ð¿ÑƒÑтимым и приводит к `ContactError.INVALID_ARGUMENT_ERROR` . Значение **contactFields** `"*"` возвращает вÑе Ð¿Ð¾Ð»Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¾Ð². + +Строка **ContactFindOptions.filter** может иÑпользоватьÑÑ ÐºÐ°Ðº фильтр поиÑка при запроÑах к базе данных контактов. ЕÑли указано, то к каждому полю, указанному в параметре **contactFields**, применÑетÑÑ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ чаÑтичного поиÑка без учета региÑтра. ЕÑли еÑть Ñовпадение Ð´Ð»Ñ *любого* из указанных полей, контакт возвращаетÑÑ. ИÑпользование параметра **contactFindOptions.desiredFields** Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑвойÑтва контакта должны быть возвращены обратно. + +### Параметры + +* **contactSuccess**: Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð² Ñлучае уÑпешного поиÑка, Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ в виде маÑÑива объектов Contact возвращенных из базы данных контактов. [ОбÑзательный] + +* **contactError**: Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова, вызываетÑÑ Ð¿Ñ€Ð¸ возникновении ошибки. [Опционально] + +* **contactFields**: контакт Ð¿Ð¾Ð»Ñ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве квалификатора ПоиÑк. *(DOMString[])* [ТребуетÑÑ] + +* **contactFindOptions**: Параметры поиÑка Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ filter navigator.contacts. [Опционально] Ключи включают: + +* **filter**: Строка поиÑка иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка по navigator.contacts. *(DOMString)* (по умолчанию: `""`) + +* **multiple**: ОпределÑет еÑли Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ð¸Ñка возвращает неÑколкько navigator.contacts. *(Булевое)* (По умолчанию: `false`) + + * **desiredFields**: контакт Ð¿Ð¾Ð»Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ÑÑ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾. ЕÑли указан, в результате `Contact` объект только функции Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтих полей. *(DOMString[])* [Опционально] + +### Поддерживаемые платформы + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 +* Windows (только Ð´Ð»Ñ ÑƒÑтройÑтв на Windows Phone 8.1) + +### Пример + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### СовмеÑтимоÑти Windows + +* `__contactFields__`не поддерживаетÑÑ Ð¸ будет игнорироватьÑÑ. `find`метод вÑегда будет пытатьÑÑ ÑоответÑтвовать имÑ, Ð°Ð´Ñ€ÐµÑ Ñлектронной почты или номер телефона контакта. + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact`Метод запуÑкает контакт выбора, чтобы выбрать один контакт. Результирующий объект передаетÑÑ Ð² `contactSuccess` функции обратного вызова, указанный параметром **contactSuccess** . + +### Параметры + +* **contactSuccess**: уÑпех Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова вызываетÑÑ Ñ Ð¾Ð´Ð½Ð¸Ð¼ объектом контакта. [ТребуетÑÑ] + +* **contactError**: Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова, вызываетÑÑ Ð¿Ñ€Ð¸ возникновении ошибки. [Опционально] + +### Поддерживаемые платформы + +* Android +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### Пример + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## Contact + +Объект `Contact` предÑтавлÑет контакт пользователÑ. Контакты могут быть Ñозданы, Ñохранены или удалены из базы данных контактов уÑтройÑтва. Контакты также могут быть получены (индивидуально или маÑÑово) из базы данных путем вызова метода `navigator.contacts.find`. + +**Примечание**: не вÑе Ð¿Ð¾Ð»Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¾Ð², перечиÑленных выше, поддерживаютÑÑ Ð½Ð° каждой платформе уÑтройÑтва. ПожалуйÑта, проверьте раздел *ОÑобенноÑти* каждой платформы Ð´Ð»Ñ Ð´ÐµÑ‚Ð°Ð»ÑŒÐ½Ð¾Ð¹ информации. + +### Параметры + +* **ID**: глобальный уникальный идентификатор. *(DOMString)* + +* **displayName**: Ð¸Ð¼Ñ Ñтого контакта, подходÑщую Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŽ. *(DOMString)* + +* **name**: объект, Ñодержащий вÑе компоненты имени человека. *(ContactName)* + +* **nickname**: обычное имÑ, по которому определÑетÑÑ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚. *(DOMString)* + +* **phoneNumbers**: маÑÑив Ñо вÑеми номерами контактных телефонов. *(ContactField[])* + +* **emails**: маÑÑив вÑех адреÑов Ñлектронной почты контакта. *(ContactField[])* + +* **addresses**: маÑÑив вÑех адреÑов контакта. *(ContactAddress[])* + +* **IMS**: маÑÑив вÑех IM-адреÑов контакта. *(ContactField[])* + +* **organizations**: маÑÑив вÑех организаций контакта. *(ContactOrganization[])* + +* **birthday**: день Ñ€Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð°. *(Дата)* + +* **note**: ТекÑÑ‚Ð¾Ð²Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ° о контакте. *(DOMString)* + +* **photos**: маÑÑив фотографий контакта. *(ContactField[])* + +* **categories**: маÑÑив вÑех определенных пользователем категории, ÑвÑзанных Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¾Ð¼. *(ContactField[])* + +* **urls**: маÑÑив веб-Ñтраниц, ÑвÑзанных Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¾Ð¼. *(ContactField[])* + +### Методы + +* **clone**: возвращает новый объект `Contact`, ÑвлÑющийÑÑ Ð³Ð»ÑƒÐ±Ð¾ÐºÐ¾Ð¹ копией вызывающего объекта Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸ÐµÐ¼ ÑвойÑтва `id` равным `null`. + +* **remove**: удалÑет контакт из базы данных контактов уÑтройÑтва, в противном Ñлучае выполнÑет обратный вызов ошибки Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð¾Ð¼ `ContactError` опиÑывающим ошибку. + +* **save**: ÑохранÑет новый контакт в базе данных контактов уÑтройÑтва или обновлÑет ÑущеÑтвующий контакт, еÑли контакт Ñ Ñ‚ÐµÐ¼ же **id** уже ÑущеÑтвует. + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 +* Windows 8 +* Windows + +### Пример ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### Пример ÐºÐ»Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### Пример ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### ОÑобенноÑти Android 2.X + +* **categories**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах Android 2.X, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +### ОÑобенноÑти BlackBerry 10 + +* **ID**: приÑвоенный уÑтройÑтва при Ñохранении контакта. + +### ОÑобенноÑти Firefox OS + +* **categories**: чаÑтично поддерживаетÑÑ. ÐŸÐ¾Ð»Ñ **pref** и **type** возвращают `null` + +* **ims**: Ðе поддерживаетÑÑ + +* **photos**: Ðе поддерживаетÑÑ + +### ОÑобенноÑти iOS + +* **displayName**: не поддерживаетÑÑ Ð½Ð° iOS, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null` ЕÑли `ContactName` не указан, в Ñтом Ñлучае он возвращает ÑоÑтавное имÑ, **nickname** или `""` , ÑоответÑтвенно. + +* **birthday**: Должен быть указан как объект JavaScript `Date`, таким же образом, значение Ñтого Ð¿Ð¾Ð»Ñ Ð¸ возвращаетÑÑ. + +* **photos**: Возвращает URL-Ð°Ð´Ñ€ÐµÑ Ñ„Ð°Ð¹Ð»Ð° изображениÑ, которое хранитÑÑ Ð²Ð¾ временном каталоге приложениÑ. Содержание временного каталога удалÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¸ выходе из приложениÑ. + +* **categories**: Ñто ÑвойÑтво в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ поддерживаетÑÑ, возвращаÑ`null`. + +### ОÑобенноÑти Windows Phone 7 и 8 + +* **displayName**: при Ñоздании контакта, значение, указанное в параметре отличаетÑÑ Ð¾Ñ‚ отображаемого имени полученного при поиÑке контакта. + +* **urls**: при Ñоздании контакта, пользователи могут ввеÑти и Ñохранить более чем одного веб-адреÑ, но только один доÑтупен при поиÑке контакта. + +* **phoneNumbers**: параметр *pref* не поддерживаетÑÑ. *type* не поддерживаетÑÑ Ð² операции *find* . Только один `phoneNumber` допуÑкаетÑÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ *type*. + +* **emails**: параметр *pref* не поддерживаетÑÑ. Домашний и рабочий email ÑÑылаютÑÑ Ð½Ð° одну и ту же запиÑÑŒ Ñлектронной почты. Разрешена только одна запиÑÑŒ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ *type*. + +* **addresses**: поддерживает только рабочий и домашний/личный *type*. ЗапиÑи типа *type* home и personal ÑÑылаютÑÑ Ð½Ð° одну и ту же запиÑÑŒ адреÑа. Разрешена только одна запиÑÑŒ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ *type*. + +* **organizations**: только одна Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð° и она не поддерживает атрибуты *pref*, *type*и *department* . + +* **note**: не поддерживаетÑÑ, возвращаÑ`null`. + +* **ims**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +* **birthdays**: не поддерживаетÑÑ, возвращаÑ`null`. + +* **categories**: не поддерживаетÑÑ, возвращаÑ`null`. + +### СовмеÑтимоÑти Windows + +* **фотографии**: Возвращает URL-Ð°Ð´Ñ€ÐµÑ Ñ„Ð°Ð¹Ð»Ð° изображениÑ, которое хранитÑÑ Ð²Ð¾ временном каталоге приложениÑ. + +* **birthdays**: не поддерживаетÑÑ, возвращаÑ`null`. + +* **categories**: не поддерживаетÑÑ, возвращаÑ`null`. + +## ContactAddress + +Объект `ContactAddress` хранит ÑвойÑтва Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ адреÑа контакта. Объект `Contact` может включать более чем один Ð°Ð´Ñ€ÐµÑ Ð² маÑÑиве `ContactAddress[]`. + +### Параметры + +* **pref**: УÑтановите значение `true` еÑли `ContactAddress` Ñодержит предпочитаемое Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ. *(логичеÑкий)* + +* **type**: Ñтрока, ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°ÑŽÑ‰Ð°Ñ Ñ‚Ð¸Ð¿ полÑ, *home*, например. *(DOMString)* + +* **formatted**: полный Ð°Ð´Ñ€ÐµÑ Ð¾Ñ‚Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ñ‹Ð¹ Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ. *(DOMString)* + +* **streetAddress**: полный почтовый адреÑ. *(DOMString)* + +* **locality**: город или наÑеленный пункт. *(DOMString)* + +* **region**: штат или регион. *(DOMString)* + +* **postalCode**: почтовый Ð¸Ð½Ð´ÐµÐºÑ Ð¸Ð»Ð¸ почтовый код. *(DOMString)* + +* **country**: название Ñтраны. *(DOMString)* + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 +* Windows 8 +* Windows + +### Пример + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### ОÑобенноÑти Android 2.X + +* **ап**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false` на уÑтройÑтвах Android 2.X. + +### ОÑобенноÑти BlackBerry 10 + +* **ап**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах BlackBerry, возвращаÑ`false`. + +* **тип**: чаÑтично поддерживаетÑÑ. Контакт может хранитьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ один из *работы* и *дома* типа адреÑа. + +* **Формат**: чаÑтично поддерживаетÑÑ. Возвращает объединение вÑех полей адреÑа BlackBerry. + +* **streetAddress**: поддерживаетÑÑ. Возвращает объединение и BlackBerry **ÐдреÑ1** **ÐдреÑ2** Ð¿Ð¾Ð»Ñ Ð°Ð´Ñ€ÐµÑа. + +* **наÑеленный пункт**: поддерживаетÑÑ. ХранитÑÑ Ð² поле Ð°Ð´Ñ€ÐµÑ **город** BlackBerry. + +* **регион**: поддерживает. ХранитÑÑ Ð² поле адреÑа **stateProvince** BlackBerry. + +* **postalCode**: поддерживаетÑÑ. ХранитÑÑ Ð² поле адреÑа **zipPostal** BlackBerry. + +* **Ñтрана**: поддерживаетÑÑ. + +### ОÑобенноÑти Firefox OS + +* **formatted**: Ðа данный момент не поддерживаетÑÑ + +### ОÑобенноÑти iOS + +* **pref**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах iOS, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +* **formatted**: Ðа данный момент не поддерживаетÑÑ. + +### СовмеÑтимоÑти Windows 8 + +* **pref**: Ðе поддерживаетÑÑ + +### СовмеÑтимоÑти Windows + +* **pref**: Ðе поддерживаетÑÑ + +## ContactError + +Объект `ContactError` возвращаетÑÑ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŽ через функцию обратного вызова `contactError` в Ñлучае возникновении ошибки. + +### Параметры + +* **code**: один из Ñтандартных кодов ошибок, перечиÑленных ниже. + +### КонÑтанты + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +Объект `ContactField` ÑвлÑетÑÑ Ð¼Ð½Ð¾Ð³Ð¾ÐºÑ€Ð°Ñ‚Ð½Ð¾ иÑпользуемым компонентом, который предÑтавлÑет контактные Ð¿Ð¾Ð»Ñ Ð¾Ð±Ñ‰ÐµÐ³Ð¾ назначениÑ. Каждый объект `ContactField` Ñодержит ÑвойÑтва `value` , `type`, и `pref`. Объект `Contact` имеет неÑколько ÑвойÑтв ÑохранÑющих данные в маÑÑиве `ContactField[]`, такие как номера телефонов и адреÑа Ñлектронной почты. + +Ð’ большинÑтве Ñлучаев не ÑущеÑтвует заранее определенные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð° **type** объекта `ContactField`. Ðапример номер телефона можно указать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ **type** *home*, *work*, *mobile*, *iPhone* или любого другого значениÑ, поддерживаемые базы данных контактов на платформе конкретного уÑтройÑтва . Однако, Ð´Ð»Ñ `Contact` Ð¿Ð¾Ð»Ñ **photos**, поле **type** указывает формат возвращаемого изображениÑ: **URL-адреÑ,** когда атрибут **value** Ñодержит URL-Ð°Ð´Ñ€ÐµÑ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ‚Ð¾ или *base64* , еÑли **value** Ñодержит Ñтроку Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² кодировке base64. + +### Параметры + +* **type**: Ñтрока, ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°ÑŽÑ‰Ð°Ñ Ñ‚Ð¸Ð¿ полÑ, *home*, например. *(DOMString)* + +* **value**: значение полÑ, например номер телефона или Ð°Ð´Ñ€ÐµÑ Ñлектронной почты. *(DOMString)* + +* **pref**: набор `true` ЕÑли `ContactField` Ñодержит значение предпочитаемое пользователем. *(логичеÑкий)* + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 +* Windows 8 +* Windows + +### Пример + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### ОÑобенноÑти Android + +* **pref**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +### ОÑобенноÑти BlackBerry 10 + +* **type**: чаÑтично поддерживаетÑÑ. ИÑпользуетÑÑ Ð´Ð»Ñ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð½Ñ‹Ñ… номеров. + +* **value**: поддерживаетÑÑ. + +* **pref**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +### ОÑобенноÑти iOS + +* **pref**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +### Причуды Windows8 + +* **pref**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +### СовмеÑтимоÑти Windows + +* **pref**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +## ContactName + +Содержит различную информации об имени объекта `Contact`. + +### Параметры + +* **formatted**: полное Ð¸Ð¼Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð°. *(DOMString)* + +* **familyName**: ÑÐµÐ¼ÑŒÑ Ð¸Ð¼Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð°. *(DOMString)* + +* **givenName**: Ð¸Ð¼Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð°. *(DOMString)* + +* **middleName**: отчеÑтво контакта. *(DOMString)* + +* **honorificPrefix**: Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ Ð¸Ð¼ÐµÐ½Ð¸ контакта (например, *г-н* или *д-Ñ€*) *(DOMString)* + +* **honorificSuffix**: ÑÑƒÑ„Ñ„Ð¸ÐºÑ Ð¸Ð¼ÐµÐ½Ð¸ контакта (например, *ÑÑквайр*). *(DOMString)* + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android 2.X +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 +* Windows 8 +* Windows + +### Пример + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### ОÑобенноÑти Android + +* **formatted**: чаÑтично поддерживаетÑÑ Ð¸ только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. Возвращает объединение значений `honorificPrefix`, `givenName`, `middleName`, `familyName`, и `honorificSuffix`. + +### ОÑобенноÑти BlackBerry 10 + +* **formatted**: чаÑтично поддерживаетÑÑ. Возвращает объединение полей BlackBerry **firstName** и **lastName** . + +* **familyName**: поддерживаетÑÑ. ХранитÑÑ Ð² поле **lastName** BlackBerry. + +* **givenName**: поддерживаетÑÑ. ХранитÑÑ Ð² поле **firstName** BlackBerry. + +* **middleName**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +* **honorificPrefix**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +* **honorificSuffix**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +### ОÑобенноÑти Firefox OS + +* **formatted**: чаÑтично поддерживаетÑÑ Ð¸ только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. Возвращает объединение значений `honorificPrefix`, `givenName`, `middleName`, `familyName`, и `honorificSuffix`. + +### ОÑобенноÑти iOS + +* **formatted**: чаÑтично поддерживаетÑÑ. Возвращает ÑоÑтавное Ð¸Ð¼Ñ iOS, но только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. + +### СовмеÑтимоÑти Windows 8 + +* **Формат**: Ñто единÑтвенное Ð¸Ð¼Ñ ÑвойÑтва и идентичен `displayName` , и`nickname` + +* **familyName**: не поддерживаетÑÑ + +* **givenName**: не поддерживаетÑÑ + +* **отчеÑтво**: не поддерживаетÑÑ + +* **honorificPrefix**: не поддерживаетÑÑ + +* **honorificSuffix**: не поддерживаетÑÑ + +### СовмеÑтимоÑти Windows + +* **Формат**: Ñто же`displayName` + +## ContactOrganization + +Объект `ContactOrganization` ÑохранÑет ÑвойÑтва организации контакта. A объект `Contact` хранит один или больше объектов `ContactOrganization` в маÑÑиве. + +### Параметры + +* **pref**: УÑтановите в `true` еÑли `ContactOrganization` Ñодержит значение предпочитаемое пользователем. *(логичеÑкий)* + +* **type**: Ñтрока, ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°ÑŽÑ‰Ð°Ñ Ñ‚Ð¸Ð¿ полÑ, *home*, например. _(DOMString) + +* **name**: название организации. *(DOMString)* + +* **department**: Отдел в котором работает контакт. *(DOMString)* + +* **title**: должноÑть контакта в организации. *(DOMString)* + +### Поддерживаемые платформы + +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 +* Windows (только Windows 8.1 и 8.1 Windows Phone уÑтройÑтв) + +### Пример + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### ОÑобенноÑти Android 2.X + +* **pref**: не поддерживаетÑÑ ÑƒÑтройÑтвами Android 2.X, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +### ОÑобенноÑти BlackBerry 10 + +* **pref**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах BlackBerry, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +* **type**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах BlackBerry, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +* **name**: чаÑтично поддерживаетÑÑ. Первый название организации хранитÑÑ Ð² поле **company** BlackBerry. + +* **department**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +* **title**: чаÑтично поддерживаетÑÑ. Первый должноÑть в организации хранитÑÑ Ð² поле **jobTitle** BlackBerry. + +### ОÑобенноÑти Firefox OS + +* **pref**: Ðе поддерживаетÑÑ + +* **type**: Ðе поддерживаетÑÑ + +* **department**: Ðе поддерживаетÑÑ + +* ÐŸÐ¾Ð»Ñ **name** и **title** хранÑÑ‚ÑÑ Ð² полÑÑ… **org** and **jobTitle**. + +### ОÑобенноÑти iOS + +* **pref**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах iOS, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +* **type**: не поддерживаетÑÑ Ð½Ð° уÑтройÑтвах iOS, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `null`. + +* **name**: чаÑтично поддерживаетÑÑ. Первое название организации хранитÑÑ Ð² поле **kABPersonOrganizationProperty** iOS. + +* **department**: чаÑтично поддерживаетÑÑ. Ð˜Ð¼Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ отдела хранитÑÑ Ð² поле **kABPersonDepartmentProperty** iOS. + +* **title**: чаÑтично поддерживаетÑÑ. ÐŸÐµÑ€Ð²Ð°Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾Ñть хранитÑÑ Ð² поле **kABPersonJobTitleProperty** iOS. + +### СовмеÑтимоÑти Windows + +* **pref**: не поддерживаетÑÑ, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ `false`. + +* **тип**: не поддерживаетÑÑ, возвращаÑ`null`. diff --git a/plugins/cordova-plugin-contacts/doc/zh/README.md b/plugins/cordova-plugin-contacts/doc/zh/README.md new file mode 100644 index 0000000..7ac16eb --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/zh/README.md @@ -0,0 +1,735 @@ + + +# cordova-plugin-contacts + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-contacts.svg)](https://travis-ci.org/apache/cordova-plugin-contacts) + +這個外掛程å¼å®šç¾©äº†ä¸€å€‹å…¨åŸŸ `navigator.contacts` 物件,æä¾›å°è¨­å‚™é€£çµ¡äººè³‡æ–™åº«çš„訪å•。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**警告**: 連絡人資料的收集和使用æå‡ºäº†é‡è¦çš„éš±ç§å•題。 您的應用程å¼çš„éš±ç§æ¬ŠåŽŸå‰‡æ‡‰è©²è¨Žè«–æ‡‰ç”¨ç¨‹å¼å¦‚何使用連絡人資料和它是å¦è¢«å…±ç”¨èˆ‡ä»»ä½•其他締約方。 è¯ç¹«è³‡è¨Šè¢«èªç‚ºæ˜¯æ•感,因為它æ­ç¤ºäº†çš„人與人æºé€šäº†ã€‚ å› æ­¤ï¼Œé™¤äº†éš±ç§æ¬ŠåŽŸå‰‡çš„æ‡‰ç”¨ç¨‹å¼ï¼Œæ‚¨æ‡‰å¼·çƒˆè€ƒæ…®æä¾›æ™‚é–“åªæ˜¯é€šçŸ¥ä¹‹å‰æ‡‰ç”¨ç¨‹å¼è¨ªå•或使用連絡人的資料,如果設備作業系統ä¸å·²ç¶“這樣åšäº†ã€‚ 該通知應æä¾›ç›¸åŒçš„資訊,如上所述,以åŠç²å–è©²ä½¿ç”¨è€…çš„è¨±å¯æ¬Š ï¼ˆä¾‹å¦‚ï¼Œé€šéŽæå‡ºé¸æ“‡ **確定** 並 **䏿„Ÿè¬**)。 請注æ„一些應用程å¼å¸‚å ´å¯èƒ½éœ€è¦æ‡‰ç”¨ç¨‹å¼æä¾›åªæ˜¯æ™‚間的通知,並ç²å¾—ä½¿ç”¨è€…çš„è¨±å¯æ‰èƒ½è¨ªå•連絡人資料。 周åœçš„連絡人資料å¯ä»¥èª¬æ˜Žé¿å…使用者混淆使用和連絡人資料感知的濫用的清楚和容易ç†è§£çš„使用者體驗。 有關詳細資訊,請åƒé–±éš±ç§æŒ‡å—。 + +## å®‰è£ + +é€™å°±è¦æ±‚科爾多瓦 5.0 + (ç•¶å‰ç©©å®š v1.0.0) + + cordova plugin add cordova-plugin-contacts + + +舊版本的科爾多瓦ä»å¯ä»¥å®‰è£é€šéŽ**棄用**id (陳舊 v0.2.16) + + cordova plugin add org.apache.cordova.contacts + + +它也是å¯ä»¥ç›´æŽ¥é€šéŽå›žè³¼ url å®‰è£ (ä¸ç©©å®š) + + cordova plugin add https://github.com/apache/cordova-plugin-contacts.git + + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +在 [清單檔](https://developer.mozilla.org/en-US/Apps/Developing/Manifest) 中所述創建 **www/manifest.webapp**。 æ·»åŠ ç›¸é—œçš„è¨±å¯æ¬Šã€‚ 也是需è¦çš„ web 應用程å¼é¡žåž‹æ›´æ”¹ç‚º"privileged"— — [顯化的文檔](https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type)。 **警告**: 所有的特權應用程å¼å¼·åˆ¶åŸ·è¡Œç¦æ­¢å…§è¯è…³æœ¬çš„ [內容的安全性原則](https://developer.mozilla.org/en-US/Apps/CSP)。 在å¦ä¸€ç¨®æ–¹å¼åˆå§‹åŒ–您的應用程å¼ã€‚ + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows 的怪癖 + +**ä¹‹å‰ Windows 10:**從`發ç¾`å’Œ`pickContact`方法返回任何連絡人是唯讀,因此您的應用程å¼ä¸èƒ½ä¿®æ”¹å®ƒå€‘。 僅在 Windows Phone 8.1 設備上å¯ç”¨çš„ `find` 方法。 + +**Windows 10 åŠä»¥ä¸Š:**連絡人å¯èƒ½ä¿å­˜ï¼Œä¸¦å°‡ä¿å­˜åˆ°æ‡‰ç”¨ç¨‹å¼æœ¬åœ°é€£çµ¡äººå­˜å„²ã€‚ 連絡人也會被刪除。 + +### Windows 8 的怪癖 + +Windows 8 連絡人是唯讀的。 é€éŽç§‘爾多瓦 API æŽ¥è§¸çš„ä¸æ˜¯å¯æŸ¥è©¢/æœç´¢ï¼Œæ‚¨æ‡‰é€šçŸ¥ä½¿ç”¨è€…挑é¸é€£çµ¡äººä½œç‚ºèª¿ç”¨ contacts.pickContact,將會打開 '人' 的應用程å¼ï¼Œä½¿ç”¨è€…å¿…é ˆé¸æ“‡ä¸€å€‹é€£çµ¡äººã€‚ 返回任何連絡人是唯讀,因此您的應用程å¼ä¸èƒ½ä¿®æ”¹å®ƒå€‘。 + +## navigator.contacts + +### 方法 + + * navigator.contacts.create + * navigator.contacts.find + * navigator.contacts.pickContact + +### 物件 + + * 連絡人 + * 連絡人姓å + * ContactField + * ContactAddress + * ContactOrganization + * ContactFindOptions + * ContactError + * ContactFieldType + +## navigator.contacts.create + +`navigator.contacts.create` æ–¹æ³•æ˜¯åŒæ­¥çš„並返回一個新的 `Contact` 物件。 + +æ­¤æ–¹æ³•å°‡ä¸æœƒä¿ç•™åœ¨è¨­å‚™é€£çµ¡äººè³‡æ–™åº«ä¸­ï¼Œéœ€è¦èª¿ç”¨ `Contact.save` 方法的è¯ç¹«ç‰©ä»¶ã€‚ + +### 支æ´çš„平臺 + + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + +### 示例 + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +`navigator.contacts.find` 方法以éžåŒæ­¥æ–¹å¼ï¼ŒåŸ·è¡Œè¨­å‚™é€£çµ¡äººè³‡æ–™åº«æŸ¥è©¢ä¸¦è¿”回 `Contact` 物件的陣列。 生æˆçš„物件被傳éžåˆ°ç”± **contactSuccess** åƒæ•¸æŒ‡å®šçš„ `contactSuccess` 回呼函數。 + +**contactFields** åƒæ•¸æŒ‡å®šçš„æ¬„ä½ç”¨ä½œæœç´¢é™å®šè©žã€‚ 長度為零的 **contactFields** åƒæ•¸æ˜¯ä¸æ­£ç¢ºä¸¦å°Žè‡´ `ContactError.INVALID_ARGUMENT_ERROR`。 **contactFields** 值為 `"*"` æœç´¢æ‰€æœ‰é€£çµ¡äººæ¬„ä½ã€‚ + +在連絡人資料庫查詢時,**contactFindOptions.filter** 字串å¯ä»¥ç”¨ä½œæœç´¢ç¯©é¸å™¨ã€‚ 如果æä¾›ï¼Œä¸å€åˆ†å¤§å°å¯«ï¼Œéƒ¨åˆ†å€¼åŒ¹é…被é©ç”¨äºŽåœ¨ **contactFields** åƒæ•¸ä¸­æŒ‡å®šçš„æ¯å€‹æ¬„ä½ã€‚ 如果存在匹é…çš„ *任何* 指定的欄ä½ï¼Œå‰‡è¿”回連絡人。 使用 **contactFindOptions.desiredFields** åƒæ•¸ä¾†æŽ§åˆ¶å“ªäº›é€£çµ¡äººå±¬æ€§å¿…須回來。 + +### åƒæ•¸ + + * **contactFields**: '連絡人' 欄ä½ç”¨ä½œæœç´¢é™å®šè©žã€‚*(DOMString[])* [Required] + + * **contactSuccess**: 從資料庫返回的æˆåŠŸå›žå‘¼å‡½æ•¸èª¿ç”¨æ™‚ä½¿ç”¨çš„é€£çµ¡äººç‰©ä»¶çš„é™£åˆ—ã€‚[Required] + + * **contactError**: 錯誤回呼函數,當發生錯誤時調用。[å¯é¸] + + * **contactFindOptions**: æœç´¢é¸é …ä¾†ç¯©é¸ navigator.contacts。[Optional] + + éµåŒ…括: + + * **filter**: 用來找到 navigator.contacts çš„æœç´¢å­—串。*() DOMString*(é è¨­ï¼š `""`) + + * **multiple**: ç¢ºå®šæ˜¯å¦æŸ¥æ‰¾æ“作返回多個 navigator.contacts。*(布林值)*(é è¨­å€¼ï¼š `false`) + + * **desiredFields**: '連絡人' 欄ä½ï¼ŒåˆæŠ˜å›žä¾†ã€‚如果指定,由此產生的 `Contact` ç‰©ä»¶åªæœ‰é€™äº›æ¬„ä½çš„功能值。*(DOMString[])* [Optional] + +### 支æ´çš„平臺 + + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows (Windows Phone 8.1 å’Œ Windows 10) + +### 示例 + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows 的怪癖 + + * `__contactFields__`ä¸å—支æ´ï¼Œå°‡è¢«å¿½ç•¥ã€‚`find`方法將始終嘗試匹é…å稱〠電å­éƒµä»¶åœ°å€æˆ–電話號碼的連絡人。 + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact` 方法啟動連絡人é¸å–噍來鏿“‡ä¸€å€‹é€£çµ¡äººã€‚ 將生æˆçš„物件傳éžçµ¦ **contactSuccess** åƒæ•¸æ‰€æŒ‡å®šçš„ `contactSuccess` 回呼函數。 + +### åƒæ•¸ + + * **contactSuccess**: æˆåŠŸä½¿ç”¨å–®å€‹é€£çµ¡äººç‰©ä»¶èª¿ç”¨çš„å›žå‘¼å‡½æ•¸ã€‚[è¦æ±‚] + + * **contactError**: 錯誤回呼函數,當發生錯誤時調用。[å¯é¸] + +### 支æ´çš„平臺 + + * Android 系統 + * iOS + * Windows Phone 8 + * Windows 8 + * Windows + +### 示例 + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## 連絡人 + +`Contact` 物件表示使用者的連絡人。 å¯ä»¥å‰µå»ºã€ 存儲,或從設備的連絡人資料庫中刪除連絡人。 連絡人å¯ä»¥ä¹Ÿè¢« ï¼ˆå–®ç¨æˆ–批é‡ï¼‰ 從資料庫中檢索通éŽèª¿ç”¨ `navigator.contacts.find` 方法。 + +**注**: 䏦䏿˜¯æ‰€æœ‰ä¸Šé¢åˆ—å‡ºçš„é€£çµ¡äººæ¬„ä½æ”¯æ´çš„æ¯å€‹è¨­å‚™çš„å¹³è‡ºã€‚è«‹æª¢æŸ¥æ¯å€‹å¹³è‡º *的怪癖* 節瞭解詳細資訊。 + +### 屬性 + + * **id**: 一個全域唯一識別碼。*() DOMString* + + * **displayName**: 此連絡人,é©åˆæ–¼å‘最終使用者顯示的å稱。*() DOMString* + + * **name**: ä¸€å€‹ç‰©ä»¶ï¼ŒåŒ…å«æ‰€æœ‰å…ƒä»¶çš„一個人的å字。*(連絡人姓å)* + + * **nickname**: 休閒的ä½å€é€£çµ¡äººå稱。*() DOMString* + + * **phoneNumbers**: 陣列的所有連絡人的電話號碼。*(ContactField[])* + + * **emails**: 所有連絡人的電å­éƒµä»¶åœ°å€çš„陣列。*(ContactField[])* + + * **addresses**: 該連絡人的所有ä½å€çš„陣列。*(ContactAddress[])* + + * **ims**: 所有連絡人的 IM ä½å€çš„陣列。*(ContactField[])* + + * **organizations**: 該連絡人的所有組織的陣列。*(ContactOrganization[])* + + * **birthday**: 連絡人的生日。*(Date)* + + * **note**: æ³¨æ„æœ‰é—œçš„è¯ç¹«ã€‚*() DOMString* + + * **photos**: 陣列的連絡人的照片。*(ContactField[])* + + * **categories**: 陣列與連絡人關è¯çš„æ‰€æœ‰ä½¿ç”¨è€…定義的類別。*(ContactField[])* + + * **url**: 陣列與連絡人關è¯çš„ web é ã€‚*(ContactField[])* + +### 方法 + + * **clone**: 返回一個新的 `Contact` 物件就是調用物件的深層副本 `id` 屬性設置為`null`. + + * **remove**: 從設備的連絡人資料庫中刪除連絡人,å¦å‰‡åŸ·è¡ŒéŒ¯èª¤å›žæª”與 `ContactError` 物件。 + + * **save**: 將一個新的連絡人ä¿å­˜åˆ°è¨­å‚™çš„é€£çµ¡äººè³‡æ–™åº«ä¸­ï¼Œæˆ–æ›´æ–°ç¾æœ‰çš„é€£çµ¡äººï¼Œå¦‚æžœå·²å­˜åœ¨å…·æœ‰ç›¸åŒ **id** 的連絡人。 + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### ä¿å­˜ç¤ºä¾‹ + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### 克隆示例 + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### 刪除示例 + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X 的怪癖 + + * **categories**: 䏿”¯æ´ Android 2.X 在設備上,返回`null`. + +### 黑莓 10 怪癖 + + * **id**: ç”±è©²è¨­å‚™åˆ†é…æ™‚ä¿å­˜è©²é€£çµ¡äººã€‚ + +### FirefoxOS 的怪癖 + + * **categories**: 部分支æ´ã€‚返回欄ä½**pref**å’Œ**type**`null` + + * **ims**: 䏿”¯æ´ + + * **photos**: 䏿”¯æ´ + +### iOS 的怪癖 + + * **displayName**: 上返回的 iOS 䏿”¯æ´ `null` é™¤éžæœ‰æ²’有 `ContactName` 指定,在這種情æ³ä¸‹å®ƒå°‡è¿”回複åˆå稱,**nickname**或 `""` ,分別。 + + * **birthday**: 必須輸入 JavaScript 作為 `Date` 物件,它將返回相åŒçš„æ–¹å¼ã€‚ + + * **photos**: 返回到圖åƒä¸­ï¼Œå­˜å„²åœ¨æ‡‰ç”¨ç¨‹å¼çš„臨時目錄中檔的 URL。當應用程å¼é€€å‡ºæ™‚刪除臨時目錄的內容。 + + * **categories**: ç›®å‰ä¸æ”¯æ´æ­¤å±¬æ€§ï¼Œè¿”回`null`. + +### Windows Phone 7 å’Œ 8 怪癖 + + * **displayName**: 當創建一個連絡人,æä¾›çš„顯示åç¨±åƒæ•¸ä¸åŒäºŽé¡¯ç¤ºå稱的值檢索查找連絡人時。 + + * **url**: 當創建一個連絡人,使用者å¯ä»¥è¼¸å…¥å’Œä¿å­˜å¤šå€‹ web ä½å€ï¼Œä½†åªæœ‰ä¸€å€‹æ˜¯å¯ç”¨çš„æœç´¢é€£çµ¡äººæ™‚ã€‚ + + * **phoneNumbers**:*pref*é¸é …ä¸å—支æ´ã€‚ 在*type*æ“ä½œä¸­ä¸æ˜¯æ”¯æ´çš„*find*。 åªæœ‰ä¸€å€‹ `phoneNumber` å…許的æ¯å€‹*type*. + + * **emails**:*pref*é¸é …ä¸å—支æ´ã€‚家庭和個人使用引用åŒä¸€é›»å­éƒµä»¶é …ã€‚åªæœ‰ä¸€é …是å…許的æ¯å€‹*type*. + + * **addresses**: 僅支æ´çš„工作和家庭/個人*type*。家庭和個人*type*引用相åŒçš„ä½å€æ¢ç›®ã€‚åªæœ‰ä¸€é …是å…許的æ¯å€‹*type*. + + * **organizations**: 唯一一個å…è¨±çš„å’Œä¸æ”¯æ´çš„*pref*ã€*type*å’Œ*department*的屬性。 + + * **note**: 䏿”¯æ´ï¼Œè¿”回`null`. + + * **ims**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **birthdays**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **categories**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **remove**: 䏿”¯æ´æ–¹æ³• + +### Windows 的怪癖 + + * **photos**: 返回到圖åƒä¸­ï¼Œå­˜å„²åœ¨æ‡‰ç”¨ç¨‹å¼çš„臨時目錄中檔的 URL。 + + * **birthdays**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **categories**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **remove**: æ–¹æ³•åªæ”¯æ´ Windows 10 或以上。 + +## ContactAddress + +`ContactAddress` 物件存儲單個ä½å€çš„連絡人的屬性。 `Contact` 物件å¯èƒ½åŒ…括多個ä½å€ `ContactAddress []` 陣列中。 + +### 屬性 + + * **pref**: 設置為 `true` 如果這個 `ContactAddress` 包å«ä½¿ç”¨è€…的首é¸çš„價值。*(Boolean)* + + * **type**: 一個字串,例如指示哪種類型的欄ä½ï¼Œé€™æ˜¯*home*。*() DOMString* + + * **formatted**: 顯示格å¼çš„完整ä½å€ã€‚*() DOMString* + + * **streetAddress**: 完整的街é“ä½å€ã€‚*() DOMString* + + * **locality**: 城市或地點。*() DOMString* + + * **region**: 國家或地å€ã€‚*() DOMString* + + * **postalCode**: 郵éžå€è™Ÿã€‚*() DOMString* + + * **country**: 國家å稱。*() DOMString* + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### 示例 + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X 的怪癖 + + * **pref**: ä¸å—支æ´ï¼Œè¿”回 `false` Android 2.X 的設備上。 + +### 黑莓 10 怪癖 + + * **pref**: åœ¨è¿”å›žçš„é»‘èŽ“è¨­å‚™ä¸Šä¸æ”¯æ´`false`. + + * **type**: 部分支æ´ã€‚åªæœ‰ä¸€å€‹*Word*å’Œ*Home*類型ä½å€å¯ä»¥å­˜å„²æ¯å€‹é€£çµ¡äººã€‚ + + * **formatted**: 部分支æ´ã€‚返回的串è¯çš„æ‰€æœ‰é»‘莓手機ä½å€æ¬„ä½ã€‚ + + * **streetAddress**: 支æ´ã€‚返回和串è¯çµ„æˆçš„黑莓**address1** **address2**ä½å€æ¬„ä½ã€‚ + + * **locality**: 支æ´ã€‚黑莓手機**city**ä½å€æ¬„ä½ä¸­å­˜å„²ã€‚ + + * **region**: 支æ´ã€‚黑莓**stateProvince**ä½å€æ¬„ä½ä¸­å­˜å„²ã€‚ + + * **postalCode**: 支æ´ã€‚黑莓**zipPostal**ä½å€æ¬„ä½ä¸­å­˜å„²ã€‚ + + * **country**: 支æ´ã€‚ + +### FirefoxOS 的怪癖 + + * **formatted**: ç›®å‰ä¸æ”¯æ´ + +### iOS 的怪癖 + + * **pref**: 䏿”¯æ´åœ¨ iOS 設備上,返回`false`. + + * **formatted**: ç›®å‰ä¸æ”¯æ´ã€‚ + +### Windows 8 的怪癖 + + * **pref**: 䏿”¯æ´ + +### Windows 的怪癖 + + * **pref**: 䏿”¯æ´ + +## ContactError + +ç•¶ç™¼ç”ŸéŒ¯èª¤æ™‚ï¼Œé€šéŽ `contactError` 回呼函數為使用者情æ³ä¸‹æœƒè¿”回的 `ContactError` 物件。 + +### 屬性 + + * **code**: 下é¢åˆ—出的é å®šç¾©çš„錯誤代碼之一。 + +### å¸¸é‡ + + * `ContactError.UNKNOWN_ERROR` (code 0) + * `ContactError.INVALID_ARGUMENT_ERROR` (code 1) + * `ContactError.TIMEOUT_ERROR` (code 2) + * `ContactError.PENDING_OPERATION_ERROR` (code 3) + * `ContactError.IO_ERROR` (code 4) + * `ContactError.NOT_SUPPORTED_ERROR` (code 5) + * `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +`ContactField` 物件是å¯é‡ç”¨çš„元件代表一般連絡人欄ä½ã€‚ æ¯å€‹ `ContactField` 物件包å«ä¸€å€‹ `value`〠`type` å’Œ `pref` 的屬性。 `Contacat` 物件將幾個屬性存儲在 `ContactField []` 陣列,例如電話號碼和電å­éƒµä»¶åœ°å€ã€‚ + +在大多數情æ³ä¸‹ï¼Œæ²’有é å…ˆç¢ºå®šçš„ `ContactField` 物件的 **type** 屬性值。 例如,一個電話號碼å¯ä»¥æŒ‡å®š **type** 值的 *home*〠*work*〠*mobile*〠*iPhone* 或由一個特定的設備平臺接觸資料庫系統支æ´çš„任何其他值。 然而,為 `photos` **照片** 欄ä½ä¸­ï¼Œ**type** æ¬„ä½æŒ‡ç¤ºè¿”回圖åƒçš„æ ¼å¼ï¼š ç•¶ **value** 屬性包å«ä¸€å€‹æŒ‡å‘çš„ç…§ç‰‡åœ–åƒæˆ– *base64* URL 時的 **value** åŒ…å« string base64 編碼的圖åƒçš„ **url**。 + +### 屬性 + + * **type**: 一個字串,例如指示哪種類型的欄ä½ï¼Œé€™æ˜¯*home*。*() DOMString* + + * **value**: 欄ä½çš„值,如電話號碼或電å­éƒµä»¶åœ°å€ã€‚*() DOMString* + + * **pref**: 設置為 `true` 如果這個 `ContactField` 包å«ä½¿ç”¨è€…的首é¸çš„價值。*(布林)* + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### 示例 + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Android 的怪癖 + + * **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### 黑莓 10 怪癖 + + * **type**: 部分支æ´ã€‚使用的電話號碼。 + + * **value**: 支æ´ã€‚ + + * **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### iOS 的怪癖 + + * **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### Windows8 的怪癖 + + * **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### Windows 的怪癖 + + * **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +## 連絡人姓å + +包å«ä¸åŒç¨®é¡žçš„ `Contact` 物件å稱有關的資訊。 + +### 屬性 + + * **formatted**: 該連絡人的完整å稱。*() DOMString* + + * **familyName**: 連絡人的姓æ°ã€‚*() DOMString* + + * **givenName**: 連絡人的å字。*() DOMString* + + * **middleName**: 連絡人的中間å。*() DOMString* + + * **honorificPrefix**: 連絡人的首碼 (例如*先生*或*åšå£«*) *(DOMString)* + + * **honorificSuffix**: 連絡人的尾碼 (例如*æŸæŸæŸ*)。*() DOMString* + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### 示例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 的怪癖 + + * **formatted**: éƒ¨åˆ†å—æ”¯æ´ï¼Œä¸¦ç‚ºå”¯è®€ã€‚ 返回的串è¯çš„ `honorificPrefix` , `givenName` , `middleName` , `familyName` ,和`honorificSuffix`. + +### 黑莓 10 怪癖 + + * **formatted**: 部分支æ´ã€‚返回的串è¯çš„黑莓手機**firstName**å’Œ**lastName**欄ä½ã€‚ + + * **familyName**: 支æ´ã€‚黑莓**lastName**欄ä½ä¸­å­˜å„²ã€‚ + + * **givenName**: 支æ´ã€‚黑莓**firstName**欄ä½ä¸­å­˜å„²ã€‚ + + * **middleName**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **honorificPrefix**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **honorificSuffix**: ä¸å—支æ´ï¼Œè¿”回`null`. + +### FirefoxOS 的怪癖 + + * **formatted**: éƒ¨åˆ†å—æ”¯æ´ï¼Œä¸¦ç‚ºå”¯è®€ã€‚ 返回的串è¯çš„ `honorificPrefix` , `givenName` , `middleName` , `familyName` ,和`honorificSuffix`. + +### iOS 的怪癖 + + * **formatted**: 部分支æ´ã€‚返回 iOS 複åˆå稱,但為唯讀。 + +### Windows 8 的怪癖 + + * **formatted**: 這是唯一å稱屬性,並且是相åŒçš„ `displayName` ,和`nickname` + + * **familyName**: 䏿”¯æ´ + + * **givenName**: 䏿”¯æ´ + + * **middleName**: 䏿”¯æ´ + + * **honorificPrefix**: 䏿”¯æ´ + + * **honorificSuffix**: 䏿”¯æ´ + +### Windows 的怪癖 + + * **formatted**: 它是完全相åŒ`displayName` + +## ContactOrganization + +`ContactOrganization` 物件存儲連絡人的組織屬性。`Contact` 物件將一個或多個 `ContactOrganization` 物件存儲在一個陣列中。 + +### 屬性 + + * **pref**: 設置為 `true` 如果這個 `ContactOrganization` 包å«ä½¿ç”¨è€…的首é¸çš„價值。*(布林)* + + * **type**: 一個字串,例如指示哪種類型的欄ä½ï¼Œé€™æ˜¯*回家*。_(DOMString) + + * **name**: 組織的å稱。*() DOMString* + + * **department**: åˆåŒå·¥ä½œç‚ºçš„部門。*() DOMString* + + * **title**: 在組織連絡人的標題。*() DOMString* + +### 支æ´çš„平臺 + + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows (Windows 8.1 å’Œ Windows Phone 8.1 設備) + +### 示例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X 的怪癖 + + * **pref**: 䏿”¯æ´çš„ Android 2.X 的設備,返回`false`. + +### 黑莓 10 怪癖 + + * **pref**: 䏿”¯æ´çš„黑莓手機,返回`false`. + + * **type**: 䏿”¯æ´çš„黑莓手機,返回`null`. + + * **name**: 部分支æ´ã€‚第一次組織å稱存儲在黑莓**company**欄ä½ä¸­ã€‚ + + * **department**: ä¸å—支æ´ï¼Œè¿”回`null`. + + * **title**: 部分支æ´ã€‚第一次組織標題存儲在欄ä½ä¸­é»‘莓**jobTitle**。 + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + + * **pref**: 䏿”¯æ´ + + * **type**: 䏿”¯æ´ + + * **department**: 䏿”¯æ´ + + * 欄ä½**name**å’Œ**title**存儲在**org**å’Œ**jobTitle**. + +### iOS 的怪癖 + + * **pref**: 䏿”¯æ´åœ¨ iOS 設備上,返回`false`. + + * **type**: 䏿”¯æ´åœ¨ iOS 設備上,返回`null`. + + * **name**: 部分支æ´ã€‚第一次組織å稱存儲在 iOS **kABPersonOrganizationProperty**欄ä½ä¸­ã€‚ + + * **department**: 部分支æ´ã€‚第一部門å稱存儲在 iOS **kABPersonDepartmentProperty**欄ä½ä¸­ã€‚ + + * **title**: 部分支æ´ã€‚第一個標題存儲在 iOS **kABPersonJobTitleProperty**欄ä½ä¸­ã€‚ + +### Windows 的怪癖 + + * **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + + * **type**: ä¸å—支æ´ï¼Œè¿”回`null`. \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/doc/zh/index.md b/plugins/cordova-plugin-contacts/doc/zh/index.md new file mode 100644 index 0000000..59d7e43 --- /dev/null +++ b/plugins/cordova-plugin-contacts/doc/zh/index.md @@ -0,0 +1,719 @@ + + +# cordova-plugin-contacts + +這個外掛程å¼å®šç¾©äº†ä¸€å€‹å…¨åŸŸ `navigator.contacts` 物件,æä¾›å°è¨­å‚™é€£çµ¡äººè³‡æ–™åº«çš„訪å•。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.contacts); + } + + +**警告**: 連絡人資料的收集和使用æå‡ºäº†é‡è¦çš„éš±ç§å•題。 您的應用程å¼çš„éš±ç§æ¬ŠåŽŸå‰‡æ‡‰è©²è¨Žè«–æ‡‰ç”¨ç¨‹å¼å¦‚何使用連絡人資料和它是å¦è¢«å…±ç”¨èˆ‡ä»»ä½•其他締約方。 è¯ç¹«è³‡è¨Šè¢«èªç‚ºæ˜¯æ•感,因為它æ­ç¤ºäº†çš„人與人æºé€šäº†ã€‚ å› æ­¤ï¼Œé™¤äº†éš±ç§æ¬ŠåŽŸå‰‡çš„æ‡‰ç”¨ç¨‹å¼ï¼Œæ‚¨æ‡‰å¼·çƒˆè€ƒæ…®æä¾›æ™‚é–“åªæ˜¯é€šçŸ¥ä¹‹å‰æ‡‰ç”¨ç¨‹å¼è¨ªå•或使用連絡人的資料,如果設備作業系統ä¸å·²ç¶“這樣åšäº†ã€‚ 該通知應æä¾›ç›¸åŒçš„資訊,如上所述,以åŠç²å–è©²ä½¿ç”¨è€…çš„è¨±å¯æ¬Š ï¼ˆä¾‹å¦‚ï¼Œé€šéŽæå‡ºé¸æ“‡ **確定** 並 **䏿„Ÿè¬**)。 請注æ„一些應用程å¼å¸‚å ´å¯èƒ½éœ€è¦æ‡‰ç”¨ç¨‹å¼æä¾›åªæ˜¯æ™‚間的通知,並ç²å¾—ä½¿ç”¨è€…çš„è¨±å¯æ‰èƒ½è¨ªå•連絡人資料。 周åœçš„連絡人資料å¯ä»¥èª¬æ˜Žé¿å…使用者混淆使用和連絡人資料感知的濫用的清楚和容易ç†è§£çš„使用者體驗。 有關詳細資訊,請åƒé–±éš±ç§æŒ‡å—。 + +## å®‰è£ + + cordova plugin add cordova-plugin-contacts + + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +在 [清單檔][1] 中所述創建 **www/manifest.webapp**。 æ·»åŠ ç›¸é—œçš„è¨±å¯æ¬Šã€‚ 也是需è¦çš„ web 應用程å¼é¡žåž‹æ›´æ”¹ç‚º"privileged"— — [顯化的文檔][2]。 **警告**: 所有的特權應用程å¼å¼·åˆ¶åŸ·è¡Œç¦æ­¢å…§è¯è…³æœ¬çš„ [內容的安全性原則][3]。 在å¦ä¸€ç¨®æ–¹å¼åˆå§‹åŒ–您的應用程å¼ã€‚ + + [1]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest + [2]: https://developer.mozilla.org/en-US/Apps/Developing/Manifest#type + [3]: https://developer.mozilla.org/en-US/Apps/CSP + + "type": "privileged", + "permissions": { + "contacts": { + "access": "readwrite", + "description": "Describe why there is a need for such permission" + } + } + + +### Windows 的怪癖 + +從 `find` å’Œ `pickContact` 方法返回任何連絡人是唯讀,因此您的應用程å¼ä¸èƒ½ä¿®æ”¹å®ƒå€‘。 僅在 Windows Phone 8.1 設備上å¯ç”¨çš„ `find` 方法。 + +### Windows 8 的怪癖 + +Windows 8 連絡人是唯讀的。 é€éŽç§‘爾多瓦 API æŽ¥è§¸çš„ä¸æ˜¯å¯æŸ¥è©¢/æœç´¢ï¼Œæ‚¨æ‡‰é€šçŸ¥ä½¿ç”¨è€…挑é¸é€£çµ¡äººä½œç‚ºèª¿ç”¨ contacts.pickContact,將會打開 '人' 的應用程å¼ï¼Œä½¿ç”¨è€…å¿…é ˆé¸æ“‡ä¸€å€‹é€£çµ¡äººã€‚ 返回任何連絡人是唯讀,因此您的應用程å¼ä¸èƒ½ä¿®æ”¹å®ƒå€‘。 + +## navigator.contacts + +### 方法 + +* navigator.contacts.create +* navigator.contacts.find +* navigator.contacts.pickContact + +### 物件 + +* 連絡人 +* 連絡人姓å +* ContactField +* ContactAddress +* ContactOrganization +* ContactFindOptions +* ContactError +* ContactFieldType + +## navigator.contacts.create + +`navigator.contacts.create` æ–¹æ³•æ˜¯åŒæ­¥çš„並返回一個新的 `Contact` 物件。 + +æ­¤æ–¹æ³•å°‡ä¸æœƒä¿ç•™åœ¨è¨­å‚™é€£çµ¡äººè³‡æ–™åº«ä¸­ï¼Œéœ€è¦èª¿ç”¨ `Contact.save` 方法的è¯ç¹«ç‰©ä»¶ã€‚ + +### 支æ´çš„平臺 + +* Android 系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± +* iOS +* Windows Phone 7 å’Œ 8 + +### 示例 + + var myContact = navigator.contacts.create({"displayName": "Test User"}); + + +## navigator.contacts.find + +`navigator.contacts.find` 方法以éžåŒæ­¥æ–¹å¼ï¼ŒåŸ·è¡Œè¨­å‚™é€£çµ¡äººè³‡æ–™åº«æŸ¥è©¢ä¸¦è¿”回 `Contact` 物件的陣列。 生æˆçš„物件被傳éžåˆ°ç”± **contactSuccess** åƒæ•¸æŒ‡å®šçš„ `contactSuccess` 回呼函數。 + +**contactFields** åƒæ•¸æŒ‡å®šçš„æ¬„ä½ç”¨ä½œæœç´¢é™å®šè©žã€‚ 長度為零的 **contactFields** åƒæ•¸æ˜¯ä¸æ­£ç¢ºä¸¦å°Žè‡´ `ContactError.INVALID_ARGUMENT_ERROR`。 **contactFields** 值為 `"*"` æœç´¢æ‰€æœ‰é€£çµ¡äººæ¬„ä½ã€‚ + +在連絡人資料庫查詢時,**contactFindOptions.filter** 字串å¯ä»¥ç”¨ä½œæœç´¢ç¯©é¸å™¨ã€‚ 如果æä¾›ï¼Œä¸å€åˆ†å¤§å°å¯«ï¼Œéƒ¨åˆ†å€¼åŒ¹é…被é©ç”¨äºŽåœ¨ **contactFields** åƒæ•¸ä¸­æŒ‡å®šçš„æ¯å€‹æ¬„ä½ã€‚ 如果存在匹é…çš„ *任何* 指定的欄ä½ï¼Œå‰‡è¿”回連絡人。 使用 **contactFindOptions.desiredFields** åƒæ•¸ä¾†æŽ§åˆ¶å“ªäº›é€£çµ¡äººå±¬æ€§å¿…須回來。 + +### åƒæ•¸ + +* **contactFields**: '連絡人' 欄ä½ç”¨ä½œæœç´¢é™å®šè©žã€‚*(DOMString[])* [Required] + +* **contactSuccess**: 從資料庫返回的æˆåŠŸå›žå‘¼å‡½æ•¸èª¿ç”¨æ™‚ä½¿ç”¨çš„é€£çµ¡äººç‰©ä»¶çš„é™£åˆ—ã€‚[Required] + +* **contactError**: 錯誤回呼函數,當發生錯誤時調用。[å¯é¸] + +* **contactFindOptions**: æœç´¢é¸é …ä¾†ç¯©é¸ navigator.contacts。[Optional] + + éµåŒ…括: + + * **filter**: 用來找到 navigator.contacts çš„æœç´¢å­—串。*() DOMString*(é è¨­ï¼š `""`) + + * **multiple**: ç¢ºå®šæ˜¯å¦æŸ¥æ‰¾æ“作返回多個 navigator.contacts。*(布林值)*(é è¨­å€¼ï¼š `false`) + + * **desiredFields**: '連絡人' 欄ä½ï¼ŒåˆæŠ˜å›žä¾†ã€‚如果指定,由此產生的 `Contact` ç‰©ä»¶åªæœ‰é€™äº›æ¬„ä½çš„功能值。*(DOMString[])* [Optional] + +### 支æ´çš„平臺 + +* Android 系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± +* iOS +* Windows Phone 7 å’Œ 8 +* Windows (僅é©ç”¨äºŽ Windows Phone 8.1 設備) + +### 示例 + + function onSuccess(contacts) { + alert('Found ' + contacts.length + ' contacts.'); + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts with 'Bob' in any name field + var options = new ContactFindOptions(); + options.filter = "Bob"; + options.multiple = true; + options.desiredFields = [navigator.contacts.fieldType.id]; + var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name]; + navigator.contacts.find(fields, onSuccess, onError, options); + + +### Windows 的怪癖 + +* `__contactFields__`ä¸å—支æ´ï¼Œå°‡è¢«å¿½ç•¥ã€‚`find`方法將始終嘗試匹é…å稱〠電å­éƒµä»¶åœ°å€æˆ–電話號碼的連絡人。 + +## navigator.contacts.pickContact + +`navigator.contacts.pickContact` 方法啟動連絡人é¸å–噍來鏿“‡ä¸€å€‹é€£çµ¡äººã€‚ 將生æˆçš„物件傳éžçµ¦ **contactSuccess** åƒæ•¸æ‰€æŒ‡å®šçš„ `contactSuccess` 回呼函數。 + +### åƒæ•¸ + +* **contactSuccess**: æˆåŠŸä½¿ç”¨å–®å€‹é€£çµ¡äººç‰©ä»¶èª¿ç”¨çš„å›žå‘¼å‡½æ•¸ã€‚[è¦æ±‚] + +* **contactError**: 錯誤回呼函數,當發生錯誤時調用。[å¯é¸] + +### 支æ´çš„平臺 + +* 安å“系統 +* iOS +* Windows Phone 8 +* Windows 8 +* Windows + +### 示例 + + navigator.contacts.pickContact(function(contact){ + console.log('The following contact has been selected:' + JSON.stringify(contact)); + },function(err){ + console.log('Error: ' + err); + }); + + +## 連絡人 + +`Contact` 物件表示使用者的連絡人。 å¯ä»¥å‰µå»ºã€ 存儲,或從設備的連絡人資料庫中刪除連絡人。 連絡人å¯ä»¥ä¹Ÿè¢« ï¼ˆå–®ç¨æˆ–批é‡ï¼‰ 從資料庫中檢索通éŽèª¿ç”¨ `navigator.contacts.find` 方法。 + +**注**: 䏦䏿˜¯æ‰€æœ‰ä¸Šé¢åˆ—å‡ºçš„é€£çµ¡äººæ¬„ä½æ”¯æ´çš„æ¯å€‹è¨­å‚™çš„å¹³è‡ºã€‚è«‹æª¢æŸ¥æ¯å€‹å¹³è‡º *的怪癖* 節瞭解詳細資訊。 + +### 屬性 + +* **id**: 一個全域唯一識別碼。*() DOMString* + +* **displayName**: 此連絡人,é©åˆæ–¼å‘最終使用者顯示的å稱。*() DOMString* + +* **name**: ä¸€å€‹ç‰©ä»¶ï¼ŒåŒ…å«æ‰€æœ‰å…ƒä»¶çš„一個人的å字。*(連絡人姓å)* + +* **nickname**: 休閒的ä½å€é€£çµ¡äººå稱。*() DOMString* + +* **phoneNumbers**: 陣列的所有連絡人的電話號碼。*(ContactField[])* + +* **emails**: 所有連絡人的電å­éƒµä»¶åœ°å€çš„陣列。*(ContactField[])* + +* **addresses**: 該連絡人的所有ä½å€çš„陣列。*(ContactAddress[])* + +* **ims**: 所有連絡人的 IM ä½å€çš„陣列。*(ContactField[])* + +* **organizations**: 該連絡人的所有組織的陣列。*(ContactOrganization[])* + +* **birthday**: 連絡人的生日。*(Date)* + +* **note**: æ³¨æ„æœ‰é—œçš„è¯ç¹«ã€‚*() DOMString* + +* **photos**: 陣列的連絡人的照片。*(ContactField[])* + +* **categories**: 陣列與連絡人關è¯çš„æ‰€æœ‰ä½¿ç”¨è€…定義的類別。*(ContactField[])* + +* **url**: 陣列與連絡人關è¯çš„ web é ã€‚*(ContactField[])* + +### 方法 + +* **clone**: 返回一個新的 `Contact` 物件就是調用物件的深層副本 `id` 屬性設置為`null`. + +* **remove**: 從設備的連絡人資料庫中刪除連絡人,å¦å‰‡åŸ·è¡ŒéŒ¯èª¤å›žæª”與 `ContactError` 物件。 + +* **save**: 將一個新的連絡人ä¿å­˜åˆ°è¨­å‚™çš„é€£çµ¡äººè³‡æ–™åº«ä¸­ï¼Œæˆ–æ›´æ–°ç¾æœ‰çš„é€£çµ¡äººï¼Œå¦‚æžœå·²å­˜åœ¨å…·æœ‰ç›¸åŒ **id** 的連絡人。 + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* 安å“系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### ä¿å­˜ç¤ºä¾‹ + + function onSuccess(contact) { + alert("Save Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // create a new contact object + var contact = navigator.contacts.create(); + contact.displayName = "Plumber"; + contact.nickname = "Plumber"; // specify both to support all devices + + // populate some fields + var name = new ContactName(); + name.givenName = "Jane"; + name.familyName = "Doe"; + contact.name = name; + + // save to device + contact.save(onSuccess,onError); + + +### 克隆示例 + + // clone the contact object + var clone = contact.clone(); + clone.name.givenName = "John"; + console.log("Original contact name = " + contact.name.givenName); + console.log("Cloned contact name = " + clone.name.givenName); + + +### 刪除示例 + + function onSuccess() { + alert("Removal Success"); + }; + + function onError(contactError) { + alert("Error = " + contactError.code); + }; + + // remove the contact from the device + contact.remove(onSuccess,onError); + + +### Android 2.X 的怪癖 + +* **categories**: 䏿”¯æ´ Android 2.X 在設備上,返回`null`. + +### 黑莓 10 的怪癖 + +* **id**: ç”±è©²è¨­å‚™åˆ†é…æ™‚ä¿å­˜è©²é€£çµ¡äººã€‚ + +### FirefoxOS 的怪癖 + +* **categories**: 部分支æ´ã€‚返回欄ä½**pref**å’Œ**type**`null` + +* **ims**: 䏿”¯æ´ + +* **photos**: 䏿”¯æ´ + +### iOS 的怪癖 + +* **displayName**: 上返回的 iOS 䏿”¯æ´ `null` é™¤éžæœ‰æ²’有 `ContactName` 指定,在這種情æ³ä¸‹å®ƒå°‡è¿”回複åˆå稱,**nickname**或 `""` ,分別。 + +* **birthday**: 必須輸入 JavaScript 作為 `Date` 物件,它將返回相åŒçš„æ–¹å¼ã€‚ + +* **photos**: 返回到圖åƒä¸­ï¼Œå­˜å„²åœ¨æ‡‰ç”¨ç¨‹å¼çš„臨時目錄中檔的 URL。當應用程å¼é€€å‡ºæ™‚刪除臨時目錄的內容。 + +* **categories**: ç›®å‰ä¸æ”¯æ´æ­¤å±¬æ€§ï¼Œè¿”回`null`. + +### Windows Phone 7 å’Œ 8 的怪癖 + +* **displayName**: 當創建一個連絡人,æä¾›çš„顯示åç¨±åƒæ•¸ä¸åŒäºŽé¡¯ç¤ºå稱的值檢索查找連絡人時。 + +* **url**: 當創建一個連絡人,使用者å¯ä»¥è¼¸å…¥å’Œä¿å­˜å¤šå€‹ web ä½å€ï¼Œä½†åªæœ‰ä¸€å€‹æ˜¯å¯ç”¨çš„æœç´¢é€£çµ¡äººæ™‚ã€‚ + +* **phoneNumbers**:*pref*é¸é …ä¸å—支æ´ã€‚ 在*type*æ“ä½œä¸­ä¸æ˜¯æ”¯æ´çš„*find*。 åªæœ‰ä¸€å€‹ `phoneNumber` å…許的æ¯å€‹*type*. + +* **emails**:*pref*é¸é …ä¸å—支æ´ã€‚家庭和個人使用引用åŒä¸€é›»å­éƒµä»¶é …ã€‚åªæœ‰ä¸€é …是å…許的æ¯å€‹*type*. + +* **addresses**: 僅支æ´çš„工作和家庭/個人*type*。家庭和個人*type*引用相åŒçš„ä½å€æ¢ç›®ã€‚åªæœ‰ä¸€é …是å…許的æ¯å€‹*type*. + +* **organizations**: 唯一一個å…è¨±çš„å’Œä¸æ”¯æ´çš„*pref*ã€*type*å’Œ*department*的屬性。 + +* **note**: 䏿”¯æ´ï¼Œè¿”回`null`. + +* **ims**: ä¸å—支æ´ï¼Œè¿”回`null`. + +* **birthdays**: ä¸å—支æ´ï¼Œè¿”回`null`. + +* **categories**: ä¸å—支æ´ï¼Œè¿”回`null`. + +### Windows 的怪癖 + +* **photos**: 返回到圖åƒä¸­ï¼Œå­˜å„²åœ¨æ‡‰ç”¨ç¨‹å¼çš„臨時目錄中檔的 URL。 + +* **birthdays**: ä¸å—支æ´ï¼Œè¿”回`null`. + +* **categories**: ä¸å—支æ´ï¼Œè¿”回`null`. + +## ContactAddress + +`ContactAddress` 物件存儲單個ä½å€çš„連絡人的屬性。 `Contact` 物件å¯èƒ½åŒ…括多個ä½å€ `ContactAddress []` 陣列中。 + +### 屬性 + +* **pref**: 設置為 `true` 如果這個 `ContactAddress` 包å«ä½¿ç”¨è€…的首é¸çš„價值。*(Boolean)* + +* **type**: 一個字串,例如指示哪種類型的欄ä½ï¼Œé€™æ˜¯*home*。*() DOMString* + +* **formatted**: 顯示格å¼çš„完整ä½å€ã€‚*() DOMString* + +* **streetAddress**: 完整的街é“ä½å€ã€‚*() DOMString* + +* **locality**: 城市或地點。*() DOMString* + +* **region**: 國家或地å€ã€‚*() DOMString* + +* **postalCode**: 郵éžå€è™Ÿã€‚*() DOMString* + +* **country**: 國家å稱。*() DOMString* + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* 安å“系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### 示例 + + // display the address information for all contacts + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].addresses.length; j++) { + alert("Pref: " + contacts[i].addresses[j].pref + "\n" + + "Type: " + contacts[i].addresses[j].type + "\n" + + "Formatted: " + contacts[i].addresses[j].formatted + "\n" + + "Street Address: " + contacts[i].addresses[j].streetAddress + "\n" + + "Locality: " + contacts[i].addresses[j].locality + "\n" + + "Region: " + contacts[i].addresses[j].region + "\n" + + "Postal Code: " + contacts[i].addresses[j].postalCode + "\n" + + "Country: " + contacts[i].addresses[j].country); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + // find all contacts + var options = new ContactFindOptions(); + options.filter = ""; + var filter = ["displayName", "addresses"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X 的怪癖 + +* **pref**: ä¸å—支æ´ï¼Œè¿”回 `false` Android 2.X 的設備上。 + +### 黑莓 10 的怪癖 + +* **pref**: åœ¨è¿”å›žçš„é»‘èŽ“è¨­å‚™ä¸Šä¸æ”¯æ´`false`. + +* **type**: 部分支æ´ã€‚åªæœ‰ä¸€å€‹*Word*å’Œ*Home*類型ä½å€å¯ä»¥å­˜å„²æ¯å€‹é€£çµ¡äººã€‚ + +* **formatted**: 部分支æ´ã€‚返回的串è¯çš„æ‰€æœ‰é»‘莓手機ä½å€æ¬„ä½ã€‚ + +* **streetAddress**: 支æ´ã€‚返回和串è¯çµ„æˆçš„黑莓**address1** **address2**ä½å€æ¬„ä½ã€‚ + +* **locality**: 支æ´ã€‚黑莓手機**city**ä½å€æ¬„ä½ä¸­å­˜å„²ã€‚ + +* **region**: 支æ´ã€‚黑莓**stateProvince**ä½å€æ¬„ä½ä¸­å­˜å„²ã€‚ + +* **postalCode**: 支æ´ã€‚黑莓**zipPostal**ä½å€æ¬„ä½ä¸­å­˜å„²ã€‚ + +* **country**: 支æ´ã€‚ + +### FirefoxOS 的怪癖 + +* **formatted**: ç›®å‰ä¸æ”¯æ´ + +### iOS 的怪癖 + +* **pref**: 䏿”¯æ´åœ¨ iOS 設備上,返回`false`. + +* **formatted**: ç›®å‰ä¸æ”¯æ´ã€‚ + +### Windows 8 的怪癖 + +* **pref**: 䏿”¯æ´ + +### Windows 的怪癖 + +* **pref**: 䏿”¯æ´ + +## ContactError + +ç•¶ç™¼ç”ŸéŒ¯èª¤æ™‚ï¼Œé€šéŽ `contactError` 回呼函數為使用者情æ³ä¸‹æœƒè¿”回的 `ContactError` 物件。 + +### 屬性 + +* **code**: 下é¢åˆ—出的é å®šç¾©çš„錯誤代碼之一。 + +### å¸¸é‡ + +* `ContactError.UNKNOWN_ERROR` (code 0) +* `ContactError.INVALID_ARGUMENT_ERROR` (code 1) +* `ContactError.TIMEOUT_ERROR` (code 2) +* `ContactError.PENDING_OPERATION_ERROR` (code 3) +* `ContactError.IO_ERROR` (code 4) +* `ContactError.NOT_SUPPORTED_ERROR` (code 5) +* `ContactError.PERMISSION_DENIED_ERROR` (code 20) + +## ContactField + +`ContactField` 物件是å¯é‡ç”¨çš„元件代表一般連絡人欄ä½ã€‚ æ¯å€‹ `ContactField` 物件包å«ä¸€å€‹ `value`〠`type` å’Œ `pref` 的屬性。 `Contacat` 物件將幾個屬性存儲在 `ContactField []` 陣列,例如電話號碼和電å­éƒµä»¶åœ°å€ã€‚ + +在大多數情æ³ä¸‹ï¼Œæ²’有é å…ˆç¢ºå®šçš„ `ContactField` 物件的 **type** 屬性值。 例如,一個電話號碼å¯ä»¥æŒ‡å®š **type** 值的 *home*〠*work*〠*mobile*〠*iPhone* 或由一個特定的設備平臺接觸資料庫系統支æ´çš„任何其他值。 然而,為 `photos` **照片** 欄ä½ä¸­ï¼Œ**type** æ¬„ä½æŒ‡ç¤ºè¿”回圖åƒçš„æ ¼å¼ï¼š ç•¶ **value** 屬性包å«ä¸€å€‹æŒ‡å‘çš„ç…§ç‰‡åœ–åƒæˆ– *base64* URL 時的 **value** åŒ…å« string base64 編碼的圖åƒçš„ **url**。 + +### 屬性 + +* **type**: 一個字串,例如指示哪種類型的欄ä½ï¼Œé€™æ˜¯*home*。*() DOMString* + +* **value**: 欄ä½çš„值,如電話號碼或電å­éƒµä»¶åœ°å€ã€‚*() DOMString* + +* **pref**: 設置為 `true` 如果這個 `ContactField` 包å«ä½¿ç”¨è€…的首é¸çš„價值。*(布林)* + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* 安å“系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### 示例 + + // create a new contact + var contact = navigator.contacts.create(); + + // store contact phone numbers in ContactField[] + var phoneNumbers = []; + phoneNumbers[0] = new ContactField('work', '212-555-1234', false); + phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true); // preferred number + phoneNumbers[2] = new ContactField('home', '203-555-7890', false); + contact.phoneNumbers = phoneNumbers; + + // save the contact + contact.save(); + + +### Android 的怪癖 + +* **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### 黑莓 10 的怪癖 + +* **type**: 部分支æ´ã€‚使用的電話號碼。 + +* **value**: 支æ´ã€‚ + +* **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### iOS 的怪癖 + +* **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### Windows8 的怪癖 + +* **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +### Windows 的怪癖 + +* **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +## ContactName + +包å«ä¸åŒç¨®é¡žçš„ `Contact` 物件å稱有關的資訊。 + +### 屬性 + +* **formatted**: 該連絡人的完整å稱。*() DOMString* + +* **familyName**: 連絡人的姓æ°ã€‚*() DOMString* + +* **givenName**: 連絡人的å字。*() DOMString* + +* **middleName**: 連絡人的中間å。*() DOMString* + +* **honorificPrefix**: 連絡人的首碼 (例如*先生*或*åšå£«*) *(DOMString)* + +* **honorificSuffix**: 連絡人的尾碼 (例如*æŸæŸæŸ*)。*() DOMString* + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### 示例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + alert("Formatted: " + contacts[i].name.formatted + "\n" + + "Family Name: " + contacts[i].name.familyName + "\n" + + "Given Name: " + contacts[i].name.givenName + "\n" + + "Middle Name: " + contacts[i].name.middleName + "\n" + + "Suffix: " + contacts[i].name.honorificSuffix + "\n" + + "Prefix: " + contacts[i].name.honorificSuffix); + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "name"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 的怪癖 + +* **formatted**: éƒ¨åˆ†å—æ”¯æ´ï¼Œä¸¦ç‚ºå”¯è®€ã€‚ 返回的串è¯çš„ `honorificPrefix` , `givenName` , `middleName` , `familyName` ,和`honorificSuffix`. + +### 黑莓 10 的怪癖 + +* **formatted**: 部分支æ´ã€‚返回的串è¯çš„黑莓手機**firstName**å’Œ**lastName**欄ä½ã€‚ + +* **familyName**: 支æ´ã€‚黑莓**lastName**欄ä½ä¸­å­˜å„²ã€‚ + +* **givenName**: 支æ´ã€‚黑莓**firstName**欄ä½ä¸­å­˜å„²ã€‚ + +* **middleName**: ä¸å—支æ´ï¼Œè¿”回`null`. + +* **honorificPrefix**: ä¸å—支æ´ï¼Œè¿”回`null`. + +* **honorificSuffix**: ä¸å—支æ´ï¼Œè¿”回`null`. + +### FirefoxOS 的怪癖 + +* **formatted**: éƒ¨åˆ†å—æ”¯æ´ï¼Œä¸¦ç‚ºå”¯è®€ã€‚ 返回的串è¯çš„ `honorificPrefix` , `givenName` , `middleName` , `familyName` ,和`honorificSuffix`. + +### iOS 的怪癖 + +* **formatted**: 部分支æ´ã€‚返回 iOS 複åˆå稱,但為唯讀。 + +### Windows 8 的怪癖 + +* **formatted**: 這是唯一å稱屬性,並且是相åŒçš„ `displayName` ,和`nickname` + +* **familyName**: 䏿”¯æ´ + +* **givenName**: 䏿”¯æ´ + +* **middleName**: 䏿”¯æ´ + +* **honorificPrefix**: 䏿”¯æ´ + +* **honorificSuffix**: 䏿”¯æ´ + +### Windows 的怪癖 + +* **formatted**: 它是完全相åŒ`displayName` + +## ContactOrganization + +`ContactOrganization` 物件存儲連絡人的組織屬性。`Contact` 物件將一個或多個 `ContactOrganization` 物件存儲在一個陣列中。 + +### 屬性 + +* **pref**: 設置為 `true` 如果這個 `ContactOrganization` 包å«ä½¿ç”¨è€…的首é¸çš„價值。*(布林)* + +* **type**: 一個字串,例如指示哪種類型的欄ä½ï¼Œé€™æ˜¯*回家*。_(DOMString) + +* **name**: 組織的å稱。*() DOMString* + +* **department**: åˆåŒå·¥ä½œç‚ºçš„部門。*() DOMString* + +* **title**: 在組織連絡人的標題。*() DOMString* + +### 支æ´çš„平臺 + +* 安å“系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* Windows Phone 7 å’Œ 8 +* Windows (Windows 8.1 å’Œ Windows Phone 8.1 設備) + +### 示例 + + function onSuccess(contacts) { + for (var i = 0; i < contacts.length; i++) { + for (var j = 0; j < contacts[i].organizations.length; j++) { + alert("Pref: " + contacts[i].organizations[j].pref + "\n" + + "Type: " + contacts[i].organizations[j].type + "\n" + + "Name: " + contacts[i].organizations[j].name + "\n" + + "Department: " + contacts[i].organizations[j].department + "\n" + + "Title: " + contacts[i].organizations[j].title); + } + } + }; + + function onError(contactError) { + alert('onError!'); + }; + + var options = new ContactFindOptions(); + options.filter = ""; + filter = ["displayName", "organizations"]; + navigator.contacts.find(filter, onSuccess, onError, options); + + +### Android 2.X 的怪癖 + +* **pref**: 䏿”¯æ´çš„ Android 2.X 的設備,返回`false`. + +### 黑莓 10 的怪癖 + +* **pref**: 䏿”¯æ´çš„黑莓手機,返回`false`. + +* **type**: 䏿”¯æ´çš„黑莓手機,返回`null`. + +* **name**: 部分支æ´ã€‚第一次組織å稱存儲在黑莓**company**欄ä½ä¸­ã€‚ + +* **department**: ä¸å—支æ´ï¼Œè¿”回`null`. + +* **title**: 部分支æ´ã€‚第一次組織標題存儲在欄ä½ä¸­é»‘莓**jobTitle**。 + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +* **pref**: 䏿”¯æ´ + +* **type**: 䏿”¯æ´ + +* **department**: 䏿”¯æ´ + +* 欄ä½**name**å’Œ**title**存儲在**org**å’Œ**jobTitle**. + +### iOS 的怪癖 + +* **pref**: 䏿”¯æ´åœ¨ iOS 設備上,返回`false`. + +* **type**: 䏿”¯æ´åœ¨ iOS 設備上,返回`null`. + +* **name**: 部分支æ´ã€‚第一次組織å稱存儲在 iOS **kABPersonOrganizationProperty**欄ä½ä¸­ã€‚ + +* **department**: 部分支æ´ã€‚第一部門å稱存儲在 iOS **kABPersonDepartmentProperty**欄ä½ä¸­ã€‚ + +* **title**: 部分支æ´ã€‚第一個標題存儲在 iOS **kABPersonJobTitleProperty**欄ä½ä¸­ã€‚ + +### Windows 的怪癖 + +* **pref**: ä¸å—支æ´ï¼Œè¿”回`false`. + +* **type**: ä¸å—支æ´ï¼Œè¿”回`null`. diff --git a/plugins/cordova-plugin-contacts/package.json b/plugins/cordova-plugin-contacts/package.json new file mode 100644 index 0000000..7e1ceb3 --- /dev/null +++ b/plugins/cordova-plugin-contacts/package.json @@ -0,0 +1,40 @@ +{ + "name": "cordova-plugin-contacts", + "version": "2.0.1", + "description": "Cordova Contacts Plugin", + "cordova": { + "id": "cordova-plugin-contacts", + "platforms": [ + "android", + "amazon-fireos", + "ubuntu", + "ios", + "blackberry10", + "wp8", + "firefoxos", + "windows8", + "windows" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-contacts" + }, + "keywords": [ + "cordova", + "contacts", + "ecosystem:cordova", + "cordova-android", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-ios", + "cordova-blackberry10", + "cordova-wp7", + "cordova-wp8", + "cordova-firefoxos", + "cordova-windows8", + "cordova-windows" + ], + "author": "Apache Software Foundation", + "license": "Apache 2.0" +} diff --git a/plugins/cordova-plugin-contacts/plugin.xml b/plugins/cordova-plugin-contacts/plugin.xml new file mode 100644 index 0000000..d44e466 --- /dev/null +++ b/plugins/cordova-plugin-contacts/plugin.xml @@ -0,0 +1,224 @@ + + + + + + Contacts + Cordova Contacts Plugin + Apache 2.0 + cordova,contacts + https://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts.git + https://issues.apache.org/jira/browse/CB/component/12320652 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + access_pimdomain_contacts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-contacts/src/android/ContactAccessor.java b/plugins/cordova-plugin-contacts/src/android/ContactAccessor.java new file mode 100644 index 0000000..2b73d43 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/android/ContactAccessor.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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. + */ + +package org.apache.cordova.contacts; + +import java.util.HashMap; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.util.Log; +import org.apache.cordova.CordovaInterface; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * This abstract class defines SDK-independent API for communication with + * Contacts Provider. The actual implementation used by the application depends + * on the level of API available on the device. If the API level is Cupcake or + * Donut, we want to use the {@link ContactAccessorSdk3_4} class. If it is + * Eclair or higher, we want to use {@link ContactAccessorSdk5}. + */ +public abstract class ContactAccessor { + + protected final String LOG_TAG = "ContactsAccessor"; + protected CordovaInterface mApp; + + /** + * Check to see if the data associated with the key is required to + * be populated in the Contact object. + * @param key + * @param map created by running buildPopulationSet. + * @return true if the key data is required + */ + protected boolean isRequired(String key, HashMap map) { + Boolean retVal = map.get(key); + return (retVal == null) ? false : retVal.booleanValue(); + } + + /** + * Create a hash map of what data needs to be populated in the Contact object + * @param fields the list of fields to populate + * @return the hash map of required data + */ + protected HashMap buildPopulationSet(JSONObject options) { + HashMap map = new HashMap(); + + String key; + try { + JSONArray desiredFields = null; + if (options!=null && options.has("desiredFields")) { + desiredFields = options.getJSONArray("desiredFields"); + } + if (desiredFields == null || desiredFields.length() == 0) { + map.put("displayName", true); + map.put("name", true); + map.put("nickname", true); + map.put("phoneNumbers", true); + map.put("emails", true); + map.put("addresses", true); + map.put("ims", true); + map.put("organizations", true); + map.put("birthday", true); + map.put("note", true); + map.put("urls", true); + map.put("photos", true); + map.put("categories", true); + } else { + for (int i = 0; i < desiredFields.length(); i++) { + key = desiredFields.getString(i); + if (key.startsWith("displayName")) { + map.put("displayName", true); + } else if (key.startsWith("name")) { + map.put("displayName", true); + map.put("name", true); + } else if (key.startsWith("nickname")) { + map.put("nickname", true); + } else if (key.startsWith("phoneNumbers")) { + map.put("phoneNumbers", true); + } else if (key.startsWith("emails")) { + map.put("emails", true); + } else if (key.startsWith("addresses")) { + map.put("addresses", true); + } else if (key.startsWith("ims")) { + map.put("ims", true); + } else if (key.startsWith("organizations")) { + map.put("organizations", true); + } else if (key.startsWith("birthday")) { + map.put("birthday", true); + } else if (key.startsWith("note")) { + map.put("note", true); + } else if (key.startsWith("urls")) { + map.put("urls", true); + } else if (key.startsWith("photos")) { + map.put("photos", true); + } else if (key.startsWith("categories")) { + map.put("categories", true); + } + } + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return map; + } + + /** + * Convenience method to get a string from a JSON object. Saves a + * lot of try/catch writing. + * If the property is not found in the object null will be returned. + * + * @param obj contact object to search + * @param property to be looked up + * @return The value of the property + */ + protected String getJsonString(JSONObject obj, String property) { + String value = null; + try { + if (obj != null) { + value = obj.getString(property); + if (value.equals("null")) { + Log.d(LOG_TAG, property + " is string called 'null'"); + value = null; + } + } + } + catch (JSONException e) { + Log.d(LOG_TAG, "Could not get = " + e.getMessage()); + } + return value; + } + + /** + * Handles adding a JSON Contact object into the database. + * @return TODO + */ + public abstract String save(JSONObject contact); + + /** + * Handles searching through SDK-specific contacts API. + */ + public abstract JSONArray search(JSONArray filter, JSONObject options); + + /** + * Handles searching through SDK-specific contacts API. + * @throws JSONException + */ + public abstract JSONObject getContactById(String id) throws JSONException; + + /** + * Handles searching through SDK-specific contacts API. + * @param desiredFields fields that will filled. All fields will be filled if null + * @throws JSONException + */ + public abstract JSONObject getContactById(String id, JSONArray desiredFields) throws JSONException; + + /** + * Handles removing a contact from the database. + */ + public abstract boolean remove(String id); + + /** + * A class that represents the where clause to be used in the database query + */ + class WhereOptions { + private String where; + private String[] whereArgs; + public void setWhere(String where) { + this.where = where; + } + public String getWhere() { + return where; + } + public void setWhereArgs(String[] whereArgs) { + this.whereArgs = whereArgs; + } + public String[] getWhereArgs() { + return whereArgs; + } + } +} diff --git a/plugins/cordova-plugin-contacts/src/android/ContactAccessorSdk5.java b/plugins/cordova-plugin-contacts/src/android/ContactAccessorSdk5.java new file mode 100644 index 0000000..bd43bf1 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/android/ContactAccessorSdk5.java @@ -0,0 +1,2293 @@ +/* + 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.contacts; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.sql.Date; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.cordova.CordovaInterface; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.net.Uri; +import android.os.RemoteException; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.text.TextUtils; +import android.util.Log; + +/** + * An implementation of {@link ContactAccessor} that uses current Contacts API. + * This class should be used on Eclair or beyond, but would not work on any earlier + * release of Android. As a matter of fact, it could not even be loaded. + *

+ * This implementation has several advantages: + *

    + *
  • It sees contacts from multiple accounts. + *
  • It works with aggregated contacts. So for example, if the contact is the result + * of aggregation of two raw contacts from different accounts, it may return the name from + * one and the phone number from the other. + *
  • It is efficient because it uses the more efficient current API. + *
  • Not obvious in this particular example, but it has access to new kinds + * of data available exclusively through the new APIs. Exercise for the reader: add support + * for nickname (see {@link android.provider.ContactsContract.CommonDataKinds.Nickname}) or + * social status updates (see {@link android.provider.ContactsContract.StatusUpdates}). + *
+ */ + +public class ContactAccessorSdk5 extends ContactAccessor { + + /** + * Keep the photo size under the 1 MB blog limit. + */ + private static final long MAX_PHOTO_SIZE = 1048576; + + private static final String EMAIL_REGEXP = ".+@.+\\.+.+"; /* @.*/ + + private static final String ASSET_URL_PREFIX = "file:///android_asset/"; + + /** + * A static map that converts the JavaScript property name to Android database column name. + */ + private static final Map dbMap = new HashMap(); + + static { + dbMap.put("id", ContactsContract.Data.CONTACT_ID); + dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME); + dbMap.put("name", CommonDataKinds.StructuredName.DISPLAY_NAME); + dbMap.put("name.formatted", CommonDataKinds.StructuredName.DISPLAY_NAME); + dbMap.put("name.familyName", CommonDataKinds.StructuredName.FAMILY_NAME); + dbMap.put("name.givenName", CommonDataKinds.StructuredName.GIVEN_NAME); + dbMap.put("name.middleName", CommonDataKinds.StructuredName.MIDDLE_NAME); + dbMap.put("name.honorificPrefix", CommonDataKinds.StructuredName.PREFIX); + dbMap.put("name.honorificSuffix", CommonDataKinds.StructuredName.SUFFIX); + dbMap.put("nickname", CommonDataKinds.Nickname.NAME); + dbMap.put("phoneNumbers", CommonDataKinds.Phone.NUMBER); + dbMap.put("phoneNumbers.value", CommonDataKinds.Phone.NUMBER); + dbMap.put("emails", CommonDataKinds.Email.DATA); + dbMap.put("emails.value", CommonDataKinds.Email.DATA); + dbMap.put("addresses", CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); + dbMap.put("addresses.formatted", CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); + dbMap.put("addresses.streetAddress", CommonDataKinds.StructuredPostal.STREET); + dbMap.put("addresses.locality", CommonDataKinds.StructuredPostal.CITY); + dbMap.put("addresses.region", CommonDataKinds.StructuredPostal.REGION); + dbMap.put("addresses.postalCode", CommonDataKinds.StructuredPostal.POSTCODE); + dbMap.put("addresses.country", CommonDataKinds.StructuredPostal.COUNTRY); + dbMap.put("ims", CommonDataKinds.Im.DATA); + dbMap.put("ims.value", CommonDataKinds.Im.DATA); + dbMap.put("organizations", CommonDataKinds.Organization.COMPANY); + dbMap.put("organizations.name", CommonDataKinds.Organization.COMPANY); + dbMap.put("organizations.department", CommonDataKinds.Organization.DEPARTMENT); + dbMap.put("organizations.title", CommonDataKinds.Organization.TITLE); + dbMap.put("birthday", CommonDataKinds.Event.CONTENT_ITEM_TYPE); + dbMap.put("note", CommonDataKinds.Note.NOTE); + dbMap.put("photos.value", CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + //dbMap.put("categories.value", null); + dbMap.put("urls", CommonDataKinds.Website.URL); + dbMap.put("urls.value", CommonDataKinds.Website.URL); + } + + /** + * Create an contact accessor. + */ + public ContactAccessorSdk5(CordovaInterface context) { + mApp = context; + } + + /** + * This method takes the fields required and search options in order to produce an + * array of contacts that matches the criteria provided. + * @param fields an array of items to be used as search criteria + * @param options that can be applied to contact searching + * @return an array of contacts + */ + @Override + public JSONArray search(JSONArray fields, JSONObject options) { + // Get the find options + String searchTerm = ""; + int limit = Integer.MAX_VALUE; + boolean multiple = true; + boolean hasPhoneNumber = false; + + if (options != null) { + searchTerm = options.optString("filter"); + if (searchTerm.length() == 0) { + searchTerm = "%"; + } + else { + searchTerm = "%" + searchTerm + "%"; + } + + try { + multiple = options.getBoolean("multiple"); + if (!multiple) { + limit = 1; + } + } catch (JSONException e) { + // Multiple was not specified so we assume the default is true. + Log.e(LOG_TAG, e.getMessage(), e); + } + + try { + hasPhoneNumber = options.getBoolean("hasPhoneNumber"); + } catch (JSONException e) { + // hasPhoneNumber was not specified so we assume the default is false. + } + } + else { + searchTerm = "%"; + } + + // Loop through the fields the user provided to see what data should be returned. + HashMap populate = buildPopulationSet(options); + + // Build the ugly where clause and where arguments for one big query. + WhereOptions whereOptions = buildWhereClause(fields, searchTerm, hasPhoneNumber); + + // Get all the id's where the search term matches the fields passed in. + Cursor idCursor = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, + new String[] { ContactsContract.Data.CONTACT_ID }, + whereOptions.getWhere(), + whereOptions.getWhereArgs(), + ContactsContract.Data.CONTACT_ID + " ASC"); + + // Create a set of unique ids + Set contactIds = new HashSet(); + int idColumn = -1; + while (idCursor.moveToNext()) { + if (idColumn < 0) { + idColumn = idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + } + contactIds.add(idCursor.getString(idColumn)); + } + idCursor.close(); + + // Build a query that only looks at ids + WhereOptions idOptions = buildIdClause(contactIds, searchTerm, hasPhoneNumber); + + // Determine which columns we should be fetching. + HashSet columnsToFetch = new HashSet(); + columnsToFetch.add(ContactsContract.Data.CONTACT_ID); + columnsToFetch.add(ContactsContract.Data.RAW_CONTACT_ID); + columnsToFetch.add(ContactsContract.Data.MIMETYPE); + + if (isRequired("displayName", populate)) { + columnsToFetch.add(CommonDataKinds.StructuredName.DISPLAY_NAME); + } + if (isRequired("name", populate)) { + columnsToFetch.add(CommonDataKinds.StructuredName.FAMILY_NAME); + columnsToFetch.add(CommonDataKinds.StructuredName.GIVEN_NAME); + columnsToFetch.add(CommonDataKinds.StructuredName.MIDDLE_NAME); + columnsToFetch.add(CommonDataKinds.StructuredName.PREFIX); + columnsToFetch.add(CommonDataKinds.StructuredName.SUFFIX); + } + if (isRequired("phoneNumbers", populate)) { + columnsToFetch.add(CommonDataKinds.Phone._ID); + columnsToFetch.add(CommonDataKinds.Phone.NUMBER); + columnsToFetch.add(CommonDataKinds.Phone.TYPE); + } + if (isRequired("emails", populate)) { + columnsToFetch.add(CommonDataKinds.Email._ID); + columnsToFetch.add(CommonDataKinds.Email.DATA); + columnsToFetch.add(CommonDataKinds.Email.TYPE); + } + if (isRequired("addresses", populate)) { + columnsToFetch.add(CommonDataKinds.StructuredPostal._ID); + columnsToFetch.add(CommonDataKinds.Organization.TYPE); + columnsToFetch.add(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); + columnsToFetch.add(CommonDataKinds.StructuredPostal.STREET); + columnsToFetch.add(CommonDataKinds.StructuredPostal.CITY); + columnsToFetch.add(CommonDataKinds.StructuredPostal.REGION); + columnsToFetch.add(CommonDataKinds.StructuredPostal.POSTCODE); + columnsToFetch.add(CommonDataKinds.StructuredPostal.COUNTRY); + } + if (isRequired("organizations", populate)) { + columnsToFetch.add(CommonDataKinds.Organization._ID); + columnsToFetch.add(CommonDataKinds.Organization.TYPE); + columnsToFetch.add(CommonDataKinds.Organization.DEPARTMENT); + columnsToFetch.add(CommonDataKinds.Organization.COMPANY); + columnsToFetch.add(CommonDataKinds.Organization.TITLE); + } + if (isRequired("ims", populate)) { + columnsToFetch.add(CommonDataKinds.Im._ID); + columnsToFetch.add(CommonDataKinds.Im.DATA); + columnsToFetch.add(CommonDataKinds.Im.TYPE); + } + if (isRequired("note", populate)) { + columnsToFetch.add(CommonDataKinds.Note.NOTE); + } + if (isRequired("nickname", populate)) { + columnsToFetch.add(CommonDataKinds.Nickname.NAME); + } + if (isRequired("urls", populate)) { + columnsToFetch.add(CommonDataKinds.Website._ID); + columnsToFetch.add(CommonDataKinds.Website.URL); + columnsToFetch.add(CommonDataKinds.Website.TYPE); + } + if (isRequired("birthday", populate)) { + columnsToFetch.add(CommonDataKinds.Event.START_DATE); + columnsToFetch.add(CommonDataKinds.Event.TYPE); + } + if (isRequired("photos", populate)) { + columnsToFetch.add(CommonDataKinds.Photo._ID); + } + + // Do the id query + Cursor c = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, + columnsToFetch.toArray(new String[] {}), + idOptions.getWhere(), + idOptions.getWhereArgs(), + ContactsContract.Data.CONTACT_ID + " ASC"); + + JSONArray contacts = populateContactArray(limit, populate, c); + + if (!c.isClosed()) { + c.close(); + } + return contacts; + } + + /** + * A special search that finds one contact by id + * + * @param id contact to find by id + * @return a JSONObject representing the contact + * @throws JSONException + */ + public JSONObject getContactById(String id) throws JSONException { + // Call overloaded version with no desiredFields + return getContactById(id, null); + } + + @Override + public JSONObject getContactById(String id, JSONArray desiredFields) throws JSONException { + // Do the id query + Cursor c = mApp.getActivity().getContentResolver().query( + ContactsContract.Data.CONTENT_URI, + null, + ContactsContract.Data.RAW_CONTACT_ID + " = ? ", + new String[] { id }, + ContactsContract.Data.RAW_CONTACT_ID + " ASC"); + + HashMap populate = buildPopulationSet( + new JSONObject().put("desiredFields", desiredFields) + ); + + JSONArray contacts = populateContactArray(1, populate, c); + + if (!c.isClosed()) { + c.close(); + } + + if (contacts.length() == 1) { + return contacts.getJSONObject(0); + } else { + return null; + } + } + + /** + * Creates an array of contacts from the cursor you pass in + * + * @param limit max number of contacts for the array + * @param populate whether or not you should populate a certain value + * @param c the cursor + * @return a JSONArray of contacts + */ + private JSONArray populateContactArray(int limit, + HashMap populate, Cursor c) { + + String contactId = ""; + String rawId = ""; + String oldContactId = ""; + boolean newContact = true; + String mimetype = ""; + + JSONArray contacts = new JSONArray(); + JSONObject contact = new JSONObject(); + JSONArray organizations = new JSONArray(); + JSONArray addresses = new JSONArray(); + JSONArray phones = new JSONArray(); + JSONArray emails = new JSONArray(); + JSONArray ims = new JSONArray(); + JSONArray websites = new JSONArray(); + JSONArray photos = new JSONArray(); + + // Column indices + int colContactId = c.getColumnIndex(ContactsContract.Data.CONTACT_ID); + int colRawContactId = c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID); + int colMimetype = c.getColumnIndex(ContactsContract.Data.MIMETYPE); + int colDisplayName = c.getColumnIndex(CommonDataKinds.StructuredName.DISPLAY_NAME); + int colNote = c.getColumnIndex(CommonDataKinds.Note.NOTE); + int colNickname = c.getColumnIndex(CommonDataKinds.Nickname.NAME); + int colEventType = c.getColumnIndex(CommonDataKinds.Event.TYPE); + + if (c.getCount() > 0) { + while (c.moveToNext() && (contacts.length() <= (limit - 1))) { + try { + contactId = c.getString(colContactId); + rawId = c.getString(colRawContactId); + + // If we are in the first row set the oldContactId + if (c.getPosition() == 0) { + oldContactId = contactId; + } + + // When the contact ID changes we need to push the Contact object + // to the array of contacts and create new objects. + if (!oldContactId.equals(contactId)) { + // Populate the Contact object with it's arrays + // and push the contact into the contacts array + contacts.put(populateContact(contact, organizations, addresses, phones, + emails, ims, websites, photos)); + + // Clean up the objects + contact = new JSONObject(); + organizations = new JSONArray(); + addresses = new JSONArray(); + phones = new JSONArray(); + emails = new JSONArray(); + ims = new JSONArray(); + websites = new JSONArray(); + photos = new JSONArray(); + + // Set newContact to true as we are starting to populate a new contact + newContact = true; + } + + // When we detect a new contact set the ID and display name. + // These fields are available in every row in the result set returned. + if (newContact) { + newContact = false; + contact.put("id", contactId); + contact.put("rawId", rawId); + } + + // Grab the mimetype of the current row as it will be used in a lot of comparisons + mimetype = c.getString(colMimetype); + + if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) && isRequired("name", populate)) { + contact.put("displayName", c.getString(colDisplayName)); + } + + if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + && isRequired("name", populate)) { + contact.put("name", nameQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + && isRequired("phoneNumbers", populate)) { + phones.put(phoneQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE) + && isRequired("emails", populate)) { + emails.put(emailQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) + && isRequired("addresses", populate)) { + addresses.put(addressQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE) + && isRequired("organizations", populate)) { + organizations.put(organizationQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE) + && isRequired("ims", populate)) { + ims.put(imQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE) + && isRequired("note", populate)) { + contact.put("note", c.getString(colNote)); + } + else if (mimetype.equals(CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) + && isRequired("nickname", populate)) { + contact.put("nickname", c.getString(colNickname)); + } + else if (mimetype.equals(CommonDataKinds.Website.CONTENT_ITEM_TYPE) + && isRequired("urls", populate)) { + websites.put(websiteQuery(c)); + } + else if (mimetype.equals(CommonDataKinds.Event.CONTENT_ITEM_TYPE)) { + if (isRequired("birthday", populate) && + CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(colEventType)) { + + Date birthday = getBirthday(c); + if (birthday != null) { + contact.put("birthday", birthday.getTime()); + } + } + } + else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + && isRequired("photos", populate)) { + JSONObject photo = photoQuery(c, contactId); + if (photo != null) { + photos.put(photo); + } + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + + // Set the old contact ID + oldContactId = contactId; + + } + + // Push the last contact into the contacts array + if (contacts.length() < limit) { + contacts.put(populateContact(contact, organizations, addresses, phones, + emails, ims, websites, photos)); + } + } + c.close(); + return contacts; + } + + /** + * Builds a where clause all all the ids passed into the method + * @param contactIds a set of unique contact ids + * @param searchTerm what to search for + * @return an object containing the selection and selection args + */ + private WhereOptions buildIdClause(Set contactIds, String searchTerm, boolean hasPhoneNumber) { + WhereOptions options = new WhereOptions(); + + // If the user is searching for every contact then short circuit the method + // and return a shorter where clause to be searched. + if (searchTerm.equals("%") && !hasPhoneNumber) { + options.setWhere("(" + ContactsContract.Data.CONTACT_ID + " LIKE ? )"); + options.setWhereArgs(new String[] { searchTerm }); + return options; + } + + // This clause means that there are specific ID's to be populated + Iterator it = contactIds.iterator(); + StringBuffer buffer = new StringBuffer("("); + + while (it.hasNext()) { + buffer.append("'" + it.next() + "'"); + if (it.hasNext()) { + buffer.append(","); + } + } + buffer.append(")"); + + options.setWhere(ContactsContract.Data.CONTACT_ID + " IN " + buffer.toString()); + options.setWhereArgs(null); + + return options; + } + + /** + * Create a new contact using a JSONObject to hold all the data. + * @param contact + * @param organizations array of organizations + * @param addresses array of addresses + * @param phones array of phones + * @param emails array of emails + * @param ims array of instant messenger addresses + * @param websites array of websites + * @param photos + * @return + */ + private JSONObject populateContact(JSONObject contact, JSONArray organizations, + JSONArray addresses, JSONArray phones, JSONArray emails, + JSONArray ims, JSONArray websites, JSONArray photos) { + try { + // Only return the array if it has at least one entry + if (organizations.length() > 0) { + contact.put("organizations", organizations); + } + if (addresses.length() > 0) { + contact.put("addresses", addresses); + } + if (phones.length() > 0) { + contact.put("phoneNumbers", phones); + } + if (emails.length() > 0) { + contact.put("emails", emails); + } + if (ims.length() > 0) { + contact.put("ims", ims); + } + if (websites.length() > 0) { + contact.put("urls", websites); + } + if (photos.length() > 0) { + contact.put("photos", photos); + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return contact; + } + + /** + * Take the search criteria passed into the method and create a SQL WHERE clause. + * @param fields the properties to search against + * @param searchTerm the string to search for + * @return an object containing the selection and selection args + */ + private WhereOptions buildWhereClause(JSONArray fields, String searchTerm, boolean hasPhoneNumber) { + + ArrayList where = new ArrayList(); + ArrayList whereArgs = new ArrayList(); + + WhereOptions options = new WhereOptions(); + + /* + * Special case where the user wants all fields returned + */ + if (isWildCardSearch(fields)) { + // Get all contacts with all properties + if ("%".equals(searchTerm) && !hasPhoneNumber) { + options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )"); + options.setWhereArgs(new String[] { searchTerm }); + return options; + } else { + // Get all contacts that match the filter but return all properties + where.add("(" + dbMap.get("displayName") + " LIKE ? )"); + whereArgs.add(searchTerm); + where.add("(" + dbMap.get("name") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("nickname") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Nickname.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("phoneNumbers") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("emails") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Email.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("addresses") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("ims") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Im.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("organizations") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Organization.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("note") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Note.CONTENT_ITEM_TYPE); + where.add("(" + dbMap.get("urls") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Website.CONTENT_ITEM_TYPE); + } + } + + /* + * Special case for when the user wants all the contacts but + */ + if ("%".equals(searchTerm) && !hasPhoneNumber) { + options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )"); + options.setWhereArgs(new String[] { searchTerm }); + return options; + }else if(!("%".equals(searchTerm))){ + String key; + try { + //Log.d(LOG_TAG, "How many fields do we have = " + fields.length()); + for (int i = 0; i < fields.length(); i++) { + key = fields.getString(i); + + if (key.equals("id")) { + where.add("(" + dbMap.get(key) + " = ? )"); + whereArgs.add(searchTerm.substring(1, searchTerm.length() - 1)); + } + else if (key.startsWith("displayName")) { + where.add("(" + dbMap.get(key) + " LIKE ? )"); + whereArgs.add(searchTerm); + } + else if (key.startsWith("name")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("nickname")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Nickname.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("phoneNumbers")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("emails")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Email.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("addresses")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("ims")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Im.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("organizations")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Organization.CONTENT_ITEM_TYPE); + } + // else if (key.startsWith("birthday")) { + // where.add("(" + dbMap.get(key) + " LIKE ? AND " + // + ContactsContract.Data.MIMETYPE + " = ? )"); + // } + else if (key.startsWith("note")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Note.CONTENT_ITEM_TYPE); + } + else if (key.startsWith("urls")) { + where.add("(" + dbMap.get(key) + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); + whereArgs.add(searchTerm); + whereArgs.add(CommonDataKinds.Website.CONTENT_ITEM_TYPE); + } + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + } + + // Creating the where string + StringBuffer selection = new StringBuffer(); + for (int i = 0; i < where.size(); i++) { + selection.append(where.get(i)); + if (i != (where.size() - 1)) { + selection.append(" OR "); + } + } + + //Only contacts with phone number informed + if(hasPhoneNumber){ + if(where.size()>0){ + selection.insert(0,"("); + selection.append(") AND (" + ContactsContract.Contacts.HAS_PHONE_NUMBER + " = ?)"); + whereArgs.add("1"); + }else{ + selection.append("(" + ContactsContract.Contacts.HAS_PHONE_NUMBER + " = ?)"); + whereArgs.add("1"); + } + } + + options.setWhere(selection.toString()); + + // Creating the where args array + String[] selectionArgs = new String[whereArgs.size()]; + for (int i = 0; i < whereArgs.size(); i++) { + selectionArgs[i] = whereArgs.get(i); + } + options.setWhereArgs(selectionArgs); + + return options; + } + + /** + * If the user passes in the '*' wildcard character for search then they want all fields for each contact + * + * @param fields + * @return true if wildcard search requested, false otherwise + */ + private boolean isWildCardSearch(JSONArray fields) { + // Only do a wildcard search if we are passed ["*"] + if (fields.length() == 1) { + try { + if ("*".equals(fields.getString(0))) { + return true; + } + } catch (JSONException e) { + return false; + } + } + return false; + } + + /** + * Create a ContactOrganization JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactOrganization + */ + private JSONObject organizationQuery(Cursor cursor) { + JSONObject organization = new JSONObject(); + try { + organization.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Organization._ID))); + organization.put("pref", false); // Android does not store pref attribute + organization.put("type", getOrgType(cursor.getInt(cursor.getColumnIndex(CommonDataKinds.Organization.TYPE)))); + organization.put("department", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Organization.DEPARTMENT))); + organization.put("name", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Organization.COMPANY))); + organization.put("title", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Organization.TITLE))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return organization; + } + + /** + * Create a ContactAddress JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactAddress + */ + private JSONObject addressQuery(Cursor cursor) { + JSONObject address = new JSONObject(); + try { + address.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal._ID))); + address.put("pref", false); // Android does not store pref attribute + address.put("type", getAddressType(cursor.getInt(cursor.getColumnIndex(CommonDataKinds.Organization.TYPE)))); + address.put("formatted", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS))); + address.put("streetAddress", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal.STREET))); + address.put("locality", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal.CITY))); + address.put("region", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal.REGION))); + address.put("postalCode", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal.POSTCODE))); + address.put("country", cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredPostal.COUNTRY))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return address; + } + + /** + * Create a ContactName JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactName + */ + private JSONObject nameQuery(Cursor cursor) { + JSONObject contactName = new JSONObject(); + try { + String familyName = cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredName.FAMILY_NAME)); + String givenName = cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredName.GIVEN_NAME)); + String middleName = cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredName.MIDDLE_NAME)); + String honorificPrefix = cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredName.PREFIX)); + String honorificSuffix = cursor.getString(cursor.getColumnIndex(CommonDataKinds.StructuredName.SUFFIX)); + + // Create the formatted name + StringBuffer formatted = new StringBuffer(""); + if (!TextUtils.isEmpty(honorificPrefix)) { + formatted.append(honorificPrefix + " "); + } + if (!TextUtils.isEmpty(givenName)) { + formatted.append(givenName + " "); + } + if (!TextUtils.isEmpty(middleName)) { + formatted.append(middleName + " "); + } + if (!TextUtils.isEmpty(familyName)) { + formatted.append(familyName); + } + if (!TextUtils.isEmpty(honorificSuffix)) { + formatted.append(" " + honorificSuffix); + } + if (TextUtils.isEmpty(formatted)) { + formatted = null; + } + + contactName.put("familyName", familyName); + contactName.put("givenName", givenName); + contactName.put("middleName", middleName); + contactName.put("honorificPrefix", honorificPrefix); + contactName.put("honorificSuffix", honorificSuffix); + contactName.put("formatted", formatted); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return contactName; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject phoneQuery(Cursor cursor) { + JSONObject phoneNumber = new JSONObject(); + try { + phoneNumber.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Phone._ID))); + phoneNumber.put("pref", false); // Android does not store pref attribute + phoneNumber.put("value", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER))); + phoneNumber.put("type", getPhoneType(cursor.getInt(cursor.getColumnIndex(CommonDataKinds.Phone.TYPE)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (Exception excp) { + Log.e(LOG_TAG, excp.getMessage(), excp); + } + return phoneNumber; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject emailQuery(Cursor cursor) { + JSONObject email = new JSONObject(); + try { + email.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Email._ID))); + email.put("pref", false); // Android does not store pref attribute + email.put("value", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Email.DATA))); + email.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(CommonDataKinds.Email.TYPE)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return email; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject imQuery(Cursor cursor) { + JSONObject im = new JSONObject(); + try { + im.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Im._ID))); + im.put("pref", false); // Android does not store pref attribute + im.put("value", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Im.DATA))); + im.put("type", getImType(cursor.getString(cursor.getColumnIndex(CommonDataKinds.Im.PROTOCOL)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return im; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject websiteQuery(Cursor cursor) { + JSONObject website = new JSONObject(); + try { + website.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Website._ID))); + website.put("pref", false); // Android does not store pref attribute + website.put("value", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Website.URL))); + website.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(CommonDataKinds.Website.TYPE)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return website; + } + + /** + * Create a ContactField JSONObject + * @param contactId + * @return a JSONObject representing a ContactField + */ + private JSONObject photoQuery(Cursor cursor, String contactId) { + JSONObject photo = new JSONObject(); + Cursor photoCursor = null; + try { + photo.put("id", cursor.getString(cursor.getColumnIndex(CommonDataKinds.Photo._ID))); + photo.put("pref", false); + photo.put("type", "url"); + Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, (Long.valueOf(contactId))); + Uri photoUri = Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); + photo.put("value", photoUri.toString()); + + // Query photo existance + photoCursor = mApp.getActivity().getContentResolver().query(photoUri, new String[] {ContactsContract.Contacts.Photo.PHOTO}, null, null, null); + if (photoCursor == null) return null; + if (!photoCursor.moveToFirst()) { + photoCursor.close(); + return null; + } + photoCursor.close(); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (SQLiteException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } finally { + if(photoCursor != null && !photoCursor.isClosed()) { + photoCursor.close(); + } + } + return photo; + } + + @Override + /** + * This method will save a contact object into the devices contacts database. + * + * @param contact the contact to be saved. + * @returns the id if the contact is successfully saved, null otherwise. + */ + public String save(JSONObject contact) { + AccountManager mgr = AccountManager.get(mApp.getActivity()); + Account[] accounts = mgr.getAccounts(); + String accountName = null; + String accountType = null; + + if (accounts.length == 1) { + accountName = accounts[0].name; + accountType = accounts[0].type; + } + else if (accounts.length > 1) { + for (Account a : accounts) { + if (a.type.contains("eas") && a.name.matches(EMAIL_REGEXP)) /*Exchange ActiveSync*/{ + accountName = a.name; + accountType = a.type; + break; + } + } + if (accountName == null) { + for (Account a : accounts) { + if (a.type.contains("com.google") && a.name.matches(EMAIL_REGEXP)) /*Google sync provider*/{ + accountName = a.name; + accountType = a.type; + break; + } + } + } + if (accountName == null) { + for (Account a : accounts) { + if (a.name.matches(EMAIL_REGEXP)) /*Last resort, just look for an email address...*/{ + accountName = a.name; + accountType = a.type; + break; + } + } + } + } + + String id = getJsonString(contact, "id"); + if (id == null) { + // Create new contact + return createNewContact(contact, accountType, accountName); + } else { + // Modify existing contact + return modifyContact(id, contact, accountType, accountName); + } + } + + /** + * Creates a new contact and stores it in the database + * + * @param id the raw contact id which is required for linking items to the contact + * @param contact the contact to be saved + * @param account the account to be saved under + */ + private String modifyContact(String id, JSONObject contact, String accountType, String accountName) { + // Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact. + // But not needed to update existing values. + String rawId = getJsonString(contact, "rawId"); + + // Create a list of attributes to add to the contact database + ArrayList ops = new ArrayList(); + + //Add contact type + ops.add(ContentProviderOperation.newUpdate(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .build()); + + // Modify name + JSONObject name; + try { + String displayName = getJsonString(contact, "displayName"); + name = contact.getJSONObject("name"); + if (displayName != null || name != null) { + ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { id, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE }); + + if (displayName != null) { + builder.withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, displayName); + } + + String familyName = getJsonString(name, "familyName"); + if (familyName != null) { + builder.withValue(CommonDataKinds.StructuredName.FAMILY_NAME, familyName); + } + String middleName = getJsonString(name, "middleName"); + if (middleName != null) { + builder.withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, middleName); + } + String givenName = getJsonString(name, "givenName"); + if (givenName != null) { + builder.withValue(CommonDataKinds.StructuredName.GIVEN_NAME, givenName); + } + String honorificPrefix = getJsonString(name, "honorificPrefix"); + if (honorificPrefix != null) { + builder.withValue(CommonDataKinds.StructuredName.PREFIX, honorificPrefix); + } + String honorificSuffix = getJsonString(name, "honorificSuffix"); + if (honorificSuffix != null) { + builder.withValue(CommonDataKinds.StructuredName.SUFFIX, honorificSuffix); + } + + ops.add(builder.build()); + } + } catch (JSONException e1) { + Log.d(LOG_TAG, "Could not get name"); + } + + // Modify phone numbers + JSONArray phones = null; + try { + phones = contact.getJSONArray("phoneNumbers"); + if (phones != null) { + // Delete all the phones + if (phones.length() == 0) { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.Phone.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a phone + else { + for (int i = 0; i < phones.length(); i++) { + JSONObject phone = (JSONObject) phones.get(i); + String phoneId = getJsonString(phone, "id"); + // This is a new phone so do a DB insert + if (phoneId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + contentValues.put(CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")); + contentValues.put(CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing phone so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Phone._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { phoneId, CommonDataKinds.Phone.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")) + .withValue(CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) + .build()); + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get phone numbers"); + } + + // Modify emails + JSONArray emails = null; + try { + emails = contact.getJSONArray("emails"); + if (emails != null) { + // Delete all the emails + if (emails.length() == 0) { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.Email.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a email + else { + for (int i = 0; i < emails.length(); i++) { + JSONObject email = (JSONObject) emails.get(i); + String emailId = getJsonString(email, "id"); + // This is a new email so do a DB insert + if (emailId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE); + contentValues.put(CommonDataKinds.Email.DATA, getJsonString(email, "value")); + contentValues.put(CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing email so do a DB update + else { + String emailValue=getJsonString(email, "value"); + if(!emailValue.isEmpty()) { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Email._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { emailId, CommonDataKinds.Email.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Email.DATA, getJsonString(email, "value")) + .withValue(CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))) + .build()); + } else { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Email._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { emailId, CommonDataKinds.Email.CONTENT_ITEM_TYPE }) + .build()); + } + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Modify addresses + JSONArray addresses = null; + try { + addresses = contact.getJSONArray("addresses"); + if (addresses != null) { + // Delete all the addresses + if (addresses.length() == 0) { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a address + else { + for (int i = 0; i < addresses.length(); i++) { + JSONObject address = (JSONObject) addresses.get(i); + String addressId = getJsonString(address, "id"); + // This is a new address so do a DB insert + if (addressId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); + contentValues.put(CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))); + contentValues.put(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")); + contentValues.put(CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")); + contentValues.put(CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")); + contentValues.put(CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")); + contentValues.put(CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")); + contentValues.put(CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing address so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.StructuredPostal._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { addressId, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))) + .withValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")) + .withValue(CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) + .withValue(CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) + .withValue(CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) + .withValue(CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) + .withValue(CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) + .build()); + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get addresses"); + } + + // Modify organizations + JSONArray organizations = null; + try { + organizations = contact.getJSONArray("organizations"); + if (organizations != null) { + // Delete all the organizations + if (organizations.length() == 0) { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.Organization.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a organization + else { + for (int i = 0; i < organizations.length(); i++) { + JSONObject org = (JSONObject) organizations.get(i); + String orgId = getJsonString(org, "id"); + // This is a new organization so do a DB insert + if (orgId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE); + contentValues.put(CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))); + contentValues.put(CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")); + contentValues.put(CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")); + contentValues.put(CommonDataKinds.Organization.TITLE, getJsonString(org, "title")); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing organization so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Organization._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { orgId, CommonDataKinds.Organization.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))) + .withValue(CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")) + .withValue(CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) + .withValue(CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) + .build()); + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get organizations"); + } + + // Modify IMs + JSONArray ims = null; + try { + ims = contact.getJSONArray("ims"); + if (ims != null) { + // Delete all the ims + if (ims.length() == 0) { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.Im.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a im + else { + for (int i = 0; i < ims.length(); i++) { + JSONObject im = (JSONObject) ims.get(i); + String imId = getJsonString(im, "id"); + // This is a new IM so do a DB insert + if (imId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE); + contentValues.put(CommonDataKinds.Im.DATA, getJsonString(im, "value")); + contentValues.put(CommonDataKinds.Im.TYPE, getImType(getJsonString(im, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing IM so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Im._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { imId, CommonDataKinds.Im.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Im.DATA, getJsonString(im, "value")) + .withValue(CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type"))) + .build()); + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Modify note + String note = getJsonString(contact, "note"); + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { id, CommonDataKinds.Note.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Note.NOTE, note) + .build()); + + // Modify nickname + String nickname = getJsonString(contact, "nickname"); + if (nickname != null) { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { id, CommonDataKinds.Nickname.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Nickname.NAME, nickname) + .build()); + } + + // Modify urls + JSONArray websites = null; + try { + websites = contact.getJSONArray("urls"); + if (websites != null) { + // Delete all the websites + if (websites.length() == 0) { + Log.d(LOG_TAG, "This means we should be deleting all the phone numbers."); + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.Website.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a website + else { + for (int i = 0; i < websites.length(); i++) { + JSONObject website = (JSONObject) websites.get(i); + String websiteId = getJsonString(website, "id"); + // This is a new website so do a DB insert + if (websiteId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE); + contentValues.put(CommonDataKinds.Website.DATA, getJsonString(website, "value")); + contentValues.put(CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing website so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Website._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { websiteId, CommonDataKinds.Website.CONTENT_ITEM_TYPE }) + .withValue(CommonDataKinds.Website.DATA, getJsonString(website, "value")) + .withValue(CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))) + .build()); + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get websites"); + } + + // Modify birthday + Date birthday = getBirthday(contact); + if (birthday != null) { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=? AND " + + CommonDataKinds.Event.TYPE + "=?", + new String[]{id, CommonDataKinds.Event.CONTENT_ITEM_TYPE, "" + CommonDataKinds.Event.TYPE_BIRTHDAY}) + .withValue(CommonDataKinds.Event.TYPE, CommonDataKinds.Event.TYPE_BIRTHDAY) + .withValue(CommonDataKinds.Event.START_DATE, birthday.toString()) + .build()); + } + + // Modify photos + JSONArray photos = null; + try { + photos = contact.getJSONArray("photos"); + if (photos != null) { + // Delete all the photos + if (photos.length() == 0) { + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { "" + rawId, CommonDataKinds.Photo.CONTENT_ITEM_TYPE }) + .build()); + } + // Modify or add a photo + else { + for (int i = 0; i < photos.length(); i++) { + JSONObject photo = (JSONObject) photos.get(i); + String photoId = getJsonString(photo, "id"); + byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); + // This is a new photo so do a DB insert + if (photoId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); + contentValues.put(CommonDataKinds.Photo.PHOTO, bytes); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing photo so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(CommonDataKinds.Photo._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { photoId, CommonDataKinds.Photo.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) + .withValue(CommonDataKinds.Photo.PHOTO, bytes) + .build()); + } + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get photos"); + } + + boolean retVal = true; + + //Modify contact + try { + mApp.getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage(), e); + Log.e(LOG_TAG, Log.getStackTraceString(e), e); + retVal = false; + } catch (OperationApplicationException e) { + Log.e(LOG_TAG, e.getMessage(), e); + Log.e(LOG_TAG, Log.getStackTraceString(e), e); + retVal = false; + } + + // if the save was a success return the contact ID + if (retVal) { + return rawId; + } else { + return null; + } + } + + /** + * Add a website to a list of database actions to be performed + * + * @param ops the list of database actions + * @param website the item to be inserted + */ + private void insertWebsite(ArrayList ops, + JSONObject website) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Website.DATA, getJsonString(website, "value")) + .withValue(CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))) + .build()); + } + + /** + * Add an im to a list of database actions to be performed + * + * @param ops the list of database actions + * @param im the item to be inserted + */ + private void insertIm(ArrayList ops, JSONObject im) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Im.DATA, getJsonString(im, "value")) + .withValue(CommonDataKinds.Im.TYPE, getImType(getJsonString(im, "type"))) + .build()); + } + + /** + * Add an organization to a list of database actions to be performed + * + * @param ops the list of database actions + * @param org the item to be inserted + */ + private void insertOrganization(ArrayList ops, + JSONObject org) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))) + .withValue(CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")) + .withValue(CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) + .withValue(CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) + .build()); + } + + /** + * Add an address to a list of database actions to be performed + * + * @param ops the list of database actions + * @param address the item to be inserted + */ + private void insertAddress(ArrayList ops, + JSONObject address) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))) + .withValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")) + .withValue(CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) + .withValue(CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) + .withValue(CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) + .withValue(CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) + .withValue(CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) + .build()); + } + + /** + * Add an email to a list of database actions to be performed + * + * @param ops the list of database actions + * @param email the item to be inserted + */ + private void insertEmail(ArrayList ops, + JSONObject email) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Email.DATA, getJsonString(email, "value")) + .withValue(CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))) + .build()); + } + + /** + * Add a phone to a list of database actions to be performed + * + * @param ops the list of database actions + * @param phone the item to be inserted + */ + private void insertPhone(ArrayList ops, + JSONObject phone) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")) + .withValue(CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) + .build()); + } + + /** + * Add a phone to a list of database actions to be performed + * + * @param ops the list of database actions + * @param phone the item to be inserted + */ + private void insertPhoto(ArrayList ops, + JSONObject photo) { + byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Photo.PHOTO, bytes) + .build()); + } + + /** + * Gets the raw bytes from the supplied filename + * + * @param filename the file to read the bytes from + * @return a byte array + * @throws IOException + */ + private byte[] getPhotoBytes(String filename) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + try { + int bytesRead = 0; + long totalBytesRead = 0; + byte[] data = new byte[8192]; + InputStream in = getPathFromUri(filename); + + while ((bytesRead = in.read(data, 0, data.length)) != -1 && totalBytesRead <= MAX_PHOTO_SIZE) { + buffer.write(data, 0, bytesRead); + totalBytesRead += bytesRead; + } + + in.close(); + buffer.flush(); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (IOException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return buffer.toByteArray(); + } + + /** + * Get an input stream based on file path or uri content://, http://, file:// + * + * @param path path to file + * @return an input stream + * @throws IOException + */ + private InputStream getPathFromUri(String path) throws IOException { + if (path.startsWith("content:")) { + Uri uri = Uri.parse(path); + return mApp.getActivity().getContentResolver().openInputStream(uri); + } + + if (path.startsWith(ASSET_URL_PREFIX)) { + String assetRelativePath = path.replace(ASSET_URL_PREFIX, ""); + return mApp.getActivity().getAssets().open(assetRelativePath); + } + + if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("file:")) { + URL url = new URL(path); + return url.openStream(); + } + + return new FileInputStream(path); + } + + /** + * Creates a new contact and stores it in the database + * + * @param contact the contact to be saved + * @param account the account to be saved under + */ + private String createNewContact(JSONObject contact, String accountType, String accountName) { + // Create a list of attributes to add to the contact database + ArrayList ops = new ArrayList(); + + //Add contact type + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .build()); + + // Add name + JSONObject name = contact.optJSONObject("name"); + String displayName = getJsonString(contact, "displayName"); + if (displayName != null || name != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .withValue(CommonDataKinds.StructuredName.FAMILY_NAME, getJsonString(name, "familyName")) + .withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, getJsonString(name, "middleName")) + .withValue(CommonDataKinds.StructuredName.GIVEN_NAME, getJsonString(name, "givenName")) + .withValue(CommonDataKinds.StructuredName.PREFIX, getJsonString(name, "honorificPrefix")) + .withValue(CommonDataKinds.StructuredName.SUFFIX, getJsonString(name, "honorificSuffix")) + .build()); + } else { + Log.d(LOG_TAG, "Both \"name\" and \"displayName\" properties are empty"); + } + + //Add phone numbers + JSONArray phones = null; + try { + phones = contact.getJSONArray("phoneNumbers"); + if (phones != null) { + for (int i = 0; i < phones.length(); i++) { + JSONObject phone = (JSONObject) phones.get(i); + insertPhone(ops, phone); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get phone numbers"); + } + + // Add emails + JSONArray emails = null; + try { + emails = contact.getJSONArray("emails"); + if (emails != null) { + for (int i = 0; i < emails.length(); i++) { + JSONObject email = (JSONObject) emails.get(i); + insertEmail(ops, email); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Add addresses + JSONArray addresses = null; + try { + addresses = contact.getJSONArray("addresses"); + if (addresses != null) { + for (int i = 0; i < addresses.length(); i++) { + JSONObject address = (JSONObject) addresses.get(i); + insertAddress(ops, address); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get addresses"); + } + + // Add organizations + JSONArray organizations = null; + try { + organizations = contact.getJSONArray("organizations"); + if (organizations != null) { + for (int i = 0; i < organizations.length(); i++) { + JSONObject org = (JSONObject) organizations.get(i); + insertOrganization(ops, org); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get organizations"); + } + + // Add IMs + JSONArray ims = null; + try { + ims = contact.getJSONArray("ims"); + if (ims != null) { + for (int i = 0; i < ims.length(); i++) { + JSONObject im = (JSONObject) ims.get(i); + insertIm(ops, im); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Add note + String note = getJsonString(contact, "note"); + if (note != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Note.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Note.NOTE, note) + .build()); + } + + // Add nickname + String nickname = getJsonString(contact, "nickname"); + if (nickname != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Nickname.NAME, nickname) + .build()); + } + + // Add urls + JSONArray websites = null; + try { + websites = contact.getJSONArray("urls"); + if (websites != null) { + for (int i = 0; i < websites.length(); i++) { + JSONObject website = (JSONObject) websites.get(i); + insertWebsite(ops, website); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get websites"); + } + + // Add birthday + Date birthday = getBirthday(contact); + if (birthday != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Event.TYPE, CommonDataKinds.Event.TYPE_BIRTHDAY) + .withValue(CommonDataKinds.Event.START_DATE, birthday.toString()) + .build()); + } + + // Add photos + JSONArray photos = null; + try { + photos = contact.getJSONArray("photos"); + if (photos != null) { + for (int i = 0; i < photos.length(); i++) { + JSONObject photo = (JSONObject) photos.get(i); + insertPhoto(ops, photo); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get photos"); + } + + String newId = null; + //Add contact + try { + ContentProviderResult[] cpResults = mApp.getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + if (cpResults.length >= 0) { + newId = cpResults[0].uri.getLastPathSegment(); + } + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (OperationApplicationException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return newId; + } + + @Override + /** + * This method will remove a Contact from the database based on ID. + * @param id the unique ID of the contact to remove + */ + public boolean remove(String id) { + int result = 0; + Cursor cursor = mApp.getActivity().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + null, + ContactsContract.Contacts._ID + " = ?", + new String[] { id }, null); + + if (cursor.getCount() == 1) { + cursor.moveToFirst(); + String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)); + Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey); + result = mApp.getActivity().getContentResolver().delete(uri, null, null); + } else { + Log.d(LOG_TAG, "Could not find contact with ID"); + } + + return (result > 0) ? true : false; + } + + /** + * Gets birthday date from contact JSON object + * @param contact an object to get birthday from + * @return birthday or null, if the field isn't present or + * is malformed in the contact + */ + private Date getBirthday(JSONObject contact) { + try { + Long timestamp = contact.getLong("birthday"); + return new Date(timestamp); + } catch (JSONException e) { + Log.e(LOG_TAG, "Could not get birthday from JSON object", e); + return null; + } + } + + /** + * Gets birthday from contacts database cursor object + * @param c cursor for the contact + * @return birthday or null, if birthday column is empty or + * the value can't be parsed into valid date object + */ + private Date getBirthday(Cursor c) { + int colBirthday = c.getColumnIndex(CommonDataKinds.Event.START_DATE); + + try { + return Date.valueOf(c.getString(colBirthday)); + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "Failed to get birthday for contact from cursor", e); + return null; + } + } + + /************************************************************************** + * + * All methods below this comment are used to convert from JavaScript + * text types to Android integer types and vice versa. + * + *************************************************************************/ + + /** + * Converts a string from the W3C Contact API to it's Android int value. + * @param string + * @return Android int value + */ + private int getPhoneType(String string) { + + int type = Phone.TYPE_OTHER; + + if (string != null) { + String lowerType = string.toLowerCase(Locale.getDefault()); + + if ("home".equals(lowerType)) { + return Phone.TYPE_HOME; + } + else if ("mobile".equals(lowerType)) { + return Phone.TYPE_MOBILE; + } + else if ("work".equals(lowerType)) { + return Phone.TYPE_WORK; + } + else if ("work fax".equals(lowerType)) { + return Phone.TYPE_FAX_WORK; + } + else if ("home fax".equals(lowerType)) { + return Phone.TYPE_FAX_HOME; + } + else if ("fax".equals(lowerType)) { + return Phone.TYPE_FAX_WORK; + } + else if ("pager".equals(lowerType)) { + return Phone.TYPE_PAGER; + } + else if ("other".equals(lowerType)) { + return Phone.TYPE_OTHER; + } + else if ("car".equals(lowerType)) { + return Phone.TYPE_CAR; + } + else if ("company main".equals(lowerType)) { + return Phone.TYPE_COMPANY_MAIN; + } + else if ("isdn".equals(lowerType)) { + return Phone.TYPE_ISDN; + } + else if ("main".equals(lowerType)) { + return Phone.TYPE_MAIN; + } + else if ("other fax".equals(lowerType)) { + return Phone.TYPE_OTHER_FAX; + } + else if ("radio".equals(lowerType)) { + return Phone.TYPE_RADIO; + } + else if ("telex".equals(lowerType)) { + return Phone.TYPE_TELEX; + } + else if ("work mobile".equals(lowerType)) { + return Phone.TYPE_WORK_MOBILE; + } + else if ("work pager".equals(lowerType)) { + return Phone.TYPE_WORK_PAGER; + } + else if ("assistant".equals(lowerType)) { + return Phone.TYPE_ASSISTANT; + } + else if ("mms".equals(lowerType)) { + return Phone.TYPE_MMS; + } + else if ("callback".equals(lowerType)) { + return Phone.TYPE_CALLBACK; + } + else if ("tty ttd".equals(lowerType)) { + return Phone.TYPE_TTY_TDD; + } + else if ("custom".equals(lowerType)) { + return Phone.TYPE_CUSTOM; + } + } + return type; + } + + /** + * getPhoneType converts an Android phone type into a string + * @param type + * @return phone type as string. + */ + private String getPhoneType(int type) { + String stringType; + + switch (type) { + case Phone.TYPE_CUSTOM: + stringType = "custom"; + break; + case Phone.TYPE_FAX_HOME: + stringType = "home fax"; + break; + case Phone.TYPE_FAX_WORK: + stringType = "work fax"; + break; + case Phone.TYPE_HOME: + stringType = "home"; + break; + case Phone.TYPE_MOBILE: + stringType = "mobile"; + break; + case Phone.TYPE_PAGER: + stringType = "pager"; + break; + case Phone.TYPE_WORK: + stringType = "work"; + break; + case Phone.TYPE_CALLBACK: + stringType = "callback"; + break; + case Phone.TYPE_CAR: + stringType = "car"; + break; + case Phone.TYPE_COMPANY_MAIN: + stringType = "company main"; + break; + case Phone.TYPE_OTHER_FAX: + stringType = "other fax"; + break; + case Phone.TYPE_RADIO: + stringType = "radio"; + break; + case Phone.TYPE_TELEX: + stringType = "telex"; + break; + case Phone.TYPE_TTY_TDD: + stringType = "tty tdd"; + break; + case Phone.TYPE_WORK_MOBILE: + stringType = "work mobile"; + break; + case Phone.TYPE_WORK_PAGER: + stringType = "work pager"; + break; + case Phone.TYPE_ASSISTANT: + stringType = "assistant"; + break; + case Phone.TYPE_MMS: + stringType = "mms"; + break; + case Phone.TYPE_ISDN: + stringType = "isdn"; + break; + case Phone.TYPE_OTHER: + default: + stringType = "other"; + break; + } + return stringType; + } + + /** + * Converts a string from the W3C Contact API to it's Android int value. + * @param string + * @return Android int value + */ + private int getContactType(String string) { + int type = CommonDataKinds.Email.TYPE_OTHER; + if (string != null) { + + String lowerType = string.toLowerCase(Locale.getDefault()); + + if ("home".equals(lowerType)) { + return CommonDataKinds.Email.TYPE_HOME; + } + else if ("work".equals(lowerType)) { + return CommonDataKinds.Email.TYPE_WORK; + } + else if ("other".equals(lowerType)) { + return CommonDataKinds.Email.TYPE_OTHER; + } + else if ("mobile".equals(lowerType)) { + return CommonDataKinds.Email.TYPE_MOBILE; + } + else if ("custom".equals(lowerType)) { + return CommonDataKinds.Email.TYPE_CUSTOM; + } + } + return type; + } + + /** + * getPhoneType converts an Android phone type into a string + * @param type + * @return phone type as string. + */ + private String getContactType(int type) { + String stringType; + switch (type) { + case CommonDataKinds.Email.TYPE_CUSTOM: + stringType = "custom"; + break; + case CommonDataKinds.Email.TYPE_HOME: + stringType = "home"; + break; + case CommonDataKinds.Email.TYPE_WORK: + stringType = "work"; + break; + case CommonDataKinds.Email.TYPE_MOBILE: + stringType = "mobile"; + break; + case CommonDataKinds.Email.TYPE_OTHER: + default: + stringType = "other"; + break; + } + return stringType; + } + + /** + * Converts a string from the W3C Contact API to it's Android int value. + * @param string + * @return Android int value + */ + private int getOrgType(String string) { + int type = CommonDataKinds.Organization.TYPE_OTHER; + if (string != null) { + String lowerType = string.toLowerCase(Locale.getDefault()); + if ("work".equals(lowerType)) { + return CommonDataKinds.Organization.TYPE_WORK; + } + else if ("other".equals(lowerType)) { + return CommonDataKinds.Organization.TYPE_OTHER; + } + else if ("custom".equals(lowerType)) { + return CommonDataKinds.Organization.TYPE_CUSTOM; + } + } + return type; + } + + /** + * getPhoneType converts an Android phone type into a string + * @param type + * @return phone type as string. + */ + private String getOrgType(int type) { + String stringType; + switch (type) { + case CommonDataKinds.Organization.TYPE_CUSTOM: + stringType = "custom"; + break; + case CommonDataKinds.Organization.TYPE_WORK: + stringType = "work"; + break; + case CommonDataKinds.Organization.TYPE_OTHER: + default: + stringType = "other"; + break; + } + return stringType; + } + + /** + * Converts a string from the W3C Contact API to it's Android int value. + * @param string + * @return Android int value + */ + private int getAddressType(String string) { + int type = CommonDataKinds.StructuredPostal.TYPE_OTHER; + if (string != null) { + String lowerType = string.toLowerCase(Locale.getDefault()); + + if ("work".equals(lowerType)) { + return CommonDataKinds.StructuredPostal.TYPE_WORK; + } + else if ("other".equals(lowerType)) { + return CommonDataKinds.StructuredPostal.TYPE_OTHER; + } + else if ("home".equals(lowerType)) { + return CommonDataKinds.StructuredPostal.TYPE_HOME; + } + } + return type; + } + + /** + * getPhoneType converts an Android phone type into a string + * @param type + * @return phone type as string. + */ + private String getAddressType(int type) { + String stringType; + switch (type) { + case CommonDataKinds.StructuredPostal.TYPE_HOME: + stringType = "home"; + break; + case CommonDataKinds.StructuredPostal.TYPE_WORK: + stringType = "work"; + break; + case CommonDataKinds.StructuredPostal.TYPE_OTHER: + default: + stringType = "other"; + break; + } + return stringType; + } + + /** + * Converts a string from the W3C Contact API to it's Android int value. + * @param string + * @return Android int value + */ + private int getImType(String string) { + int type = CommonDataKinds.Im.PROTOCOL_CUSTOM; + if (string != null) { + String lowerType = string.toLowerCase(Locale.getDefault()); + + if ("aim".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_AIM; + } + else if ("google talk".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK; + } + else if ("icq".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_ICQ; + } + else if ("jabber".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_JABBER; + } + else if ("msn".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_MSN; + } + else if ("netmeeting".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_NETMEETING; + } + else if ("qq".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_QQ; + } + else if ("skype".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_SKYPE; + } + else if ("yahoo".equals(lowerType)) { + return CommonDataKinds.Im.PROTOCOL_YAHOO; + } + } + return type; + } + + /** + * getPhoneType converts an Android phone type into a string + * @param type + * @return phone type as string. + */ + @SuppressWarnings("unused") + private String getImType(int type) { + String stringType; + switch (type) { + case CommonDataKinds.Im.PROTOCOL_AIM: + stringType = "AIM"; + break; + case CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK: + stringType = "Google Talk"; + break; + case CommonDataKinds.Im.PROTOCOL_ICQ: + stringType = "ICQ"; + break; + case CommonDataKinds.Im.PROTOCOL_JABBER: + stringType = "Jabber"; + break; + case CommonDataKinds.Im.PROTOCOL_MSN: + stringType = "MSN"; + break; + case CommonDataKinds.Im.PROTOCOL_NETMEETING: + stringType = "NetMeeting"; + break; + case CommonDataKinds.Im.PROTOCOL_QQ: + stringType = "QQ"; + break; + case CommonDataKinds.Im.PROTOCOL_SKYPE: + stringType = "Skype"; + break; + case CommonDataKinds.Im.PROTOCOL_YAHOO: + stringType = "Yahoo"; + break; + default: + stringType = "custom"; + break; + } + return stringType; + } + +} diff --git a/plugins/cordova-plugin-contacts/src/android/ContactInfoDTO.java b/plugins/cordova-plugin-contacts/src/android/ContactInfoDTO.java new file mode 100644 index 0000000..a61b6e7 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/android/ContactInfoDTO.java @@ -0,0 +1,59 @@ +/* + 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.contacts; + +import java.util.HashMap; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class ContactInfoDTO { + + String displayName; + JSONObject name; + JSONArray organizations; + JSONArray addresses; + JSONArray phones; + JSONArray emails; + JSONArray ims; + JSONArray websites; + JSONArray photos; + String note; + String nickname; + String birthday; + HashMap desiredFieldsWithVals; + + public ContactInfoDTO() { + + displayName = ""; + name = new JSONObject(); + organizations = new JSONArray(); + addresses = new JSONArray(); + phones = new JSONArray(); + emails = new JSONArray(); + ims = new JSONArray(); + websites = new JSONArray(); + photos = new JSONArray(); + note = ""; + nickname = ""; + desiredFieldsWithVals = new HashMap(); + } + +} diff --git a/plugins/cordova-plugin-contacts/src/android/ContactManager.java b/plugins/cordova-plugin-contacts/src/android/ContactManager.java new file mode 100644 index 0000000..448302d --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/android/ContactManager.java @@ -0,0 +1,306 @@ +/* + 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.contacts; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaActivity; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.os.Build; +import android.os.Bundle; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.RawContacts; +import android.util.Log; + +import java.lang.reflect.Method; + +public class ContactManager extends CordovaPlugin { + + private ContactAccessor contactAccessor; + private CallbackContext callbackContext; // The callback context from which we were invoked. + private JSONArray executeArgs; + + private static final String LOG_TAG = "Contact Query"; + + public static final int UNKNOWN_ERROR = 0; + public static final int INVALID_ARGUMENT_ERROR = 1; + public static final int TIMEOUT_ERROR = 2; + public static final int PENDING_OPERATION_ERROR = 3; + public static final int IO_ERROR = 4; + public static final int NOT_SUPPORTED_ERROR = 5; + public static final int OPERATION_CANCELLED_ERROR = 6; + public static final int PERMISSION_DENIED_ERROR = 20; + private static final int CONTACT_PICKER_RESULT = 1000; + public static String [] permissions; + + + //Request code for the permissions picker (Pick is async and uses intents) + public static final int SEARCH_REQ_CODE = 0; + public static final int SAVE_REQ_CODE = 1; + public static final int REMOVE_REQ_CODE = 2; + public static final int PICK_REQ_CODE = 3; + + public static final String READ = Manifest.permission.READ_CONTACTS; + public static final String WRITE = Manifest.permission.WRITE_CONTACTS; + + + /** + * Constructor. + */ + public ContactManager() { + + } + + + protected void getReadPermission(int requestCode) + { + PermissionHelper.requestPermission(this, requestCode, READ); + } + + + protected void getWritePermission(int requestCode) + { + PermissionHelper.requestPermission(this, requestCode, WRITE); + } + + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArray of arguments for the plugin. + * @param callbackContext The callback context used when calling back into JavaScript. + * @return True if the action was valid, false otherwise. + */ + public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { + + this.callbackContext = callbackContext; + this.executeArgs = args; + + /** + * Check to see if we are on an Android 1.X device. If we are return an error as we + * do not support this as of Cordova 1.0. + */ + if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ContactManager.NOT_SUPPORTED_ERROR)); + return true; + } + + /** + * Only create the contactAccessor after we check the Android version or the program will crash + * older phones. + */ + if (this.contactAccessor == null) { + this.contactAccessor = new ContactAccessorSdk5(this.cordova); + } + + if (action.equals("search")) { + if(PermissionHelper.hasPermission(this, READ)) { + search(executeArgs); + } + else + { + getReadPermission(SEARCH_REQ_CODE); + } + } + else if (action.equals("save")) { + if(PermissionHelper.hasPermission(this, WRITE)) + { + save(executeArgs); + } + else + { + getWritePermission(SAVE_REQ_CODE); + } + } + else if (action.equals("remove")) { + if(PermissionHelper.hasPermission(this, WRITE)) + { + remove(executeArgs); + } + else + { + getWritePermission(REMOVE_REQ_CODE); + } + } + else if (action.equals("pickContact")) { + if(PermissionHelper.hasPermission(this, READ)) + { + pickContactAsync(); + } + else + { + getReadPermission(PICK_REQ_CODE); + } + } + else { + return false; + } + return true; + } + + private void remove(JSONArray args) throws JSONException { + final String contactId = args.getString(0); + this.cordova.getThreadPool().execute(new Runnable() { + public void run() { + if (contactAccessor.remove(contactId)) { + callbackContext.success(); + } else { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR)); + } + } + }); + } + + private void save(JSONArray args) throws JSONException { + final JSONObject contact = args.getJSONObject(0); + this.cordova.getThreadPool().execute(new Runnable(){ + public void run() { + JSONObject res = null; + String id = contactAccessor.save(contact); + if (id != null) { + try { + res = contactAccessor.getContactById(id); + } catch (JSONException e) { + Log.e(LOG_TAG, "JSON fail.", e); + } + } + if (res != null) { + callbackContext.success(res); + } else { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR)); + } + } + }); + } + + private void search(JSONArray args) throws JSONException + { + final JSONArray filter = args.getJSONArray(0); + final JSONObject options = args.get(1) == null ? null : args.getJSONObject(1); + this.cordova.getThreadPool().execute(new Runnable() { + public void run() { + JSONArray res = contactAccessor.search(filter, options); + callbackContext.success(res); + } + }); + } + + + /** + * Launches the Contact Picker to select a single contact. + */ + private void pickContactAsync() { + final CordovaPlugin plugin = (CordovaPlugin) this; + Runnable worker = new Runnable() { + public void run() { + Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + plugin.cordova.startActivityForResult(plugin, contactPickerIntent, CONTACT_PICKER_RESULT); + } + }; + this.cordova.getThreadPool().execute(worker); + } + + /** + * Called when user picks contact. + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + * @throws JSONException + */ + public void onActivityResult(int requestCode, int resultCode, final Intent intent) { + if (requestCode == CONTACT_PICKER_RESULT) { + if (resultCode == Activity.RESULT_OK) { + String contactId = intent.getData().getLastPathSegment(); + // to populate contact data we require Raw Contact ID + // so we do look up for contact raw id first + Cursor c = this.cordova.getActivity().getContentResolver().query(RawContacts.CONTENT_URI, + new String[] {RawContacts._ID}, RawContacts.CONTACT_ID + " = " + contactId, null, null); + if (!c.moveToFirst()) { + this.callbackContext.error("Error occured while retrieving contact raw id"); + return; + } + String id = c.getString(c.getColumnIndex(RawContacts._ID)); + c.close(); + + try { + JSONObject contact = contactAccessor.getContactById(id); + this.callbackContext.success(contact); + return; + } catch (JSONException e) { + Log.e(LOG_TAG, "JSON fail.", e); + } + } else if (resultCode == Activity.RESULT_CANCELED) { + callbackContext.error(OPERATION_CANCELLED_ERROR); + return; + } + + this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR)); + } + } + + public void onRequestPermissionResult(int requestCode, String[] permissions, + int[] grantResults) throws JSONException + { + for(int r:grantResults) + { + if(r == PackageManager.PERMISSION_DENIED) + { + this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR)); + return; + } + } + switch(requestCode) + { + case SEARCH_REQ_CODE: + search(executeArgs); + break; + case SAVE_REQ_CODE: + save(executeArgs); + break; + case REMOVE_REQ_CODE: + remove(executeArgs); + break; + case PICK_REQ_CODE: + pickContactAsync(); + break; + } + } + + /** + * This plugin launches an external Activity when a contact is picked, so we + * need to implement the save/restore API in case the Activity gets killed + * by the OS while it's in the background. We don't actually save anything + * because picking a contact doesn't take in any arguments. + */ + public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) { + this.callbackContext = callbackContext; + this.contactAccessor = new ContactAccessorSdk5(this.cordova); + } +} diff --git a/plugins/cordova-plugin-contacts/src/android/PermissionHelper.java b/plugins/cordova-plugin-contacts/src/android/PermissionHelper.java new file mode 100644 index 0000000..29a7cb1 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/android/PermissionHelper.java @@ -0,0 +1,138 @@ +/* + 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.contacts; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; + +import android.content.pm.PackageManager; + +/** + * This class provides reflective methods for permission requesting and checking so that plugins + * written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions. + */ +public class PermissionHelper { + private static final String LOG_TAG = "CordovaPermissionHelper"; + + /** + * Requests a "dangerous" permission for the application at runtime. This is a helper method + * alternative to cordovaInterface.requestPermission() that does not require the project to be + * built with cordova-android 5.0.0+ + * + * @param plugin The plugin the permission is being requested for + * @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult() + * along with the result of the permission request + * @param permission The permission to be requested + */ + public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) { + PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission}); + } + + /** + * Requests "dangerous" permissions for the application at runtime. This is a helper method + * alternative to cordovaInterface.requestPermissions() that does not require the project to be + * built with cordova-android 5.0.0+ + * + * @param plugin The plugin the permissions are being requested for + * @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult() + * along with the result of the permissions request + * @param permissions The permissions to be requested + */ + public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) { + try { + Method requestPermission = CordovaInterface.class.getDeclaredMethod( + "requestPermissions", CordovaPlugin.class, int.class, String[].class); + + // If there is no exception, then this is cordova-android 5.0.0+ + requestPermission.invoke(plugin.cordova, plugin, requestCode, permissions); + } catch (NoSuchMethodException noSuchMethodException) { + // cordova-android version is less than 5.0.0, so permission is implicitly granted + LOG.d(LOG_TAG, "No need to request permissions " + Arrays.toString(permissions)); + + // Notify the plugin that all were granted by using more reflection + deliverPermissionResult(plugin, requestCode, permissions); + } catch (IllegalAccessException illegalAccessException) { + // Should never be caught; this is a public method + LOG.e(LOG_TAG, "IllegalAccessException when requesting permissions " + Arrays.toString(permissions), illegalAccessException); + } catch(InvocationTargetException invocationTargetException) { + // This method does not throw any exceptions, so this should never be caught + LOG.e(LOG_TAG, "invocationTargetException when requesting permissions " + Arrays.toString(permissions), invocationTargetException); + } + } + + /** + * Checks at runtime to see if the application has been granted a permission. This is a helper + * method alternative to cordovaInterface.hasPermission() that does not require the project to + * be built with cordova-android 5.0.0+ + * + * @param plugin The plugin the permission is being checked against + * @param permission The permission to be checked + * + * @return True if the permission has already been granted and false otherwise + */ + public static boolean hasPermission(CordovaPlugin plugin, String permission) { + try { + Method hasPermission = CordovaInterface.class.getDeclaredMethod("hasPermission", String.class); + + // If there is no exception, then this is cordova-android 5.0.0+ + return (Boolean) hasPermission.invoke(plugin.cordova, permission); + } catch (NoSuchMethodException noSuchMethodException) { + // cordova-android version is less than 5.0.0, so permission is implicitly granted + LOG.d(LOG_TAG, "No need to check for permission " + permission); + return true; + } catch (IllegalAccessException illegalAccessException) { + // Should never be caught; this is a public method + LOG.e(LOG_TAG, "IllegalAccessException when checking permission " + permission, illegalAccessException); + } catch(InvocationTargetException invocationTargetException) { + // This method does not throw any exceptions, so this should never be caught + LOG.e(LOG_TAG, "invocationTargetException when checking permission " + permission, invocationTargetException); + } + return false; + } + + private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) { + // Generate the request results + int[] requestResults = new int[permissions.length]; + Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED); + + try { + Method onRequestPermissionResult = CordovaPlugin.class.getDeclaredMethod( + "onRequestPermissionResult", int.class, String[].class, int[].class); + + onRequestPermissionResult.invoke(plugin, requestCode, permissions, requestResults); + } catch (NoSuchMethodException noSuchMethodException) { + // Should never be caught since the plugin must be written for cordova-android 5.0.0+ if it + // made it to this point + LOG.e(LOG_TAG, "NoSuchMethodException when delivering permissions results", noSuchMethodException); + } catch (IllegalAccessException illegalAccessException) { + // Should never be caught; this is a public method + LOG.e(LOG_TAG, "IllegalAccessException when delivering permissions results", illegalAccessException); + } catch(InvocationTargetException invocationTargetException) { + // This method may throw a JSONException. We are just duplicating cordova-android's + // exception handling behavior here; all it does is log the exception in CordovaActivity, + // print the stacktrace, and ignore it + LOG.e(LOG_TAG, "InvocationTargetException when delivering permissions results", invocationTargetException); + } + } +} diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactActivity.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactActivity.js new file mode 100644 index 0000000..0205194 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactActivity.js @@ -0,0 +1,31 @@ +/* + * + * 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 ContactActivity = function (args) { + this.direction = args.direction || null; + this.description = args.description || ""; + this.mimeType = args.mimeType || ""; + this.timestamp = new Date(parseInt(args.timestamp, 10)) || null; +}; + +Object.defineProperty(ContactActivity, "INCOMING", {"value": true}); +Object.defineProperty(ContactActivity, "OUTGOING", {"value": false}); + +module.exports = ContactActivity; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactAddress.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactAddress.js new file mode 100644 index 0000000..30e9cbd --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactAddress.js @@ -0,0 +1,35 @@ +/* + * + * 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 ContactAddress = function (properties) { + this.type = properties && properties.type ? properties.type : ""; + this.streetAddress = properties && properties.streetAddress ? properties.streetAddress : ""; + this.streetOther = properties && properties.streetOther ? properties.streetOther : ""; + this.locality = properties && properties.locality ? properties.locality : ""; + this.region = properties && properties.region ? properties.region : ""; + this.postalCode = properties && properties.postalCode ? properties.postalCode : ""; + this.country = properties && properties.country ? properties.country : ""; +}; + +Object.defineProperty(ContactAddress, "HOME", {"value": "home"}); +Object.defineProperty(ContactAddress, "WORK", {"value": "work"}); +Object.defineProperty(ContactAddress, "OTHER", {"value": "other"}); + +module.exports = ContactAddress; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactError.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactError.js new file mode 100644 index 0000000..a44a546 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactError.js @@ -0,0 +1,35 @@ +/* + * + * 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 ContactError = function (code, msg) { + this.code = code; + this.message = msg; +}; + +Object.defineProperty(ContactError, "UNKNOWN_ERROR", { "value": 0 }); +Object.defineProperty(ContactError, "INVALID_ARGUMENT_ERROR", { "value": 1 }); +Object.defineProperty(ContactError, "TIMEOUT_ERROR", { "value": 2 }); +Object.defineProperty(ContactError, "PENDING_OPERATION_ERROR", { "value": 3 }); +Object.defineProperty(ContactError, "IO_ERROR", { "value": 4 }); +Object.defineProperty(ContactError, "NOT_SUPPORTED_ERROR", { "value": 5 }); +Object.defineProperty(ContactError, "PERMISSION_DENIED_ERROR", { "value": 20 }); + +module.exports = ContactError; + diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactField.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactField.js new file mode 100644 index 0000000..9651700 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactField.js @@ -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. + * +*/ +var ContactField = function (type, value) { + this.type = type || ""; + this.value = value || ""; +}; + +Object.defineProperty(ContactField, "HOME", {"value": "home"}); +Object.defineProperty(ContactField, "WORK", {"value": "work"}); +Object.defineProperty(ContactField, "OTHER", {"value": "other"}); +Object.defineProperty(ContactField, "MOBILE", {"value": "mobile"}); +Object.defineProperty(ContactField, "DIRECT", {"value": "direct"}); + +module.exports = ContactField; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactFindOptions.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactFindOptions.js new file mode 100644 index 0000000..6f8ccfe --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactFindOptions.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. + * +*/ + +/** + * ContactFindOptions. + * @constructor + * @param filter search fields + * @param sort sort fields and order + * @param limit max number of contacts to return + * @param favorite if set, only favorite contacts will be returned + */ + +var ContactFindOptions = function (filter, sort, limit, favorite) { + this.filter = filter || null; + this.sort = sort || null; + this.limit = limit || -1; // -1 for returning all results + this.favorite = favorite || false; + this.includeAccounts = []; + this.excludeAccounts = []; +}; + +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_GIVEN_NAME", { "value": 0 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_FAMILY_NAME", { "value": 1 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_ORGANIZATION_NAME", { "value": 2 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_PHONE", { "value": 3 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_EMAIL", { "value": 4 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_BBMPIN", { "value": 5 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_LINKEDIN", { "value": 6 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_TWITTER", { "value": 7 }); +Object.defineProperty(ContactFindOptions, "SEARCH_FIELD_VIDEO_CHAT", { "value": 8 }); + +Object.defineProperty(ContactFindOptions, "SORT_FIELD_GIVEN_NAME", { "value": 0 }); +Object.defineProperty(ContactFindOptions, "SORT_FIELD_FAMILY_NAME", { "value": 1 }); +Object.defineProperty(ContactFindOptions, "SORT_FIELD_ORGANIZATION_NAME", { "value": 2 }); + +module.exports = ContactFindOptions; + diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactName.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactName.js new file mode 100644 index 0000000..80136c9 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactName.js @@ -0,0 +1,44 @@ +/* + * + * 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 toFormattedName(properties) { + var formatted = ""; + if (properties && properties.givenName) { + formatted = properties.givenName; + if (properties && properties.familyName) { + formatted += " " + properties.familyName; + } + } + return formatted; +} + +var ContactName = function (properties) { + this.familyName = properties && properties.familyName ? properties.familyName : ""; + this.givenName = properties && properties.givenName ? properties.givenName : ""; + this.formatted = toFormattedName(properties); + this.middleName = properties && properties.middleName ? properties.middleName : ""; + this.honorificPrefix = properties && properties.honorificPrefix ? properties.honorificPrefix : ""; + this.honorificSuffix = properties && properties.honorificSuffix ? properties.honorificSuffix : ""; + this.phoneticFamilyName = properties && properties.phoneticFamilyName ? properties.phoneticFamilyName : ""; + this.phoneticGivenName = properties && properties.phoneticGivenName ? properties.phoneticGivenName : ""; +}; + +module.exports = ContactName; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactNews.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactNews.js new file mode 100644 index 0000000..e47a4cd --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactNews.js @@ -0,0 +1,31 @@ +/* + * + * 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 ContactNews = function (args) { + this.title = args.title || ""; + this.body = args.body || ""; + this.articleSource = args.articleSource || ""; + this.companies = args.companies || []; + this.publishedAt = new Date(parseInt(args.publishedAt, 10)) || null; + this.uri = args.uri || ""; + this.type = args.type || ""; +}; + +module.exports = ContactNews; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactOrganization.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactOrganization.js new file mode 100644 index 0000000..fc953f2 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactOrganization.js @@ -0,0 +1,27 @@ +/* + * + * 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 ContactOrganization = function (properties) { + this.name = properties && properties.name ? properties.name : ""; + this.department = properties && properties.department ? properties.department : ""; + this.title = properties && properties.title ? properties.title : ""; +}; + +module.exports = ContactOrganization; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/ContactPhoto.js b/plugins/cordova-plugin-contacts/src/blackberry10/ContactPhoto.js new file mode 100644 index 0000000..4604710 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/ContactPhoto.js @@ -0,0 +1,28 @@ +/* + * + * 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 ContactPhoto = function (originalFilePath, pref) { + this.originalFilePath = originalFilePath || ""; + this.pref = pref || false; + this.largeFilePath = ""; + this.smallFilePath = ""; +}; + +module.exports = ContactPhoto; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/contactConsts.js b/plugins/cordova-plugin-contacts/src/blackberry10/contactConsts.js new file mode 100644 index 0000000..ef25206 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/contactConsts.js @@ -0,0 +1,225 @@ +/* +* Copyright 2012 Research In Motion Limited. +* +* 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. +*/ + +var ATTRIBUTE_KIND, + ATTRIBUTE_SUBKIND, + kindAttributeMap = {}, + subKindAttributeMap = {}, + _TITLE = 26, + _START_DATE = 43, + _END_DATE = 44; + +function populateKindAttributeMap() { + ATTRIBUTE_KIND = { + Invalid: 0, + Phone: 1, + Fax: 2, + Pager: 3, + Email: 4, + Website: 5, + Feed: 6, + Profile: 7, + Family: 8, + Person: 9, + Date: 10, + Group: 11, + Name: 12, + StockSymbol: 13, + Ranking: 14, + OrganizationAffiliation: 15, + Education: 16, + Note: 17, + InstantMessaging: 18, + VideoChat: 19, + ConnectionCount: 20, + Hidden: 21, + Biography: 22, + Sound: 23, + Notification: 24, + MessageSound: 25, + MessageNotification: 26 + }; + + kindAttributeMap[ATTRIBUTE_KIND.Phone] = "phoneNumbers"; + kindAttributeMap[ATTRIBUTE_KIND.Fax] = "faxNumbers"; + kindAttributeMap[ATTRIBUTE_KIND.Pager] = "pagerNumber"; + kindAttributeMap[ATTRIBUTE_KIND.Email] = "emails"; + kindAttributeMap[ATTRIBUTE_KIND.Website] = "urls"; + kindAttributeMap[ATTRIBUTE_KIND.Profile] = "socialNetworks"; + kindAttributeMap[ATTRIBUTE_KIND.OrganizationAffiliation] = "organizations"; + kindAttributeMap[ATTRIBUTE_KIND.Education] = "education"; + kindAttributeMap[ATTRIBUTE_KIND.Note] = "note"; + kindAttributeMap[ATTRIBUTE_KIND.InstantMessaging] = "ims"; + kindAttributeMap[ATTRIBUTE_KIND.VideoChat] = "videoChat"; + kindAttributeMap[ATTRIBUTE_KIND.Sound] = "ringtone"; +} + +function populateSubKindAttributeMap() { + ATTRIBUTE_SUBKIND = { + Invalid: 0, + Other: 1, + Home: 2, + Work: 3, + PhoneMobile: 4, + FaxDirect: 5, + Blog: 6, + WebsiteResume: 7, + WebsitePortfolio: 8, + WebsitePersonal: 9, + WebsiteCompany: 10, + ProfileFacebook: 11, + ProfileTwitter: 12, + ProfileLinkedIn: 13, + ProfileGist: 14, + ProfileTungle: 15, + FamilySpouse: 16, + FamilyChild: 17, + FamilyParent: 18, + PersonManager: 19, + PersonAssistant: 20, + DateBirthday: 21, + DateAnniversary: 22, + GroupDepartment: 23, + NameGiven: 24, + NameSurname: 25, + Title: _TITLE, + NameSuffix: 27, + NameMiddle: 28, + NameNickname: 29, + NameAlias: 30, + NameDisplayName: 31, + NamePhoneticGiven: 32, + NamePhoneticSurname: 33, + StockSymbolNyse: 34, + StockSymbolNasdaq: 35, + StockSymbolTse: 36, + StockSymbolLse: 37, + StockSymbolTsx: 38, + RankingKlout: 39, + RankingTrstRank: 40, + OrganizationAffiliationName: 41, + OrganizationAffiliationPhoneticName: 42, + OrganizationAffiliationTitle: _TITLE, + StartDate: _START_DATE, + EndDate: _END_DATE, + OrganizationAffiliationDetails: 45, + EducationInstitutionName: 46, + EducationStartDate: _START_DATE, + EducationEndDate: _END_DATE, + EducationDegree: 47, + EducationConcentration: 48, + EducationActivities: 49, + EducationNotes: 50, + InstantMessagingBbmPin: 51, + InstantMessagingAim: 52, + InstantMessagingAliwangwang: 53, + InstantMessagingGoogleTalk: 54, + InstantMessagingSametime: 55, + InstantMessagingIcq: 56, + InstantMessagingIrc: 57, + InstantMessagingJabber: 58, + InstantMessagingMsLcs: 59, + InstantMessagingMsn: 60, + InstantMessagingQq: 61, + InstantMessagingSkype: 62, + InstantMessagingYahooMessenger: 63, + InstantMessagingYahooMessengerJapan: 64, + VideoChatBbPlaybook: 65, + HiddenLinkedIn: 66, + HiddenFacebook: 67, + HiddenTwitter: 68, + ConnectionCountLinkedIn: 69, + ConnectionCountFacebook: 70, + ConnectionCountTwitter: 71, + HiddenChecksum: 72, + HiddenSpeedDial: 73, + BiographyFacebook: 74, + BiographyTwitter: 75, + BiographyLinkedIn: 76, + SoundRingtone: 77, + SimContactType: 78, + EcoID: 79, + Personal: 80, + StockSymbolAll: 81, + NotificationVibration: 82, + NotificationLED: 83, + MessageNotificationVibration: 84, + MessageNotificationLED: 85, + MessageNotificationDuringCall: 86, + VideoChatPin: 87 + }; + + subKindAttributeMap[ATTRIBUTE_SUBKIND.Other] = "other"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.Home] = "home"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.Work] = "work"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.PhoneMobile] = "mobile"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.FaxDirect] = "direct"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.Blog] = "blog"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.WebsiteResume] = "resume"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.WebsitePortfolio] = "portfolio"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.WebsitePersonal] = "personal"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.WebsiteCompany] = "company"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.ProfileFacebook] = "facebook"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.ProfileTwitter] = "twitter"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.ProfileLinkedIn] = "linkedin"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.ProfileGist] = "gist"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.ProfileTungle] = "tungle"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.DateBirthday] = "birthday"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.DateAnniversary] = "anniversary"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NameGiven] = "givenName"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NameSurname] = "familyName"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.Title] = "honorificPrefix"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NameSuffix] = "honorificSuffix"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NameMiddle] = "middleName"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NamePhoneticGiven] = "phoneticGivenName"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NamePhoneticSurname] = "phoneticFamilyName"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NameNickname] = "nickname"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.NameDisplayName] = "displayName"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.OrganizationAffiliationName] = "name"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.OrganizationAffiliationDetails] = "department"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.Title] = "title"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingBbmPin] = "BbmPin"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingAim] = "Aim"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingAliwangwang] = "Aliwangwang"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingGoogleTalk] = "GoogleTalk"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingSametime] = "Sametime"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingIcq] = "Icq"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingJabber] = "Jabber"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingMsLcs] = "MsLcs"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingSkype] = "Skype"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingYahooMessenger] = "YahooMessenger"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.InstantMessagingYahooMessengerJapan] = "YahooMessegerJapan"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.VideoChatBbPlaybook] = "BbPlaybook"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.SoundRingtone] = "ringtone"; + subKindAttributeMap[ATTRIBUTE_SUBKIND.Personal] = "personal"; +} + +module.exports = { + getKindAttributeMap: function () { + if (!ATTRIBUTE_KIND) { + populateKindAttributeMap(); + } + + return kindAttributeMap; + }, + getSubKindAttributeMap: function () { + if (!ATTRIBUTE_SUBKIND) { + populateSubKindAttributeMap(); + } + + return subKindAttributeMap; + } +}; diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/contactUtils.js b/plugins/cordova-plugin-contacts/src/blackberry10/contactUtils.js new file mode 100644 index 0000000..e1f7d67 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/contactUtils.js @@ -0,0 +1,228 @@ +/* + * + * 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 self, + ContactFindOptions = require("./ContactFindOptions"), + ContactError = require("./ContactError"), + ContactName = require("./ContactName"), + ContactOrganization = require("./ContactOrganization"), + ContactAddress = require("./ContactAddress"), + ContactField = require("./ContactField"), + contactConsts = require("./contactConsts"), + ContactPhoto = require("./ContactPhoto"), + ContactNews = require("./ContactNews"), + ContactActivity = require("./ContactActivity"); + +function populateFieldArray(contactProps, field, ClassName) { + if (contactProps[field]) { + var list = [], + obj; + + contactProps[field].forEach(function (args) { + if (ClassName === ContactField) { + list.push(new ClassName(args.type, args.value)); + } else if (ClassName === ContactPhoto) { + obj = new ContactPhoto(args.originalFilePath, args.pref); + obj.largeFilePath = args.largeFilePath; + obj.smallFilePath = args.smallFilePath; + list.push(obj); + } else if (ClassName === ContactNews) { + obj = new ContactNews(args); + list.push(obj); + } else if (ClassName === ContactActivity) { + obj = new ContactActivity(args); + list.push(obj); + } else { + list.push(new ClassName(args)); + } + }); + contactProps[field] = list; + } +} + +function populateDate(contactProps, field) { + if (contactProps[field]) { + contactProps[field] = new Date(contactProps[field]); + } +} + +function validateFindArguments(findOptions) { + var error = false; + + // findOptions is mandatory + if (!findOptions) { + error = true; + } else { + // findOptions.filter is optional + if (findOptions.filter) { + findOptions.filter.forEach(function (f) { + switch (f.fieldName) { + case ContactFindOptions.SEARCH_FIELD_GIVEN_NAME: + case ContactFindOptions.SEARCH_FIELD_FAMILY_NAME: + case ContactFindOptions.SEARCH_FIELD_ORGANIZATION_NAME: + case ContactFindOptions.SEARCH_FIELD_PHONE: + case ContactFindOptions.SEARCH_FIELD_EMAIL: + case ContactFindOptions.SEARCH_FIELD_BBMPIN: + case ContactFindOptions.SEARCH_FIELD_LINKEDIN: + case ContactFindOptions.SEARCH_FIELD_TWITTER: + case ContactFindOptions.SEARCH_FIELD_VIDEO_CHAT: + break; + default: + error = true; + } + + if (!f.fieldValue) { + error = true; + } + }); + } + + //findOptions.limit is optional + if (findOptions.limit) { + if (typeof findOptions.limit !== "number") { + error = true; + } + } + + //findOptions.favorite is optional + if (findOptions.favorite) { + if (typeof findOptions.favorite !== "boolean") { + error = true; + } + } + + // findOptions.sort is optional + if (!error && findOptions.sort && Array.isArray(findOptions.sort)) { + findOptions.sort.forEach(function (s) { + switch (s.fieldName) { + case ContactFindOptions.SORT_FIELD_GIVEN_NAME: + case ContactFindOptions.SORT_FIELD_FAMILY_NAME: + case ContactFindOptions.SORT_FIELD_ORGANIZATION_NAME: + break; + default: + error = true; + } + + if (s.desc === undefined || typeof s.desc !== "boolean") { + error = true; + } + }); + } + + if (!error && findOptions.includeAccounts) { + if (!Array.isArray(findOptions.includeAccounts)) { + error = true; + } else { + findOptions.includeAccounts.forEach(function (acct) { + if (!error && (!acct.id || window.isNaN(window.parseInt(acct.id, 10)))) { + error = true; + } + }); + } + } + + if (!error && findOptions.excludeAccounts) { + if (!Array.isArray(findOptions.excludeAccounts)) { + error = true; + } else { + findOptions.excludeAccounts.forEach(function (acct) { + if (!error && (!acct.id || window.isNaN(window.parseInt(acct.id, 10)))) { + error = true; + } + }); + } + } + } + return !error; +} + +function validateContactsPickerFilter(filter) { + var isValid = true, + availableFields = {}; + + if (typeof(filter) === "undefined") { + isValid = false; + } else { + if (filter && Array.isArray(filter)) { + availableFields = contactConsts.getKindAttributeMap(); + filter.forEach(function (e) { + isValid = isValid && Object.getOwnPropertyNames(availableFields).reduce( + function (found, key) { + return found || availableFields[key] === e; + }, false); + }); + } + } + + return isValid; +} + +function validateContactsPickerOptions(options) { + var isValid = false, + mode = options.mode; + + if (typeof(options) === "undefined") { + isValid = false; + } else { + isValid = mode === ContactPickerOptions.MODE_SINGLE || mode === ContactPickerOptions.MODE_MULTIPLE || mode === ContactPickerOptions.MODE_ATTRIBUTE; + + // if mode is attribute, fields must be defined + if (mode === ContactPickerOptions.MODE_ATTRIBUTE && !validateContactsPickerFilter(options.fields)) { + isValid = false; + } + } + + return isValid; +} + +self = module.exports = { + populateContact: function (contact) { + if (contact.name) { + contact.name = new ContactName(contact.name); + } + + populateFieldArray(contact, "addresses", ContactAddress); + populateFieldArray(contact, "organizations", ContactOrganization); + populateFieldArray(contact, "emails", ContactField); + populateFieldArray(contact, "phoneNumbers", ContactField); + populateFieldArray(contact, "faxNumbers", ContactField); + populateFieldArray(contact, "pagerNumbers", ContactField); + populateFieldArray(contact, "ims", ContactField); + populateFieldArray(contact, "socialNetworks", ContactField); + populateFieldArray(contact, "urls", ContactField); + populateFieldArray(contact, "photos", ContactPhoto); + populateFieldArray(contact, "news", ContactNews); + populateFieldArray(contact, "activities", ContactActivity); + // TODO categories + + populateDate(contact, "birthday"); + populateDate(contact, "anniversary"); + }, + invokeErrorCallback: function (errorCallback, code) { + if (errorCallback) { + errorCallback(new ContactError(code)); + } + }, + validateFindArguments: validateFindArguments, + validateContactsPickerFilter: validateContactsPickerFilter, + validateContactsPickerOptions: validateContactsPickerOptions +}; + diff --git a/plugins/cordova-plugin-contacts/src/blackberry10/index.js b/plugins/cordova-plugin-contacts/src/blackberry10/index.js new file mode 100644 index 0000000..09a4bd2 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/blackberry10/index.js @@ -0,0 +1,374 @@ +/* + * Copyright 2013 Research In Motion Limited. + * + * 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. + */ +var pimContacts, + contactUtils = require("./contactUtils"), + contactConsts = require("./contactConsts"), + ContactError = require("./ContactError"), + ContactName = require("./ContactName"), + ContactFindOptions = require("./ContactFindOptions"), + noop = function () {}; + +function getAccountFilters(options) { + if (options.includeAccounts) { + options.includeAccounts = options.includeAccounts.map(function (acct) { + return acct.id.toString(); + }); + } + + if (options.excludeAccounts) { + options.excludeAccounts = options.excludeAccounts.map(function (acct) { + return acct.id.toString(); + }); + } +} + +function populateSearchFields(fields) { + var i, + l, + key, + searchFieldsObject = {}, + searchFields = []; + + for (i = 0, l = fields.length; i < l; i++) { + if (fields[i] === "*") { + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_GIVEN_NAME] = true; + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_FAMILY_NAME] = true; + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_PHONE] = true; + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_EMAIL] = true; + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_ORGANIZATION_NAME] = true; + } else if (fields[i] === "displayName" || fields[i] === "name") { + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_GIVEN_NAME] = true; + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_FAMILY_NAME] = true; + } else if (fields[i] === "nickname") { + // not supported by Cascades + } else if (fields[i] === "phoneNumbers") { + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_PHONE] = true; + } else if (fields[i] === "emails") { + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_EMAIL] = true; + } else if (field === "addresses") { + // not supported by Cascades + } else if (field === "ims") { + // not supported by Cascades + } else if (field === "organizations") { + searchFieldsObject[ContactFindOptions.SEARCH_FIELD_ORGANIZATION_NAME] = true; + } else if (field === "birthday") { + // not supported by Cascades + } else if (field === "note") { + // not supported by Cascades + } else if (field === "photos") { + // not supported by Cascades + } else if (field === "categories") { + // not supported by Cascades + } else if (field === "urls") { + // not supported by Cascades + } + } + + for (key in searchFieldsObject) { + if (searchFieldsObject.hasOwnProperty(key)) { + searchFields.push(window.parseInt(key)); + } + } + + return searchFields; +} + +function convertBirthday(birthday) { + //Convert date string from native to milliseconds since epoch for cordova-js + var birthdayInfo; + if (birthday) { + birthdayInfo = birthday.split("-"); + return new Date(birthdayInfo[0], birthdayInfo[1] - 1, birthdayInfo[2]).getTime(); + } else { + return null; + } +} + +function processJnextSaveData(result, JnextData) { + var data = JnextData, + birthdayInfo; + + if (data._success === true) { + data.birthday = convertBirthday(data.birthday); + result.callbackOk(data, false); + } else { + result.callbackError(data.code, false); + } +} + +function processJnextRemoveData(result, JnextData) { + var data = JnextData; + + if (data._success === true) { + result.callbackOk(data); + } else { + result.callbackError(ContactError.UNKNOWN_ERROR, false); + } +} + +function processJnextFindData(eventId, eventHandler, JnextData) { + var data = JnextData, + i, + l, + more = false, + resultsObject = {}, + birthdayInfo; + + if (data.contacts) { + for (i = 0, l = data.contacts.length; i < l; i++) { + data.contacts[i].birthday = convertBirthday(data.contacts[i].birthday); + data.contacts[i].name = new ContactName(data.contacts[i].name); + } + } else { + data.contacts = []; // if JnextData.contacts return null, return an empty array + } + + if (data._success === true) { + eventHandler.error = false; + } + + if (eventHandler.multiple) { + // Concatenate results; do not add the same contacts + for (i = 0, l = eventHandler.searchResult.length; i < l; i++) { + resultsObject[eventHandler.searchResult[i].id] = true; + } + + for (i = 0, l = data.contacts.length; i < l; i++) { + if (resultsObject[data.contacts[i].id]) { + // Already existing + } else { + eventHandler.searchResult.push(data.contacts[i]); + } + } + + // check if more search is required + eventHandler.searchFieldIndex++; + if (eventHandler.searchFieldIndex < eventHandler.searchFields.length) { + more = true; + } + } else { + eventHandler.searchResult = data.contacts; + } + + if (more) { + pimContacts.getInstance().invokeJnextSearch(eventId); + } else { + if (eventHandler.error) { + eventHandler.result.callbackError(data.code, false); + } else { + eventHandler.result.callbackOk(eventHandler.searchResult, false); + } + } +} + +module.exports = { + search: function (successCb, failCb, args, env) { + var cordovaFindOptions = {}, + result = new PluginResult(args, env), + key; + + for (key in args) { + if (args.hasOwnProperty(key)) { + cordovaFindOptions[key] = JSON.parse(decodeURIComponent(args[key])); + } + } + + pimContacts.getInstance().find(cordovaFindOptions, result, processJnextFindData); + result.noResult(true); + }, + save: function (successCb, failCb, args, env) { + var attributes = {}, + result = new PluginResult(args, env), + key, + nativeEmails = []; + + attributes = JSON.parse(decodeURIComponent(args[0])); + + //convert birthday format for our native .so file + if (attributes.birthday) { + attributes.birthday = new Date(attributes.birthday).toDateString(); + } + + if (attributes.emails) { + attributes.emails.forEach(function (email) { + if (email.value) { + if (email.type) { + nativeEmails.push({ "type" : email.type, "value" : email.value }); + } else { + nativeEmails.push({ "type" : "home", "value" : email.value }); + } + } + }); + attributes.emails = nativeEmails; + } + + if (attributes.id !== null) { + attributes.id = window.parseInt(attributes.id); + } + + attributes._eventId = result.callbackId; + pimContacts.getInstance().save(attributes, result, processJnextSaveData); + result.noResult(true); + }, + remove: function (successCb, failCb, args, env) { + var result = new PluginResult(args, env), + attributes = { + "contactId": window.parseInt(JSON.parse(decodeURIComponent(args[0]))), + "_eventId": result.callbackId + }; + + if (!window.isNaN(attributes.contactId)) { + pimContacts.getInstance().remove(attributes, result, processJnextRemoveData); + result.noResult(true); + } else { + result.error(ContactError.UNKNOWN_ERROR); + result.noResult(false); + } + } +}; + +/////////////////////////////////////////////////////////////////// +// JavaScript wrapper for JNEXT plugin +/////////////////////////////////////////////////////////////////// + +JNEXT.PimContacts = function () +{ + var self = this, + hasInstance = false; + + self.find = function (cordovaFindOptions, pluginResult, handler) { + //register find eventHandler for when JNEXT onEvent fires + self.eventHandlers[cordovaFindOptions.callbackId] = { + "result" : pluginResult, + "action" : "find", + "multiple" : cordovaFindOptions[1].filter ? true : false, + "fields" : cordovaFindOptions[0], + "searchFilter" : cordovaFindOptions[1].filter, + "searchFields" : cordovaFindOptions[1].filter ? populateSearchFields(cordovaFindOptions[0]) : null, + "searchFieldIndex" : 0, + "searchResult" : [], + "handler" : handler, + "error" : true + }; + + self.invokeJnextSearch(cordovaFindOptions.callbackId); + return ""; + }; + + self.invokeJnextSearch = function(eventId) { + var jnextArgs = {}, + findHandler = self.eventHandlers[eventId]; + + jnextArgs._eventId = eventId; + jnextArgs.fields = findHandler.fields; + jnextArgs.options = {}; + jnextArgs.options.filter = []; + + if (findHandler.multiple) { + jnextArgs.options.filter.push({ + "fieldName" : findHandler.searchFields[findHandler.searchFieldIndex], + "fieldValue" : findHandler.searchFilter + }); + //findHandler.searchFieldIndex++; + } + + JNEXT.invoke(self.m_id, "find " + JSON.stringify(jnextArgs)); + } + + self.getContact = function (args) { + return JSON.parse(JNEXT.invoke(self.m_id, "getContact " + JSON.stringify(args))); + }; + + self.save = function (args, pluginResult, handler) { + //register save eventHandler for when JNEXT onEvent fires + self.eventHandlers[args._eventId] = { + "result" : pluginResult, + "action" : "save", + "handler" : handler + }; + JNEXT.invoke(self.m_id, "save " + JSON.stringify(args)); + return ""; + }; + + self.remove = function (args, pluginResult, handler) { + //register remove eventHandler for when JNEXT onEvent fires + self.eventHandlers[args._eventId] = { + "result" : pluginResult, + "action" : "remove", + "handler" : handler + }; + JNEXT.invoke(self.m_id, "remove " + JSON.stringify(args)); + return ""; + }; + + self.getId = function () { + return self.m_id; + }; + + self.getContactAccounts = function () { + var value = JNEXT.invoke(self.m_id, "getContactAccounts"); + return JSON.parse(value); + }; + + self.init = function () { + if (!JNEXT.require("libpimcontacts")) { + return false; + } + + self.m_id = JNEXT.createObject("libpimcontacts.PimContacts"); + + if (self.m_id === "") { + return false; + } + + JNEXT.registerEvents(self); + }; + + // Handle data coming back from JNEXT native layer. Each async function registers a handler and a PluginResult object. + // When JNEXT fires onEvent we parse the result string back into JSON and trigger the appropriate handler (eventHandlers map + // uses callbackId as key), along with the actual data coming back from the native layer. Each function may have its own way of + // processing native data so we do not do any processing here. + + self.onEvent = function (strData) { + var arData = strData.split(" "), + strEventDesc = arData[0], + eventHandler, + args = {}; + + if (strEventDesc === "result") { + args.result = escape(strData.split(" ").slice(2).join(" ")); + eventHandler = self.eventHandlers[arData[1]]; + if (eventHandler.action === "save" || eventHandler.action === "remove") { + eventHandler.handler(eventHandler.result, JSON.parse(decodeURIComponent(args.result))); + } else if (eventHandler.action === "find") { + eventHandler.handler(arData[1], eventHandler, JSON.parse(decodeURIComponent(args.result))); + } + } + }; + + self.m_id = ""; + self.eventHandlers = {}; + + self.getInstance = function () { + if (!hasInstance) { + self.init(); + hasInstance = true; + } + return self; + }; +}; + +pimContacts = new JNEXT.PimContacts(); diff --git a/plugins/cordova-plugin-contacts/src/firefoxos/ContactsProxy.js b/plugins/cordova-plugin-contacts/src/firefoxos/ContactsProxy.js new file mode 100644 index 0000000..6be8d8c --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/firefoxos/ContactsProxy.js @@ -0,0 +1,464 @@ +/* + * + * 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. + * +*/ + +// Cordova contact definition: +// http://cordova.apache.org/docs/en/2.5.0/cordova_contacts_contacts.md.html#Contact +// FxOS contact definition: +// https://developer.mozilla.org/en-US/docs/Web/API/mozContact + + +var Contact = require('./Contact'); +var ContactField = require('./ContactField'); +var ContactAddress = require('./ContactAddress'); +var ContactName = require('./ContactName'); + +// XXX: a hack to check if id is "empty". Cordova inserts a +// string "this string is supposed to be a unique identifier that will +// never show up on a device" if id is empty +function _hasId(id) { + if (!id || id.indexOf(' ') >= 0) { + return false; + } + return true; +} + +// Extend mozContact prototype to provide update from Cordova +function updateFromCordova(contact, fromContact) { + + function exportContactFieldArray(contactFieldArray, key) { + if (!key) { + key = 'value'; + } + var arr = []; + for (var i=0; i < contactFieldArray.length; i++) { + arr.push(contactFieldArray[i][key]); + }; + return arr; + } + + function exportAddress(addresses) { + // TODO: check moz address format + var arr = []; + + for (var i=0; i < addresses.length; i++) { + var addr = {}; + for (var key in addresses[i]) { + if (key == 'formatted' || key == 'id') { + continue; + } else if (key == 'type') { + addr[key] = [addresses[i][key]]; + } else if (key == 'country') { + addr['countryName'] = addresses[i][key]; + } else { + addr[key] = addresses[i][key]; + } + } + arr.push(addr); + } + return arr; + } + + function exportContactField(data) { + var contactFields = []; + for (var i=0; i < data.length; i++) { + var item = data[i]; + if (item.value) { + var itemData = {value: item.value}; + if (item.type) { + itemData.type = [item.type]; + } + if (item.pref) { + itemData.pref = item.pref; + } + contactFields.push(itemData); + } + } + return contactFields; + } + // adding simple fields [contactField, eventualMozContactField] + var nameFields = [['givenName'], ['familyName'], + ['honorificPrefix'], ['honorificSuffix'], + ['middleName', 'additionalName']]; + var baseArrayFields = [['displayName', 'name'], ['nickname']]; + var baseStringFields = []; + var j = 0; while(field = nameFields[j++]) { + if (fromContact.name[field[0]]) { + contact[field[1] || field[0]] = fromContact.name[field[0]].split(' '); + } + } + j = 0; while(field = baseArrayFields[j++]) { + if (fromContact[field[0]]) { + contact[field[1] || field[0]] = fromContact[field[0]].split(' '); + } + } + j = 0; while(field = baseStringFields[j++]) { + if (fromContact[field[0]]) { + contact[field[1] || field[0]] = fromContact[field[0]]; + } + } + if (fromContact.birthday) { + contact.bday = new Date(fromContact.birthday); + } + if (fromContact.emails) { + var emails = exportContactField(fromContact.emails) + contact.email = emails; + } + if (fromContact.categories) { + contact.category = exportContactFieldArray(fromContact.categories); + } + if (fromContact.addresses) { + contact.adr = exportAddress(fromContact.addresses); + } + if (fromContact.phoneNumbers) { + contact.tel = exportContactField(fromContact.phoneNumbers); + } + if (fromContact.organizations) { + // XXX: organizations are saved in 2 arrays - org and jobTitle + // depending on the usecase it might generate issues + // where wrong title will be added to an organization + contact.org = exportContactFieldArray(fromContact.organizations, 'name'); + contact.jobTitle = exportContactFieldArray(fromContact.organizations, 'title'); + } + if (fromContact.note) { + contact.note = [fromContact.note]; + } +} + + +// Extend Cordova Contact prototype to provide update from FFOS contact +Contact.prototype.updateFromMozilla = function(moz) { + function exportContactField(data) { + var contactFields = []; + for (var i=0; i < data.length; i++) { + var item = data[i]; + var itemData = new ContactField(item.type, item.value, item.pref); + contactFields.push(itemData); + } + return contactFields; + } + + function makeContactFieldFromArray(data) { + var contactFields = []; + for (var i=0; i < data.length; i++) { + var itemData = new ContactField(null, data[i]); + contactFields.push(itemData); + } + return contactFields; + } + + function exportAddresses(addresses) { + // TODO: check moz address format + var arr = []; + + for (var i=0; i < addresses.length; i++) { + var addr = {}; + for (var key in addresses[i]) { + if (key == 'countryName') { + addr['country'] = addresses[i][key]; + } else if (key == 'type') { + addr[key] = addresses[i][key].join(' '); + } else { + addr[key] = addresses[i][key]; + } + } + arr.push(addr); + } + return arr; + } + + function createOrganizations(orgs, jobs) { + orgs = (orgs) ? orgs : []; + jobs = (jobs) ? jobs : []; + var max_length = Math.max(orgs.length, jobs.length); + var organizations = []; + for (var i=0; i < max_length; i++) { + organizations.push(new ContactOrganization( + null, null, orgs[i] || null, null, jobs[i] || null)); + } + return organizations; + } + + function createFormatted(name) { + var fields = ['honorificPrefix', 'givenName', 'middleName', + 'familyName', 'honorificSuffix']; + var f = ''; + for (var i = 0; i < fields.length; i++) { + if (name[fields[i]]) { + if (f) { + f += ' '; + } + f += name[fields[i]]; + } + } + return f; + } + + + if (moz.id) { + this.id = moz.id; + } + var nameFields = [['givenName'], ['familyName'], + ['honorificPrefix'], ['honorificSuffix'], + ['additionalName', 'middleName']]; + var baseArrayFields = [['name', 'displayName'], 'nickname', ['note']]; + var baseStringFields = []; + var name = new ContactName(); + var j = 0; while(field = nameFields[j++]) { + if (moz[field[0]]) { + name[field[1] || field[0]] = moz[field[0]].join(' '); + } + } + this.name = name; + j = 0; while(field = baseArrayFields[j++]) { + if (moz[field[0]]) { + this[field[1] || field[0]] = moz[field[0]].join(' '); + } + } + j = 0; while(field = baseStringFields[j++]) { + if (moz[field[0]]) { + this[field[1] || field[0]] = moz[field[0]]; + } + } + // emails + if (moz.email) { + this.emails = exportContactField(moz.email); + } + // categories + if (moz.category) { + this.categories = makeContactFieldFromArray(moz.category); + } + + // addresses + if (moz.adr) { + this.addresses = exportAddresses(moz.adr); + } + + // phoneNumbers + if (moz.tel) { + this.phoneNumbers = exportContactField(moz.tel); + } + // birthday + if (moz.bday) { + this.birthday = Date.parse(moz.bday); + } + // organizations + if (moz.org || moz.jobTitle) { + // XXX: organizations array is created from org and jobTitle + this.organizations = createOrganizations(moz.org, moz.jobTitle); + } + // construct a read-only formatted value + this.name.formatted = createFormatted(this.name); + + /* Find out how to translate these parameters + // photo: Blob + // url: Array with metadata (?) + // impp: exportIM(contact.ims), TODO: find the moz impp definition + // anniversary + // sex + // genderIdentity + // key + */ +} + + +function createMozillaFromCordova(successCB, errorCB, contact) { + var moz; + // get contact if exists + if (_hasId(contact.id)) { + var search = navigator.mozContacts.find({ + filterBy: ['id'], filterValue: contact.id, filterOp: 'equals'}); + search.onsuccess = function() { + moz = search.result[0]; + updateFromCordova(moz, contact); + successCB(moz); + }; + search.onerror = errorCB; + return; + } + + // create empty contact + moz = new mozContact(); + // if ('init' in moz) { + // 1.2 and below compatibility + // moz.init(); + // } + updateFromCordova(moz, contact); + successCB(moz); +} + + +function createCordovaFromMozilla(moz) { + var contact = new Contact(); + contact.updateFromMozilla(moz); + return contact; +} + + +// However API requires the ability to save multiple contacts, it is +// used to save only one element array +function saveContacts(successCB, errorCB, contacts) { + // a closure which is holding the right moz contact + function makeSaveSuccessCB(moz) { + return function(result) { + // create contact from FXOS contact (might be different than + // the original one due to differences in API) + var contact = createCordovaFromMozilla(moz); + // call callback + successCB(contact); + } + } + var i=0; + var contact; + while(contact = contacts[i++]){ + var moz = createMozillaFromCordova(function(moz) { + var request = navigator.mozContacts.save(moz); + // success and/or fail will be called every time a contact is saved + request.onsuccess = makeSaveSuccessCB(moz); + request.onerror = errorCB; + }, function() {}, contact); + } +} + + +// API provides a list of ids to be removed +function remove(successCB, errorCB, ids) { + var i=0; + var id; + for (var i=0; i < ids.length; i++){ + // throw an error if no id provided + if (!_hasId(ids[i])) { + console.error('FFOS: Attempt to remove unsaved contact'); + errorCB(0); + return; + } + // check if provided id actually exists + var search = navigator.mozContacts.find({ + filterBy: ['id'], filterValue: ids[i], filterOp: 'equals'}); + search.onsuccess = function() { + if (search.result.length === 0) { + console.error('FFOS: Attempt to remove a non existing contact'); + errorCB(0); + return; + } + var moz = search.result[0]; + var request = navigator.mozContacts.remove(moz); + request.onsuccess = successCB; + request.onerror = errorCB; + }; + search.onerror = errorCB; + } +} + + +var mozContactSearchFields = [['name', 'displayName'], ['givenName'], + ['familyName'], ['email'], ['tel'], ['jobTitle'], ['note'], + ['tel', 'phoneNumbers'], ['email', 'emails']]; +// Searching by nickname and additionalName is forbidden in 1.3 and below +// Searching by name is forbidden in 1.2 and below + +// finds if a key is allowed and returns FFOS name if different +function getMozSearchField(key) { + if (mozContactSearchFields.indexOf([key]) >= 0) { + return key; + } + for (var i=0; i < mozContactSearchFields.length; i++) { + if (mozContactSearchFields[i].length > 1) { + if (mozContactSearchFields[i][1] === key) { + return mozContactSearchFields[i][0]; + } + } + } + return false; +} + + +function _getAll(successCB, errorCB, params) { + // [contactField, eventualMozContactField] + var getall = navigator.mozContacts.getAll({}); + var contacts = []; + + getall.onsuccess = function() { + if (getall.result) { + contacts.push(createCordovaFromMozilla(getall.result)); + getall.continue(); + } else { + successCB(contacts); + } + }; + getall.onerror = errorCB; +} + + +function search(successCB, errorCB, params) { + var options = params[1] || {}; + if (!options.filter) { + return _getAll(successCB, errorCB, params); + } + var filterBy = []; + // filter and translate fields + for (var i=0; i < params[0].length; i++) { + var searchField = params[0][i]; + var mozField = getMozSearchField(searchField); + if (searchField === 'name') { + // Cordova uses name for search by all name fields. + filterBy.push('givenName'); + filterBy.push('familyName'); + continue; + } + if (searchField === 'displayName' && 'init' in new mozContact()) { + // ``init`` in ``mozContact`` indicates FFOS version 1.2 or below + // Searching by name (in moz) is then forbidden + console.log('FFOS ContactProxy: Unable to search by displayName on FFOS 1.2'); + continue; + } + if (mozField) { + filterBy.push(mozField); + } else { + console.log('FXOS ContactProxy: inallowed field passed to search filtered out: ' + searchField); + } + } + + var mozOptions = {filterBy: filterBy, filterOp: 'startsWith'}; + if (!options.multiple) { + mozOptions.filterLimit = 1; + } + mozOptions.filterValue = options.filter; + var request = navigator.mozContacts.find(mozOptions); + request.onsuccess = function() { + var contacts = []; + var mozContacts = request.result; + var moz = mozContacts[0]; + for (var i=0; i < mozContacts.length; i++) { + contacts.push(createCordovaFromMozilla(mozContacts[i])); + } + successCB(contacts); + }; + request.onerror = errorCB; +} + + +module.exports = { + save: saveContacts, + remove: remove, + search: search +}; + +require("cordova/exec/proxy").add("Contacts", module.exports); \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/src/ios/CDVContact.h b/plugins/cordova-plugin-contacts/src/ios/CDVContact.h new file mode 100644 index 0000000..47aa764 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/ios/CDVContact.h @@ -0,0 +1,137 @@ +/* + 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 +#import +#import + +enum CDVContactError { + UNKNOWN_ERROR = 0, + INVALID_ARGUMENT_ERROR = 1, + TIMEOUT_ERROR = 2, + PENDING_OPERATION_ERROR = 3, + IO_ERROR = 4, + NOT_SUPPORTED_ERROR = 5, + OPERATION_CANCELLED_ERROR = 6, + PERMISSION_DENIED_ERROR = 20 +}; +typedef NSUInteger CDVContactError; + +@interface CDVContact : NSObject { + ABRecordRef record; // the ABRecord associated with this contact + NSDictionary* returnFields; // dictionary of fields to return when performing search +} + +@property (nonatomic, assign) ABRecordRef record; +@property (nonatomic, strong) NSDictionary* returnFields; + ++ (NSDictionary*)defaultABtoW3C; ++ (NSDictionary*)defaultW3CtoAB; ++ (NSSet*)defaultW3CtoNull; ++ (NSDictionary*)defaultObjectAndProperties; ++ (NSDictionary*)defaultFields; + ++ (NSDictionary*)calcReturnFields:(NSArray*)fields; +- (id)init; +- (id)initFromABRecord:(ABRecordRef)aRecord; +- (bool)setFromContactDict:(NSDictionary*)aContact asUpdate:(BOOL)bUpdate; + ++ (BOOL)needsConversion:(NSString*)W3Label; ++ (CFStringRef)convertContactTypeToPropertyLabel:(NSString*)label; ++ (NSString*)convertPropertyLabelToContactType:(NSString*)label; ++ (BOOL)isValidW3ContactType:(NSString*)label; +- (bool)setValue:(id)aValue forProperty:(ABPropertyID)aProperty inRecord:(ABRecordRef)aRecord asUpdate:(BOOL)bUpdate; + +- (NSDictionary*)toDictionary:(NSDictionary*)withFields; +- (NSNumber*)getDateAsNumber:(ABPropertyID)datePropId; +- (NSObject*)extractName; +- (NSObject*)extractMultiValue:(NSString*)propertyId; +- (NSObject*)extractAddresses; +- (NSObject*)extractIms; +- (NSObject*)extractOrganizations; +- (NSObject*)extractPhotos; + +- (NSMutableDictionary*)translateW3Dict:(NSDictionary*)dict forProperty:(ABPropertyID)prop; +- (bool)setMultiValueStrings:(NSArray*)fieldArray forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person asUpdate:(BOOL)bUpdate; +- (bool)setMultiValueDictionary:(NSArray*)array forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person asUpdate:(BOOL)bUpdate; +- (ABMultiValueRef)allocStringMultiValueFromArray:array; +- (ABMultiValueRef)allocDictMultiValueFromArray:array forProperty:(ABPropertyID)prop; +- (BOOL)foundValue:(NSString*)testValue inFields:(NSDictionary*)searchFields; +- (BOOL)testStringValue:(NSString*)testValue forW3CProperty:(NSString*)property; +- (BOOL)testDateValue:(NSString*)testValue forW3CProperty:(NSString*)property; +- (BOOL)searchContactFields:(NSArray*)fields forMVStringProperty:(ABPropertyID)propId withValue:testValue; +- (BOOL)testMultiValueStrings:(NSString*)testValue forProperty:(ABPropertyID)propId ofType:(NSString*)type; +- (NSArray*)valuesForProperty:(ABPropertyID)propId inRecord:(ABRecordRef)aRecord; +- (NSArray*)labelsForProperty:(ABPropertyID)propId inRecord:(ABRecordRef)aRecord; +- (BOOL)searchContactFields:(NSArray*)fields forMVDictionaryProperty:(ABPropertyID)propId withValue:(NSString*)testValue; + +@end + +// generic ContactField types +#define kW3ContactFieldType @"type" +#define kW3ContactFieldValue @"value" +#define kW3ContactFieldPrimary @"pref" +// Various labels for ContactField types +#define kW3ContactWorkLabel @"work" +#define kW3ContactHomeLabel @"home" +#define kW3ContactOtherLabel @"other" +#define kW3ContactPhoneFaxLabel @"fax" +#define kW3ContactPhoneMobileLabel @"mobile" +#define kW3ContactPhonePagerLabel @"pager" +#define kW3ContactUrlBlog @"blog" +#define kW3ContactUrlProfile @"profile" +#define kW3ContactImAIMLabel @"aim" +#define kW3ContactImICQLabel @"icq" +#define kW3ContactImMSNLabel @"msn" +#define kW3ContactImYahooLabel @"yahoo" +#define kW3ContactFieldId @"id" +// special translation for IM field value and type +#define kW3ContactImType @"type" +#define kW3ContactImValue @"value" + +// Contact object +#define kW3ContactId @"id" +#define kW3ContactName @"name" +#define kW3ContactFormattedName @"formatted" +#define kW3ContactGivenName @"givenName" +#define kW3ContactFamilyName @"familyName" +#define kW3ContactMiddleName @"middleName" +#define kW3ContactHonorificPrefix @"honorificPrefix" +#define kW3ContactHonorificSuffix @"honorificSuffix" +#define kW3ContactDisplayName @"displayName" +#define kW3ContactNickname @"nickname" +#define kW3ContactPhoneNumbers @"phoneNumbers" +#define kW3ContactAddresses @"addresses" +#define kW3ContactAddressFormatted @"formatted" +#define kW3ContactStreetAddress @"streetAddress" +#define kW3ContactLocality @"locality" +#define kW3ContactRegion @"region" +#define kW3ContactPostalCode @"postalCode" +#define kW3ContactCountry @"country" +#define kW3ContactEmails @"emails" +#define kW3ContactIms @"ims" +#define kW3ContactOrganizations @"organizations" +#define kW3ContactOrganizationName @"name" +#define kW3ContactTitle @"title" +#define kW3ContactDepartment @"department" +#define kW3ContactBirthday @"birthday" +#define kW3ContactNote @"note" +#define kW3ContactPhotos @"photos" +#define kW3ContactCategories @"categories" +#define kW3ContactUrls @"urls" diff --git a/plugins/cordova-plugin-contacts/src/ios/CDVContact.m b/plugins/cordova-plugin-contacts/src/ios/CDVContact.m new file mode 100644 index 0000000..a1dd85d --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/ios/CDVContact.m @@ -0,0 +1,1781 @@ +/* + 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 "CDVContact.h" + +#define DATE_OR_NULL(dateObj) ((aDate != nil) ? (id)([aDate descriptionWithLocale:[NSLocale currentLocale]]) : (id)([NSNull null])) +#define IS_VALID_VALUE(value) ((value != nil) && (![value isKindOfClass:[NSNull class]])) + +static NSDictionary* org_apache_cordova_contacts_W3CtoAB = nil; +static NSDictionary* org_apache_cordova_contacts_ABtoW3C = nil; +static NSSet* org_apache_cordova_contacts_W3CtoNull = nil; +static NSDictionary* org_apache_cordova_contacts_objectAndProperties = nil; +static NSDictionary* org_apache_cordova_contacts_defaultFields = nil; + +@implementation CDVContact : NSObject + + @synthesize returnFields; + +- (id)init +{ + if ((self = [super init]) != nil) { + ABRecordRef rec = ABPersonCreate(); + self.record = rec; + if (rec) { + CFRelease(rec); + } + } + return self; +} + +- (id)initFromABRecord:(ABRecordRef)aRecord +{ + if ((self = [super init]) != nil) { + self.record = aRecord; + } + return self; +} + +/* synthesize 'record' ourselves to have retain properties for CF types */ + +- (void)setRecord:(ABRecordRef)aRecord +{ + if (record != NULL) { + CFRelease(record); + } + if (aRecord != NULL) { + record = CFRetain(aRecord); + } +} + +- (ABRecordRef)record +{ + return record; +} + +/* Rather than creating getters and setters for each AddressBook (AB) Property, generic methods are used to deal with + * simple properties, MultiValue properties( phone numbers and emails) and MultiValueDictionary properties (Ims and addresses). + * The dictionaries below are used to translate between the W3C identifiers and the AB properties. Using the dictionaries, + * allows looping through sets of properties to extract from or set into the W3C dictionary to/from the ABRecord. + */ + +/* The two following dictionaries translate between W3C properties and AB properties. It currently mixes both + * Properties (kABPersonAddressProperty for example) and Strings (kABPersonAddressStreetKey) so users should be aware of + * what types of values are expected. + * a bit. +*/ ++ (NSDictionary*)defaultABtoW3C +{ + if (org_apache_cordova_contacts_ABtoW3C == nil) { + org_apache_cordova_contacts_ABtoW3C = [NSDictionary dictionaryWithObjectsAndKeys: + kW3ContactNickname, [NSNumber numberWithInt:kABPersonNicknameProperty], + kW3ContactGivenName, [NSNumber numberWithInt:kABPersonFirstNameProperty], + kW3ContactFamilyName, [NSNumber numberWithInt:kABPersonLastNameProperty], + kW3ContactMiddleName, [NSNumber numberWithInt:kABPersonMiddleNameProperty], + kW3ContactHonorificPrefix, [NSNumber numberWithInt:kABPersonPrefixProperty], + kW3ContactHonorificSuffix, [NSNumber numberWithInt:kABPersonSuffixProperty], + kW3ContactPhoneNumbers, [NSNumber numberWithInt:kABPersonPhoneProperty], + kW3ContactAddresses, [NSNumber numberWithInt:kABPersonAddressProperty], + kW3ContactStreetAddress, kABPersonAddressStreetKey, + kW3ContactLocality, kABPersonAddressCityKey, + kW3ContactRegion, kABPersonAddressStateKey, + kW3ContactPostalCode, kABPersonAddressZIPKey, + kW3ContactCountry, kABPersonAddressCountryKey, + kW3ContactEmails, [NSNumber numberWithInt:kABPersonEmailProperty], + kW3ContactIms, [NSNumber numberWithInt:kABPersonInstantMessageProperty], + kW3ContactOrganizations, [NSNumber numberWithInt:kABPersonOrganizationProperty], + kW3ContactOrganizationName, [NSNumber numberWithInt:kABPersonOrganizationProperty], + kW3ContactTitle, [NSNumber numberWithInt:kABPersonJobTitleProperty], + kW3ContactDepartment, [NSNumber numberWithInt:kABPersonDepartmentProperty], + kW3ContactBirthday, [NSNumber numberWithInt:kABPersonBirthdayProperty], + kW3ContactUrls, [NSNumber numberWithInt:kABPersonURLProperty], + kW3ContactNote, [NSNumber numberWithInt:kABPersonNoteProperty], + nil]; + } + + return org_apache_cordova_contacts_ABtoW3C; +} + ++ (NSDictionary*)defaultW3CtoAB +{ + if (org_apache_cordova_contacts_W3CtoAB == nil) { + org_apache_cordova_contacts_W3CtoAB = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:kABPersonNicknameProperty], kW3ContactNickname, + [NSNumber numberWithInt:kABPersonFirstNameProperty], kW3ContactGivenName, + [NSNumber numberWithInt:kABPersonLastNameProperty], kW3ContactFamilyName, + [NSNumber numberWithInt:kABPersonMiddleNameProperty], kW3ContactMiddleName, + [NSNumber numberWithInt:kABPersonPrefixProperty], kW3ContactHonorificPrefix, + [NSNumber numberWithInt:kABPersonSuffixProperty], kW3ContactHonorificSuffix, + [NSNumber numberWithInt:kABPersonPhoneProperty], kW3ContactPhoneNumbers, + [NSNumber numberWithInt:kABPersonAddressProperty], kW3ContactAddresses, + kABPersonAddressStreetKey, kW3ContactStreetAddress, + kABPersonAddressCityKey, kW3ContactLocality, + kABPersonAddressStateKey, kW3ContactRegion, + kABPersonAddressZIPKey, kW3ContactPostalCode, + kABPersonAddressCountryKey, kW3ContactCountry, + [NSNumber numberWithInt:kABPersonEmailProperty], kW3ContactEmails, + [NSNumber numberWithInt:kABPersonInstantMessageProperty], kW3ContactIms, + [NSNumber numberWithInt:kABPersonOrganizationProperty], kW3ContactOrganizations, + [NSNumber numberWithInt:kABPersonJobTitleProperty], kW3ContactTitle, + [NSNumber numberWithInt:kABPersonDepartmentProperty], kW3ContactDepartment, + [NSNumber numberWithInt:kABPersonBirthdayProperty], kW3ContactBirthday, + [NSNumber numberWithInt:kABPersonNoteProperty], kW3ContactNote, + [NSNumber numberWithInt:kABPersonURLProperty], kW3ContactUrls, + kABPersonInstantMessageUsernameKey, kW3ContactImValue, + kABPersonInstantMessageServiceKey, kW3ContactImType, + [NSNull null], kW3ContactFieldType, /* include entries in dictionary to indicate ContactField properties */ + [NSNull null], kW3ContactFieldValue, + [NSNull null], kW3ContactFieldPrimary, + [NSNull null], kW3ContactFieldId, + [NSNumber numberWithInt:kABPersonOrganizationProperty], kW3ContactOrganizationName, /* careful, name is used multiple times*/ + nil]; + } + return org_apache_cordova_contacts_W3CtoAB; +} + ++ (NSSet*)defaultW3CtoNull +{ + // these are values that have no AddressBook Equivalent OR have not been implemented yet + if (org_apache_cordova_contacts_W3CtoNull == nil) { + org_apache_cordova_contacts_W3CtoNull = [NSSet setWithObjects:kW3ContactDisplayName, + kW3ContactCategories, kW3ContactFormattedName, nil]; + } + return org_apache_cordova_contacts_W3CtoNull; +} + +/* + * The objectAndProperties dictionary contains the all of the properties of the W3C Contact Objects specified by the key + * Used in calcReturnFields, and various extract methods + */ ++ (NSDictionary*)defaultObjectAndProperties +{ + if (org_apache_cordova_contacts_objectAndProperties == nil) { + org_apache_cordova_contacts_objectAndProperties = [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObjects:kW3ContactGivenName, kW3ContactFamilyName, + kW3ContactMiddleName, kW3ContactHonorificPrefix, kW3ContactHonorificSuffix, kW3ContactFormattedName, nil], kW3ContactName, + [NSArray arrayWithObjects:kW3ContactStreetAddress, kW3ContactLocality, kW3ContactRegion, + kW3ContactPostalCode, kW3ContactCountry, /*kW3ContactAddressFormatted,*/ nil], kW3ContactAddresses, + [NSArray arrayWithObjects:kW3ContactOrganizationName, kW3ContactTitle, kW3ContactDepartment, nil], kW3ContactOrganizations, + [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactPhoneNumbers, + [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactEmails, + [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactPhotos, + [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactUrls, + [NSArray arrayWithObjects:kW3ContactImValue, kW3ContactImType, nil], kW3ContactIms, + nil]; + } + return org_apache_cordova_contacts_objectAndProperties; +} + ++ (NSDictionary*)defaultFields +{ + if (org_apache_cordova_contacts_defaultFields == nil) { + org_apache_cordova_contacts_defaultFields = [NSDictionary dictionaryWithObjectsAndKeys: + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactName], kW3ContactName, + [NSNull null], kW3ContactNickname, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactAddresses], kW3ContactAddresses, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactOrganizations], kW3ContactOrganizations, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactPhoneNumbers], kW3ContactPhoneNumbers, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactEmails], kW3ContactEmails, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactIms], kW3ContactIms, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactPhotos], kW3ContactPhotos, + [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactUrls], kW3ContactUrls, + [NSNull null], kW3ContactBirthday, + [NSNull null], kW3ContactNote, + nil]; + } + return org_apache_cordova_contacts_defaultFields; +} + +/* Translate W3C Contact data into ABRecordRef + * + * New contact information comes in as a NSMutableDictionary. All Null entries in Contact object are set + * as [NSNull null] in the dictionary when translating from the JSON input string of Contact data. However, if + * user did not set a value within a Contact object or sub-object (by not using the object constructor) some data + * may not exist. + * bUpdate = YES indicates this is a save of an existing record + */ +- (bool)setFromContactDict:(NSDictionary*)aContact asUpdate:(BOOL)bUpdate +{ + if (![aContact isKindOfClass:[NSDictionary class]]) { + return FALSE; // can't do anything if no dictionary! + } + + ABRecordRef person = self.record; + bool bSuccess = TRUE; + CFErrorRef error; + + // set name info + // iOS doesn't have displayName - might have to pull parts from it to create name + bool bName = false; + NSDictionary* dict = [aContact valueForKey:kW3ContactName]; + if ([dict isKindOfClass:[NSDictionary class]]) { + bName = true; + NSArray* propArray = [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactName]; + + for (id i in propArray) { + if (![(NSString*)i isEqualToString : kW3ContactFormattedName]) { // kW3ContactFormattedName is generated from ABRecordCopyCompositeName() and can't be set + [self setValue:[dict valueForKey:i] forProperty:(ABPropertyID)[(NSNumber*)[[CDVContact defaultW3CtoAB] objectForKey:i] intValue] + inRecord:person asUpdate:bUpdate]; + } + } + } + + id nn = [aContact valueForKey:kW3ContactNickname]; + if (![nn isKindOfClass:[NSNull class]]) { + bName = true; + [self setValue:nn forProperty:kABPersonNicknameProperty inRecord:person asUpdate:bUpdate]; + } + if (!bName) { + // if no name or nickname - try and use displayName as W3Contact must have displayName or ContactName + [self setValue:[aContact valueForKey:kW3ContactDisplayName] forProperty:kABPersonNicknameProperty + inRecord:person asUpdate:bUpdate]; + } + + // set phoneNumbers + // NSLog(@"setting phoneNumbers"); + NSArray* array = [aContact valueForKey:kW3ContactPhoneNumbers]; + if ([array isKindOfClass:[NSArray class]]) { + [self setMultiValueStrings:array forProperty:kABPersonPhoneProperty inRecord:person asUpdate:bUpdate]; + } + // set Emails + // NSLog(@"setting emails"); + array = [aContact valueForKey:kW3ContactEmails]; + if ([array isKindOfClass:[NSArray class]]) { + [self setMultiValueStrings:array forProperty:kABPersonEmailProperty inRecord:person asUpdate:bUpdate]; + } + // set Urls + // NSLog(@"setting urls"); + array = [aContact valueForKey:kW3ContactUrls]; + if ([array isKindOfClass:[NSArray class]]) { + [self setMultiValueStrings:array forProperty:kABPersonURLProperty inRecord:person asUpdate:bUpdate]; + } + + // set multivalue dictionary properties + // set addresses: streetAddress, locality, region, postalCode, country + // set ims: value = username, type = servicetype + // iOS addresses and im are a MultiValue Properties with label, value=dictionary of info, and id + // NSLog(@"setting addresses"); + error = nil; + array = [aContact valueForKey:kW3ContactAddresses]; + if ([array isKindOfClass:[NSArray class]]) { + [self setMultiValueDictionary:array forProperty:kABPersonAddressProperty inRecord:person asUpdate:bUpdate]; + } + // ims + // NSLog(@"setting ims"); + array = [aContact valueForKey:kW3ContactIms]; + if ([array isKindOfClass:[NSArray class]]) { + [self setMultiValueDictionary:array forProperty:kABPersonInstantMessageProperty inRecord:person asUpdate:bUpdate]; + } + + // organizations + // W3C ContactOrganization has pref, type, name, title, department + // iOS only supports name, title, department + // NSLog(@"setting organizations"); + // TODO this may need work - should Organization information be removed when array is empty?? + array = [aContact valueForKey:kW3ContactOrganizations]; // iOS only supports one organization - use first one + if ([array isKindOfClass:[NSArray class]]) { + BOOL bRemove = NO; + NSDictionary* dict = nil; + if ([array count] > 0) { + dict = [array objectAtIndex:0]; + } else { + // remove the organization info entirely + bRemove = YES; + } + if ([dict isKindOfClass:[NSDictionary class]] || (bRemove == YES)) { + [self setValue:(bRemove ? @"" : [dict valueForKey:@"name"]) forProperty:kABPersonOrganizationProperty inRecord:person asUpdate:bUpdate]; + [self setValue:(bRemove ? @"" : [dict valueForKey:kW3ContactTitle]) forProperty:kABPersonJobTitleProperty inRecord:person asUpdate:bUpdate]; + [self setValue:(bRemove ? @"" : [dict valueForKey:kW3ContactDepartment]) forProperty:kABPersonDepartmentProperty inRecord:person asUpdate:bUpdate]; + } + } + // add dates + // Dates come in as milliseconds in NSNumber Object + id ms = [aContact valueForKey:kW3ContactBirthday]; + NSDate* aDate = nil; + if (ms && [ms isKindOfClass:[NSNumber class]]) { + double msValue = [ms doubleValue]; + msValue = msValue / 1000; + aDate = [NSDate dateWithTimeIntervalSince1970:msValue]; + } + if ((aDate != nil) || [ms isKindOfClass:[NSString class]]) { + [self setValue:aDate != nil ? aDate:ms forProperty:kABPersonBirthdayProperty inRecord:person asUpdate:bUpdate]; + } + // don't update creation date + // modification date will get updated when save + // anniversary is removed from W3C Contact api Dec 9, 2010 spec - don't waste time on it yet + + // kABPersonDateProperty + + // kABPersonAnniversaryLabel + + // iOS doesn't have gender - ignore + // note + [self setValue:[aContact valueForKey:kW3ContactNote] forProperty:kABPersonNoteProperty inRecord:person asUpdate:bUpdate]; + + // iOS doesn't have preferredName- ignore + + // photo + array = [aContact valueForKey:kW3ContactPhotos]; + if ([array isKindOfClass:[NSArray class]]) { + if (bUpdate && ([array count] == 0)) { + // remove photo + bSuccess = ABPersonRemoveImageData(person, &error); + } else if ([array count] > 0) { + NSDictionary* dict = [array objectAtIndex:0]; // currently only support one photo + if ([dict isKindOfClass:[NSDictionary class]]) { + id value = [dict objectForKey:kW3ContactFieldValue]; + if ([value isKindOfClass:[NSString class]]) { + if (bUpdate && ([value length] == 0)) { + // remove the current image + bSuccess = ABPersonRemoveImageData(person, &error); + } else { + // use this image + // don't know if string is encoded or not so first unencode it then encode it again + NSString* cleanPath = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSURL* photoUrl = [NSURL URLWithString:[cleanPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + // caller is responsible for checking for a connection, if no connection this will fail + NSError* err = nil; + NSData* data = nil; + if (photoUrl) { + data = [NSData dataWithContentsOfURL:photoUrl options:NSDataReadingUncached error:&err]; + } + if (data && ([data length] > 0)) { + bSuccess = ABPersonSetImageData(person, (__bridge CFDataRef)data, &error); + } + if (!data || !bSuccess) { + NSLog(@"error setting contact image: %@", (err != nil ? [err localizedDescription] : @"")); + } + } + } + } + } + } + + // TODO WebURLs + + // TODO timezone + + return bSuccess; +} + +/* Set item into an AddressBook Record for the specified property. + * aValue - the value to set into the address book (code checks for null or [NSNull null] + * aProperty - AddressBook property ID + * aRecord - the record to update + * bUpdate - whether this is a possible update vs a new entry + * RETURN + * true - property was set (or input value as null) + * false - property was not set + */ +- (bool)setValue:(id)aValue forProperty:(ABPropertyID)aProperty inRecord:(ABRecordRef)aRecord asUpdate:(BOOL)bUpdate +{ + bool bSuccess = true; // if property was null, just ignore and return success + CFErrorRef error; + + if (aValue && ![aValue isKindOfClass:[NSNull class]]) { + if (bUpdate && ([aValue isKindOfClass:[NSString class]] && ([aValue length] == 0))) { // if updating, empty string means to delete + aValue = NULL; + } // really only need to set if different - more efficient to just update value or compare and only set if necessary??? + bSuccess = ABRecordSetValue(aRecord, aProperty, (__bridge CFTypeRef)aValue, &error); + if (!bSuccess) { + NSLog(@"error setting %d property", aProperty); + } + } + + return bSuccess; +} + +- (bool)removeProperty:(ABPropertyID)aProperty inRecord:(ABRecordRef)aRecord +{ + CFErrorRef err; + bool bSuccess = ABRecordRemoveValue(aRecord, aProperty, &err); + + if (!bSuccess) { + CFStringRef errDescription = CFErrorCopyDescription(err); + NSLog(@"Unable to remove property %d: %@", aProperty, errDescription); + CFRelease(errDescription); + } + return bSuccess; +} + +- (bool)addToMultiValue:(ABMultiValueRef)multi fromDictionary:dict +{ + bool bSuccess = FALSE; + id value = [dict valueForKey:kW3ContactFieldValue]; + + if (IS_VALID_VALUE(value)) { + CFStringRef label = [CDVContact convertContactTypeToPropertyLabel:[dict valueForKey:kW3ContactFieldType]]; + bSuccess = ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)value, label, NULL); + if (!bSuccess) { + NSLog(@"Error setting Value: %@ and label: %@", value, label); + } + } + return bSuccess; +} + +- (ABMultiValueRef)allocStringMultiValueFromArray:array +{ + ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiStringPropertyType); + + for (NSDictionary* dict in array) { + [self addToMultiValue:multi fromDictionary:dict]; + } + + return multi; // caller is responsible for releasing multi +} + +- (bool)setValue:(CFTypeRef)value forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person +{ + CFErrorRef error; + bool bSuccess = ABRecordSetValue(person, prop, value, &error); + + if (!bSuccess) { + NSLog(@"Error setting value for property: %d", prop); + } + return bSuccess; +} + +/* Set MultiValue string properties into Address Book Record. + * NSArray* fieldArray - array of dictionaries containing W3C properties to be set into record + * ABPropertyID prop - the property to be set (generally used for phones and emails) + * ABRecordRef person - the record to set values into + * BOOL bUpdate - whether or not to update date or set as new. + * When updating: + * empty array indicates to remove entire property + * empty string indicates to remove + * [NSNull null] do not modify (keep existing record value) + * RETURNS + * bool false indicates error + * + * used for phones and emails + */ +- (bool)setMultiValueStrings:(NSArray*)fieldArray forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person asUpdate:(BOOL)bUpdate +{ + bool bSuccess = TRUE; + ABMutableMultiValueRef multi = nil; + + if (!bUpdate) { + multi = [self allocStringMultiValueFromArray:fieldArray]; + bSuccess = [self setValue:multi forProperty:prop inRecord:person]; + } else if (bUpdate && ([fieldArray count] == 0)) { + // remove entire property + bSuccess = [self removeProperty:prop inRecord:person]; + } else { // check for and apply changes + ABMultiValueRef copy = ABRecordCopyValue(person, prop); + if (copy != nil) { + multi = ABMultiValueCreateMutableCopy(copy); + CFRelease(copy); + + for (NSDictionary* dict in fieldArray) { + id val; + NSString* label = nil; + val = [dict valueForKey:kW3ContactFieldValue]; + label = (__bridge NSString*)[CDVContact convertContactTypeToPropertyLabel:[dict valueForKey:kW3ContactFieldType]]; + if (IS_VALID_VALUE(val)) { + // is an update, find index of entry with matching id, if values are different, update. + id idValue = [dict valueForKey:kW3ContactFieldId]; + int identifier = [idValue isKindOfClass:[NSNumber class]] ? [idValue intValue] : -1; + CFIndex i = identifier >= 0 ? ABMultiValueGetIndexForIdentifier(multi, identifier) : kCFNotFound; + if (i != kCFNotFound) { + if ([val length] == 0) { + // remove both value and label + ABMultiValueRemoveValueAndLabelAtIndex(multi, i); + } else { + NSString* valueAB = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(multi, i); + NSString* labelAB = (__bridge_transfer NSString*)ABMultiValueCopyLabelAtIndex(multi, i); + if ((valueAB == nil) || ![val isEqualToString:valueAB]) { + ABMultiValueReplaceValueAtIndex(multi, (__bridge CFTypeRef)val, i); + } + if ((labelAB == nil) || ![label isEqualToString:labelAB]) { + ABMultiValueReplaceLabelAtIndex(multi, (__bridge CFStringRef)label, i); + } + } + } else { + // is a new value - insert + [self addToMultiValue:multi fromDictionary:dict]; + } + } // end of if value + } // end of for + } else { // adding all new value(s) + multi = [self allocStringMultiValueFromArray:fieldArray]; + } + // set the (updated) copy as the new value + bSuccess = [self setValue:multi forProperty:prop inRecord:person]; + } + + if (multi) { + CFRelease(multi); + } + + return bSuccess; +} + +// used for ims and addresses +- (ABMultiValueRef)allocDictMultiValueFromArray:array forProperty:(ABPropertyID)prop +{ + ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType); + NSMutableDictionary* newDict; + NSMutableDictionary* addDict; + + for (NSDictionary* dict in array) { + newDict = [self translateW3Dict:dict forProperty:prop]; + addDict = [NSMutableDictionary dictionaryWithCapacity:2]; + if (newDict) { // create a new dictionary with a Label and Value, value is the dictionary previously created + // June, 2011 W3C Contact spec adds type into ContactAddress book + // get the type out of the original dictionary for address + NSString* addrType = (NSString*)[dict valueForKey:kW3ContactFieldType]; + if (!addrType) { + addrType = (NSString*)kABOtherLabel; + } + NSObject* typeValue = ((prop == kABPersonInstantMessageProperty) ? (NSString*)kABOtherLabel : addrType); + // NSLog(@"typeValue: %@", typeValue); + [addDict setObject:typeValue forKey:kW3ContactFieldType]; // im labels will be set as Other and address labels as type from dictionary + [addDict setObject:newDict forKey:kW3ContactFieldValue]; + [self addToMultiValue:multi fromDictionary:addDict]; + } + } + + return multi; // caller is responsible for releasing +} + +// used for ims and addresses to convert W3 dictionary of values to AB Dictionary +// got messier when June, 2011 W3C Contact spec added type field into ContactAddress +- (NSMutableDictionary*)translateW3Dict:(NSDictionary*)dict forProperty:(ABPropertyID)prop +{ + NSArray* propArray = [[CDVContact defaultObjectAndProperties] valueForKey:[[CDVContact defaultABtoW3C] objectForKey:[NSNumber numberWithInt:prop]]]; + + NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:1]; + id value; + + for (NSString* key in propArray) { // for each W3 Contact key get the value + if (((value = [dict valueForKey:key]) != nil) && ![value isKindOfClass:[NSNull class]]) { + // if necessary convert the W3 value to AB Property label + NSString* setValue = value; + if ([CDVContact needsConversion:key]) { // IM types must be converted + setValue = (NSString*)[CDVContact convertContactTypeToPropertyLabel:value]; + // IMs must have a valid AB value! + if ((prop == kABPersonInstantMessageProperty) && [setValue isEqualToString:(NSString*)kABOtherLabel]) { + setValue = @""; // try empty string + } + } + // set the AB value into the dictionary + [newDict setObject:setValue forKey:(NSString*)[[CDVContact defaultW3CtoAB] valueForKey:(NSString*)key]]; + } + } + + if ([newDict count] == 0) { + newDict = nil; // no items added + } + return newDict; +} + +/* set multivalue dictionary properties into an AddressBook Record + * NSArray* array - array of dictionaries containing the W3C properties to set into the record + * ABPropertyID prop - the property id for the multivalue dictionary (addresses and ims) + * ABRecordRef person - the record to set the values into + * BOOL bUpdate - YES if this is an update to an existing record + * When updating: + * empty array indicates to remove entire property + * value/label == "" indicates to remove + * value/label == [NSNull null] do not modify (keep existing record value) + * RETURN + * bool false indicates fatal error + * + * iOS addresses and im are a MultiValue Properties with label, value=dictionary of info, and id + * set addresses: streetAddress, locality, region, postalCode, country + * set ims: value = username, type = servicetype + * there are some special cases in here for ims - needs cleanup / simplification + * + */ +- (bool)setMultiValueDictionary:(NSArray*)array forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person asUpdate:(BOOL)bUpdate +{ + bool bSuccess = FALSE; + ABMutableMultiValueRef multi = nil; + + if (!bUpdate) { + multi = [self allocDictMultiValueFromArray:array forProperty:prop]; + bSuccess = [self setValue:multi forProperty:prop inRecord:person]; + } else if (bUpdate && ([array count] == 0)) { + // remove property + bSuccess = [self removeProperty:prop inRecord:person]; + } else { // check for and apply changes + ABMultiValueRef copy = ABRecordCopyValue(person, prop); + if (copy) { + multi = ABMultiValueCreateMutableCopy(copy); + CFRelease(copy); + // get the W3C values for this property + NSArray* propArray = [[CDVContact defaultObjectAndProperties] valueForKey:[[CDVContact defaultABtoW3C] objectForKey:[NSNumber numberWithInt:prop]]]; + id value; + id valueAB; + + for (NSDictionary* field in array) { + NSMutableDictionary* dict; + // find the index for the current property + id idValue = [field valueForKey:kW3ContactFieldId]; + int identifier = [idValue isKindOfClass:[NSNumber class]] ? [idValue intValue] : -1; + CFIndex idx = identifier >= 0 ? ABMultiValueGetIndexForIdentifier(multi, identifier) : kCFNotFound; + BOOL bUpdateLabel = NO; + if (idx != kCFNotFound) { + dict = [NSMutableDictionary dictionaryWithCapacity:1]; + // NSDictionary* existingDictionary = (NSDictionary*)ABMultiValueCopyValueAtIndex(multi, idx); + CFTypeRef existingDictionary = ABMultiValueCopyValueAtIndex(multi, idx); + NSString* existingABLabel = (__bridge_transfer NSString*)ABMultiValueCopyLabelAtIndex(multi, idx); + NSString* testLabel = [field valueForKey:kW3ContactFieldType]; + // fixes cb-143 where setting empty label could cause address to not be removed + // (because empty label would become 'other' in convertContactTypeToPropertyLabel + // which may not have matched existing label thus resulting in an incorrect updating of the label + // and the address not getting removed at the end of the for loop) + if (testLabel && [testLabel isKindOfClass:[NSString class]] && ([testLabel length] > 0)) { + CFStringRef w3cLabel = [CDVContact convertContactTypeToPropertyLabel:testLabel]; + if (w3cLabel && ![existingABLabel isEqualToString:(__bridge NSString*)w3cLabel]) { + // replace the label + ABMultiValueReplaceLabelAtIndex(multi, w3cLabel, idx); + bUpdateLabel = YES; + } + } // else was invalid or empty label string so do not update + + for (id k in propArray) { + value = [field valueForKey:k]; + bool bSet = (value != nil && ![value isKindOfClass:[NSNull class]] && ([value isKindOfClass:[NSString class]] && [value length] > 0)); + // if there is a contact value, put it into dictionary + if (bSet) { + NSString* setValue = [CDVContact needsConversion:(NSString*)k] ? (NSString*)[CDVContact convertContactTypeToPropertyLabel:value] : value; + [dict setObject:setValue forKey:(NSString*)[[CDVContact defaultW3CtoAB] valueForKey:(NSString*)k]]; + } else if ((value == nil) || ([value isKindOfClass:[NSString class]] && ([value length] != 0))) { + // value not provided in contact dictionary - if prop exists in AB dictionary, preserve it + valueAB = [(__bridge NSDictionary*)existingDictionary valueForKey : [[CDVContact defaultW3CtoAB] valueForKey:k]]; + if (valueAB != nil) { + [dict setValue:valueAB forKey:[[CDVContact defaultW3CtoAB] valueForKey:k]]; + } + } // else if value == "" it will not be added into updated dict and thus removed + } // end of for loop (moving here fixes cb-143, need to end for loop before replacing or removing multivalue) + + if ([dict count] > 0) { + // something was added into new dict, + ABMultiValueReplaceValueAtIndex(multi, (__bridge CFTypeRef)dict, idx); + } else if (!bUpdateLabel) { + // nothing added into new dict and no label change so remove this property entry + ABMultiValueRemoveValueAndLabelAtIndex(multi, idx); + } + + CFRelease(existingDictionary); + } else { + // not found in multivalue so add it + dict = [self translateW3Dict:field forProperty:prop]; + if (dict) { + NSMutableDictionary* addDict = [NSMutableDictionary dictionaryWithCapacity:2]; + // get the type out of the original dictionary for address + NSObject* typeValue = ((prop == kABPersonInstantMessageProperty) ? (NSString*)kABOtherLabel : (NSString*)[field valueForKey:kW3ContactFieldType]); + // NSLog(@"typeValue: %@", typeValue); + [addDict setObject:typeValue forKey:kW3ContactFieldType]; // im labels will be set as Other and address labels as type from dictionary + [addDict setObject:dict forKey:kW3ContactFieldValue]; + [self addToMultiValue:multi fromDictionary:addDict]; + } + } + } // end of looping through dictionaries + + // set the (updated) copy as the new value + bSuccess = [self setValue:multi forProperty:prop inRecord:person]; + } + } // end of copy and apply changes + if (multi) { + CFRelease(multi); + } + + return bSuccess; +} + +/* Determine which W3C labels need to be converted + */ ++ (BOOL)needsConversion:(NSString*)W3Label +{ + BOOL bConvert = NO; + + if ([W3Label isEqualToString:kW3ContactFieldType] || [W3Label isEqualToString:kW3ContactImType]) { + bConvert = YES; + } + return bConvert; +} + +/* Translation of property type labels contact API ---> iPhone + * + * phone: work, home, other, mobile, fax, pager --> + * kABWorkLabel, kABHomeLabel, kABOtherLabel, kABPersonPhoneMobileLabel, kABPersonHomeFAXLabel || kABPersonHomeFAXLabel, kABPersonPhonePagerLabel + * emails: work, home, other ---> kABWorkLabel, kABHomeLabel, kABOtherLabel + * ims: aim, gtalk, icq, xmpp, msn, skype, qq, yahoo --> kABPersonInstantMessageService + (AIM, ICG, MSN, Yahoo). No support for gtalk, xmpp, skype, qq + * addresses: work, home, other --> kABWorkLabel, kABHomeLabel, kABOtherLabel + * + * + */ ++ (CFStringRef)convertContactTypeToPropertyLabel:(NSString*)label +{ + CFStringRef type; + + if ([label isKindOfClass:[NSNull class]] || ![label isKindOfClass:[NSString class]]) { + type = NULL; // no label + } else if ([label caseInsensitiveCompare:kW3ContactWorkLabel] == NSOrderedSame) { + type = kABWorkLabel; + } else if ([label caseInsensitiveCompare:kW3ContactHomeLabel] == NSOrderedSame) { + type = kABHomeLabel; + } else if ([label caseInsensitiveCompare:kW3ContactOtherLabel] == NSOrderedSame) { + type = kABOtherLabel; + } else if ([label caseInsensitiveCompare:kW3ContactPhoneMobileLabel] == NSOrderedSame) { + type = kABPersonPhoneMobileLabel; + } else if ([label caseInsensitiveCompare:kW3ContactPhonePagerLabel] == NSOrderedSame) { + type = kABPersonPhonePagerLabel; + } else if ([label caseInsensitiveCompare:kW3ContactImAIMLabel] == NSOrderedSame) { + type = kABPersonInstantMessageServiceAIM; + } else if ([label caseInsensitiveCompare:kW3ContactImICQLabel] == NSOrderedSame) { + type = kABPersonInstantMessageServiceICQ; + } else if ([label caseInsensitiveCompare:kW3ContactImMSNLabel] == NSOrderedSame) { + type = kABPersonInstantMessageServiceMSN; + } else if ([label caseInsensitiveCompare:kW3ContactImYahooLabel] == NSOrderedSame) { + type = kABPersonInstantMessageServiceYahoo; + } else if ([label caseInsensitiveCompare:kW3ContactUrlProfile] == NSOrderedSame) { + type = kABPersonHomePageLabel; + } else { + // CB-3950 If label is not one of kW3*Label constants, threat it as custom label, + // otherwise fetching contact and then saving it will break this label in address book. + type = (__bridge CFStringRef)(label); + } + + return type; +} + ++ (NSString*)convertPropertyLabelToContactType:(NSString*)label +{ + NSString* type = nil; + + if (label != nil) { // improve efficiency...... + if ([label isEqualToString:(NSString*)kABPersonPhoneMobileLabel]) { + type = kW3ContactPhoneMobileLabel; + } else if ([label isEqualToString:(NSString*)kABPersonPhoneHomeFAXLabel] || + [label isEqualToString:(NSString*)kABPersonPhoneWorkFAXLabel]) { + type = kW3ContactPhoneFaxLabel; + } else if ([label isEqualToString:(NSString*)kABPersonPhonePagerLabel]) { + type = kW3ContactPhonePagerLabel; + } else if ([label isEqualToString:(NSString*)kABHomeLabel]) { + type = kW3ContactHomeLabel; + } else if ([label isEqualToString:(NSString*)kABWorkLabel]) { + type = kW3ContactWorkLabel; + } else if ([label isEqualToString:(NSString*)kABOtherLabel]) { + type = kW3ContactOtherLabel; + } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceAIM]) { + type = kW3ContactImAIMLabel; + } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceICQ]) { + type = kW3ContactImICQLabel; + } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceJabber]) { + type = kW3ContactOtherLabel; + } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceMSN]) { + type = kW3ContactImMSNLabel; + } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceYahoo]) { + type = kW3ContactImYahooLabel; + } else if ([label isEqualToString:(NSString*)kABPersonHomePageLabel]) { + type = kW3ContactUrlProfile; + } else { + // CB-3950 If label is not one of kW3*Label constants, threat it as custom label, + // otherwise fetching contact and then saving it will break this label in address book. + type = label; + } + } + return type; +} + +/* Check if the input label is a valid W3C ContactField.type. This is used when searching, + * only search field types if the search string is a valid type. If we converted any search + * string to a ABPropertyLabel it could convert to kABOtherLabel which is probably not want + * the user wanted to search for and could skew the results. + */ ++ (BOOL)isValidW3ContactType:(NSString*)label +{ + BOOL isValid = NO; + + if ([label isKindOfClass:[NSNull class]] || ![label isKindOfClass:[NSString class]]) { + isValid = NO; // no label + } else if ([label caseInsensitiveCompare:kW3ContactWorkLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactHomeLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactOtherLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactPhoneMobileLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactPhonePagerLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactImAIMLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactImICQLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactImMSNLabel] == NSOrderedSame) { + isValid = YES; + } else if ([label caseInsensitiveCompare:kW3ContactImYahooLabel] == NSOrderedSame) { + isValid = YES; + } else { + isValid = NO; + } + + return isValid; +} + +/* Create a new Contact Dictionary object from an ABRecordRef that contains information in a format such that + * it can be returned to JavaScript callback as JSON object string. + * Uses: + * ABRecordRef set into Contact Object + * NSDictionary withFields indicates which fields to return from the AddressBook Record + * + * JavaScript Contact: + * @param {DOMString} id unique identifier + * @param {DOMString} displayName + * @param {ContactName} name + * @param {DOMString} nickname + * @param {ContactField[]} phoneNumbers array of phone numbers + * @param {ContactField[]} emails array of email addresses + * @param {ContactAddress[]} addresses array of addresses + * @param {ContactField[]} ims instant messaging user ids + * @param {ContactOrganization[]} organizations + * @param {DOMString} published date contact was first created + * @param {DOMString} updated date contact was last updated + * @param {DOMString} birthday contact's birthday + * @param (DOMString} anniversary contact's anniversary + * @param {DOMString} gender contact's gender + * @param {DOMString} note user notes about contact + * @param {DOMString} preferredUsername + * @param {ContactField[]} photos + * @param {ContactField[]} tags + * @param {ContactField[]} relationships + * @param {ContactField[]} urls contact's web sites + * @param {ContactAccounts[]} accounts contact's online accounts + * @param {DOMString} timezone UTC time zone offset + * @param {DOMString} connected + */ + +- (NSDictionary*)toDictionary:(NSDictionary*)withFields +{ + // if not a person type record bail out for now + if (ABRecordGetRecordType(self.record) != kABPersonType) { + return NULL; + } + id value = nil; + self.returnFields = withFields; + + NSMutableDictionary* nc = [NSMutableDictionary dictionaryWithCapacity:1]; // new contact dictionary to fill in from ABRecordRef + // id + [nc setObject:[NSNumber numberWithInt:ABRecordGetRecordID(self.record)] forKey:kW3ContactId]; + if (self.returnFields == nil) { + // if no returnFields specified, W3C says to return empty contact (but Cordova will at least return id) + return nc; + } + if ([self.returnFields objectForKey:kW3ContactDisplayName]) { + // displayname requested - iOS doesn't have so return null + [nc setObject:[NSNull null] forKey:kW3ContactDisplayName]; + // may overwrite below if requested ContactName and there are no values + } + // nickname + if ([self.returnFields valueForKey:kW3ContactNickname]) { + value = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, kABPersonNicknameProperty); + [nc setObject:(value != nil) ? value:[NSNull null] forKey:kW3ContactNickname]; + } + + // name dictionary + // NSLog(@"getting name info"); + NSObject* data = [self extractName]; + if (data != nil) { + [nc setObject:data forKey:kW3ContactName]; + } + if ([self.returnFields objectForKey:kW3ContactDisplayName] && ((data == nil) || ([(NSDictionary*)data objectForKey : kW3ContactFormattedName] == [NSNull null]))) { + // user asked for displayName which iOS doesn't support but there is no other name data being returned + // try and use Composite Name so some name is returned + id tryName = (__bridge_transfer NSString*)ABRecordCopyCompositeName(self.record); + if (tryName != nil) { + [nc setObject:tryName forKey:kW3ContactDisplayName]; + } else { + // use nickname or empty string + value = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, kABPersonNicknameProperty); + [nc setObject:(value != nil) ? value:@"" forKey:kW3ContactDisplayName]; + } + } + // phoneNumbers array + // NSLog(@"getting phoneNumbers"); + value = [self extractMultiValue:kW3ContactPhoneNumbers]; + if (value != nil) { + [nc setObject:value forKey:kW3ContactPhoneNumbers]; + } + // emails array + // NSLog(@"getting emails"); + value = [self extractMultiValue:kW3ContactEmails]; + if (value != nil) { + [nc setObject:value forKey:kW3ContactEmails]; + } + // urls array + value = [self extractMultiValue:kW3ContactUrls]; + if (value != nil) { + [nc setObject:value forKey:kW3ContactUrls]; + } + // addresses array + // NSLog(@"getting addresses"); + value = [self extractAddresses]; + if (value != nil) { + [nc setObject:value forKey:kW3ContactAddresses]; + } + // im array + // NSLog(@"getting ims"); + value = [self extractIms]; + if (value != nil) { + [nc setObject:value forKey:kW3ContactIms]; + } + // organization array (only info for one organization in iOS) + // NSLog(@"getting organizations"); + value = [self extractOrganizations]; + if (value != nil) { + [nc setObject:value forKey:kW3ContactOrganizations]; + } + + // for simple properties, could make this a bit more efficient by storing all simple properties in a single + // array in the returnFields dictionary and setting them via a for loop through the array + + // add dates + // NSLog(@"getting dates"); + NSNumber* ms; + + /** Contact Revision field removed from June 16, 2011 version of specification + + if ([self.returnFields valueForKey:kW3ContactUpdated]){ + ms = [self getDateAsNumber: kABPersonModificationDateProperty]; + if (!ms){ + // try and get published date + ms = [self getDateAsNumber: kABPersonCreationDateProperty]; + } + if (ms){ + [nc setObject: ms forKey:kW3ContactUpdated]; + } + + } + */ + + if ([self.returnFields valueForKey:kW3ContactBirthday]) { + ms = [self getDateAsNumber:kABPersonBirthdayProperty]; + if (ms) { + [nc setObject:ms forKey:kW3ContactBirthday]; + } + } + + /* Anniversary removed from 12-09-2010 W3C Contacts api spec + if ([self.returnFields valueForKey:kW3ContactAnniversary]){ + // Anniversary date is stored in a multivalue property + ABMultiValueRef multi = ABRecordCopyValue(self.record, kABPersonDateProperty); + if (multi){ + CFStringRef label = nil; + CFIndex count = ABMultiValueGetCount(multi); + // see if contains an Anniversary date + for(CFIndex i=0; i 0) { // ?? this will always be true since we set id,label,primary field?? + [(NSMutableArray*)addresses addObject : newAddress]; + } + CFRelease(dict); + } // end of loop through addresses + } else { + addresses = [NSNull null]; + } + if (multi) { + CFRelease(multi); + } + + return addresses; +} + +/* Create array of Dictionaries to match JavaScript ContactField object for ims + * type one of [aim, gtalk, icq, xmpp, msn, skype, qq, yahoo] needs other as well + * value + * (bool) primary + * id + * + * iOS IMs are a MultiValue Properties with label, value=dictionary of IM details (service, username), and id + */ +- (NSObject*)extractIms +{ + NSArray* fields = [self.returnFields objectForKey:kW3ContactIms]; + + if (fields == nil) { // no name fields requested + return nil; + } + NSObject* imArray; + ABMultiValueRef multi = ABRecordCopyValue(self.record, kABPersonInstantMessageProperty); + CFIndex count = multi ? ABMultiValueGetCount(multi) : 0; + if (count) { + imArray = [NSMutableArray arrayWithCapacity:count]; + + for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) { + NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:3]; + // iOS has label property (work, home, other) for each IM but W3C contact API doesn't use + CFDictionaryRef dict = (CFDictionaryRef)ABMultiValueCopyValueAtIndex(multi, i); + CFStringRef value; // all values should be CFStringRefs / NSString* + bool bFound; + if ([fields containsObject:kW3ContactFieldValue]) { + // value = user name + bFound = CFDictionaryGetValueIfPresent(dict, kABPersonInstantMessageUsernameKey, (void*)&value); + if (bFound && (value != NULL)) { + CFRetain(value); + [newDict setObject:(__bridge id)value forKey:kW3ContactFieldValue]; + CFRelease(value); + } else { + [newDict setObject:[NSNull null] forKey:kW3ContactFieldValue]; + } + } + if ([fields containsObject:kW3ContactFieldType]) { + bFound = CFDictionaryGetValueIfPresent(dict, kABPersonInstantMessageServiceKey, (void*)&value); + if (bFound && (value != NULL)) { + CFRetain(value); + [newDict setObject:(id)[[CDVContact class] convertPropertyLabelToContactType : (__bridge NSString*)value] forKey:kW3ContactFieldType]; + CFRelease(value); + } else { + [newDict setObject:[NSNull null] forKey:kW3ContactFieldType]; + } + } + // always set ID + id identifier = [NSNumber numberWithUnsignedInt:ABMultiValueGetIdentifierAtIndex(multi, i)]; + [newDict setObject:(identifier != nil) ? identifier:[NSNull null] forKey:kW3ContactFieldId]; + + [(NSMutableArray*)imArray addObject : newDict]; + CFRelease(dict); + } + } else { + imArray = [NSNull null]; + } + + if (multi) { + CFRelease(multi); + } + return imArray; +} + +/* Create array of Dictionaries to match JavaScript ContactOrganization object + * pref - not supported in iOS + * type - not supported in iOS + * name + * department + * title + */ + +- (NSObject*)extractOrganizations +{ + NSArray* fields = [self.returnFields objectForKey:kW3ContactOrganizations]; + + if (fields == nil) { // no name fields requested + return nil; + } + NSObject* array = nil; + NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:5]; + id value; + int validValueCount = 0; + + for (id i in fields) { + id key = [[CDVContact defaultW3CtoAB] valueForKey:i]; + if (key && [key isKindOfClass:[NSNumber class]]) { + value = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, (ABPropertyID)[[[CDVContact defaultW3CtoAB] valueForKey:i] intValue]); + if (value != nil) { + // if there are no organization values we should return null for organization + // this counter keeps indicates if any organization values have been set + validValueCount++; + } + [newDict setObject:(value != nil) ? value:[NSNull null] forKey:i]; + } else { // not a key iOS supports, set to null + [newDict setObject:[NSNull null] forKey:i]; + } + } + + if (([newDict count] > 0) && (validValueCount > 0)) { + // add pref and type + // they are not supported by iOS and thus these values never change + [newDict setObject:@"false" forKey:kW3ContactFieldPrimary]; + [newDict setObject:[NSNull null] forKey:kW3ContactFieldType]; + array = [NSMutableArray arrayWithCapacity:1]; + [(NSMutableArray*)array addObject : newDict]; + } else { + array = [NSNull null]; + } + return array; +} + +// W3C Contacts expects an array of photos. Can return photos in more than one format, currently +// just returning the default format +// Save the photo data into tmp directory and return FileURI - temp directory is deleted upon application exit +- (NSObject*)extractPhotos +{ + NSMutableArray* photos = nil; + + if (ABPersonHasImageData(self.record)) { + CFIndex photoId = ABRecordGetRecordID(self.record); + CFDataRef photoData = ABPersonCopyImageData(self.record); + if (!photoData) { + return nil; + } + + NSData* data = (__bridge NSData*)photoData; + // write to temp directory and store URI in photos array + // get the temp directory path + NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath]; + NSString* filePath = [NSString stringWithFormat:@"%@/contact_photo_%ld", docsPath, photoId]; + + // save file + if ([data writeToFile:filePath atomically:YES]) { + photos = [NSMutableArray arrayWithCapacity:1]; + NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:2]; + [newDict setObject:filePath forKey:kW3ContactFieldValue]; + [newDict setObject:@"url" forKey:kW3ContactFieldType]; + [newDict setObject:@"false" forKey:kW3ContactFieldPrimary]; + [photos addObject:newDict]; + } + + CFRelease(photoData); + } + return photos; +} + +/** + * given an array of W3C Contact field names, create a dictionary of field names to extract + * if field name represents an object, return all properties for that object: "name" - returns all properties in ContactName + * if field name is an explicit property, return only those properties: "name.givenName - returns a ContactName with only ContactName.givenName + * if field contains ONLY ["*"] return all fields + * dictionary format: + * key is W3Contact #define + * value is NSMutableArray* for complex keys: name,addresses,organizations, phone, emails, ims + * value is [NSNull null] for simple keys +*/ ++ (NSDictionary*)calcReturnFields:(NSArray*)fieldsArray // NSLog(@"getting self.returnFields"); +{ + NSMutableDictionary* d = [NSMutableDictionary dictionaryWithCapacity:1]; + + if ((fieldsArray != nil) && [fieldsArray isKindOfClass:[NSArray class]]) { + if (([fieldsArray count] == 1) && [[fieldsArray objectAtIndex:0] isEqualToString:@"*"]) { + return [CDVContact defaultFields]; // return all fields + } + + for (id i in fieldsArray) { + + // CB-7906 ignore NULL desired fields to avoid fatal exception + if ([i isKindOfClass:[NSNull class]]) continue; + + NSMutableArray* keys = nil; + NSString* fieldStr = nil; + if ([i isKindOfClass:[NSNumber class]]) { + fieldStr = [i stringValue]; + } else { + fieldStr = i; + } + + // see if this is specific property request in object - object.property + NSArray* parts = [fieldStr componentsSeparatedByString:@"."]; // returns original string if no separator found + NSString* name = [parts objectAtIndex:0]; + NSString* property = nil; + if ([parts count] > 1) { + property = [parts objectAtIndex:1]; + } + // see if this is a complex field by looking for its array of properties in objectAndProperties dictionary + id fields = [[CDVContact defaultObjectAndProperties] objectForKey:name]; + + // if find complex name (name,addresses,organizations, phone, emails, ims) in fields, add name as key + // with array of associated properties as the value + if ((fields != nil) && (property == nil)) { // request was for full object + keys = [NSMutableArray arrayWithArray:fields]; + if (keys != nil) { + [d setObject:keys forKey:name]; // will replace if prop array already exists + } + } else if ((fields != nil) && (property != nil)) { + // found an individual property request in form of name.property + // verify is real property name by using it as key in W3CtoAB + id abEquiv = [[CDVContact defaultW3CtoAB] objectForKey:property]; + if (abEquiv || [[CDVContact defaultW3CtoNull] containsObject:property]) { + // if existing array add to it + if ((keys = [d objectForKey:name]) != nil) { + [keys addObject:property]; + } else { + keys = [NSMutableArray arrayWithObject:property]; + [d setObject:keys forKey:name]; + } + } else { + NSLog(@"Contacts.find -- request for invalid property ignored: %@.%@", name, property); + } + } else { // is an individual property, verify is real property name by using it as key in W3CtoAB + id valid = [[CDVContact defaultW3CtoAB] objectForKey:name]; + if (valid || [[CDVContact defaultW3CtoNull] containsObject:name]) { + [d setObject:[NSNull null] forKey:name]; + } + } + } + } + if ([d count] == 0) { + // no array or nothing in the array. W3C spec says to return nothing + return nil; // [Contact defaultFields]; + } + return d; +} + + +- (BOOL)valueForKeyIsArray:(NSDictionary*)dict key:(NSString*)key +{ + BOOL bArray = NO; + NSObject* value = [dict objectForKey:key]; + + if (value) { + bArray = [value isKindOfClass:[NSArray class]]; + } + return bArray; +} + + +/* + * Search for the specified value in each of the fields specified in the searchFields dictionary. + * NSString* value - the string value to search for (need clarification from W3C on how to search for dates) + * NSDictionary* searchFields - a dictionary created via calcReturnFields where the key is the top level W3C + * object and the object is the array of specific fields within that object or null if it is a single property + * RETURNS + * YES as soon as a match is found in any of the fields + * NO - the specified value does not exist in any of the fields in this contact + * + * Note: I'm not a fan of returning in the middle of methods but have done it some in this method in order to + * keep the code simpler. bgibson + */ +- (BOOL)foundValue:(NSString*)testValue inFields:(NSDictionary*)searchFields +{ + BOOL bFound = NO; + + if ((testValue == nil) || ![testValue isKindOfClass:[NSString class]] || ([testValue length] == 0)) { + // nothing to find so return NO + return NO; + } + NSInteger valueAsInt = [testValue integerValue]; + + // per W3C spec, always include id in search + int recordId = ABRecordGetRecordID(self.record); + if (valueAsInt && (recordId == valueAsInt)) { + return YES; + } + + if (searchFields == nil) { + // no fields to search + return NO; + } + + if ([searchFields valueForKey:kW3ContactNickname]) { + bFound = [self testStringValue:testValue forW3CProperty:kW3ContactNickname]; + if (bFound == YES) { + return bFound; + } + } + + if ([self valueForKeyIsArray:searchFields key:kW3ContactName]) { + // test name fields. All are string properties obtained via ABRecordCopyValue except kW3ContactFormattedName + NSArray* fields = [searchFields valueForKey:kW3ContactName]; + + for (NSString* testItem in fields) { + if ([testItem isEqualToString:kW3ContactFormattedName]) { + NSString* propValue = (__bridge_transfer NSString*)ABRecordCopyCompositeName(self.record); + if ((propValue != nil) && ([propValue length] > 0)) { + NSRange range = [propValue rangeOfString:testValue options:NSCaseInsensitiveSearch]; + bFound = (range.location != NSNotFound); + propValue = nil; + } + } else { + bFound = [self testStringValue:testValue forW3CProperty:testItem]; + } + + if (bFound) { + break; + } + } + } + if (!bFound && [self valueForKeyIsArray:searchFields key:kW3ContactPhoneNumbers]) { + bFound = [self searchContactFields:(NSArray*)[searchFields valueForKey:kW3ContactPhoneNumbers] + forMVStringProperty:kABPersonPhoneProperty withValue:testValue]; + } + if (!bFound && [self valueForKeyIsArray:searchFields key:kW3ContactEmails]) { + bFound = [self searchContactFields:(NSArray*)[searchFields valueForKey:kW3ContactEmails] + forMVStringProperty:kABPersonEmailProperty withValue:testValue]; + } + + if (!bFound && [self valueForKeyIsArray:searchFields key:kW3ContactAddresses]) { + bFound = [self searchContactFields:[searchFields valueForKey:kW3ContactAddresses] + forMVDictionaryProperty:kABPersonAddressProperty withValue:testValue]; + } + + if (!bFound && [self valueForKeyIsArray:searchFields key:kW3ContactIms]) { + bFound = [self searchContactFields:[searchFields valueForKey:kW3ContactIms] + forMVDictionaryProperty:kABPersonInstantMessageProperty withValue:testValue]; + } + + if (!bFound && [self valueForKeyIsArray:searchFields key:kW3ContactOrganizations]) { + NSArray* fields = [searchFields valueForKey:kW3ContactOrganizations]; + + for (NSString* testItem in fields) { + bFound = [self testStringValue:testValue forW3CProperty:testItem]; + if (bFound == YES) { + break; + } + } + } + if (!bFound && [searchFields valueForKey:kW3ContactNote]) { + bFound = [self testStringValue:testValue forW3CProperty:kW3ContactNote]; + } + + // if searching for a date field is requested, get the date field as a localized string then look for match against testValue in date string + // searching for photos is not supported + if (!bFound && [searchFields valueForKey:kW3ContactBirthday]) { + bFound = [self testDateValue:testValue forW3CProperty:kW3ContactBirthday]; + } + if (!bFound && [self valueForKeyIsArray:searchFields key:kW3ContactUrls]) { + bFound = [self searchContactFields:(NSArray*)[searchFields valueForKey:kW3ContactUrls] + forMVStringProperty:kABPersonURLProperty withValue:testValue]; + } + + return bFound; +} + +- (BOOL)valueForKeyIsNumber:(NSDictionary*)dict key:(NSString*)key +{ + BOOL bNumber = NO; + NSObject* value = [dict objectForKey:key]; + + if (value) { + bNumber = [value isKindOfClass:[NSNumber class]]; + } + return bNumber; +} + +/* + * Test for the existence of a given string within the value of a ABPersonRecord string property based on the W3c property name. + * + * IN: + * NSString* testValue - the value to find - search is case insensitive + * NSString* property - the W3c property string + * OUT: + * BOOL YES if the given string was found within the property value + * NO if the testValue was not found, W3C property string was invalid or the AddressBook property was not a string + */ +- (BOOL)testStringValue:(NSString*)testValue forW3CProperty:(NSString*)property +{ + BOOL bFound = NO; + + if ([self valueForKeyIsNumber:[CDVContact defaultW3CtoAB] key:property]) { + ABPropertyID propId = [[[CDVContact defaultW3CtoAB] objectForKey:property] intValue]; + if (ABPersonGetTypeOfProperty(propId) == kABStringPropertyType) { + NSString* propValue = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, propId); + if ((propValue != nil) && ([propValue length] > 0)) { + NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testValue]; + bFound = [containPred evaluateWithObject:propValue]; + // NSRange range = [propValue rangeOfString:testValue options: NSCaseInsensitiveSearch]; + // bFound = (range.location != NSNotFound); + } + } + } + return bFound; +} + +/* + * Test for the existence of a given Date string within the value of a ABPersonRecord datetime property based on the W3c property name. + * + * IN: + * NSString* testValue - the value to find - search is case insensitive + * NSString* property - the W3c property string + * OUT: + * BOOL YES if the given string was found within the localized date string value + * NO if the testValue was not found, W3C property string was invalid or the AddressBook property was not a DateTime + */ +- (BOOL)testDateValue:(NSString*)testValue forW3CProperty:(NSString*)property +{ + BOOL bFound = NO; + + if ([self valueForKeyIsNumber:[CDVContact defaultW3CtoAB] key:property]) { + ABPropertyID propId = [[[CDVContact defaultW3CtoAB] objectForKey:property] intValue]; + if (ABPersonGetTypeOfProperty(propId) == kABDateTimePropertyType) { + NSDate* date = (__bridge_transfer NSDate*)ABRecordCopyValue(self.record, propId); + if (date != nil) { + NSString* dateString = [date descriptionWithLocale:[NSLocale currentLocale]]; + NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testValue]; + bFound = [containPred evaluateWithObject:dateString]; + } + } + } + return bFound; +} + +/* + * Search the specified fields within an AddressBook multivalue string property for the specified test value. + * Used for phoneNumbers, emails and urls. + * IN: + * NSArray* fields - the fields to search for within the multistring property (value and/or type) + * ABPropertyID - the property to search + * NSString* testValue - the value to search for. Will convert between W3C types and AB types. Will only + * search for types if the testValue is a valid ContactField type. + * OUT: + * YES if the test value was found in one of the specified fields + * NO if the test value was not found + */ +- (BOOL)searchContactFields:(NSArray*)fields forMVStringProperty:(ABPropertyID)propId withValue:testValue +{ + BOOL bFound = NO; + + for (NSString* type in fields) { + NSString* testString = nil; + if ([type isEqualToString:kW3ContactFieldType]) { + if ([CDVContact isValidW3ContactType:testValue]) { + // only search types if the filter string is a valid ContactField.type + testString = (NSString*)[CDVContact convertContactTypeToPropertyLabel:testValue]; + } + } else { + testString = testValue; + } + + if (testString != nil) { + bFound = [self testMultiValueStrings:testString forProperty:propId ofType:type]; + } + if (bFound == YES) { + break; + } + } + + return bFound; +} + +/* + * Searches a multiString value of the specified type for the specified test value. + * + * IN: + * NSString* testValue - the value to test for + * ABPropertyID propId - the property id of the multivalue property to search + * NSString* type - the W3C contact type to search for (value or type) + * OUT: + * YES is the test value was found + * NO if the test value was not found + */ +- (BOOL)testMultiValueStrings:(NSString*)testValue forProperty:(ABPropertyID)propId ofType:(NSString*)type +{ + BOOL bFound = NO; + + if (ABPersonGetTypeOfProperty(propId) == kABMultiStringPropertyType) { + NSArray* valueArray = nil; + if ([type isEqualToString:kW3ContactFieldType]) { + valueArray = [self labelsForProperty:propId inRecord:self.record]; + } else if ([type isEqualToString:kW3ContactFieldValue]) { + valueArray = [self valuesForProperty:propId inRecord:self.record]; + } + if (valueArray) { + NSString* valuesAsString = [valueArray componentsJoinedByString:@" "]; + NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testValue]; + bFound = [containPred evaluateWithObject:valuesAsString]; + } + } + return bFound; +} + +/* + * Returns the array of values for a multivalue string property of the specified property id + */ +- (__autoreleasing NSArray*)valuesForProperty:(ABPropertyID)propId inRecord:(ABRecordRef)aRecord +{ + ABMultiValueRef multi = ABRecordCopyValue(aRecord, propId); + NSArray* values = (__bridge_transfer NSArray*)ABMultiValueCopyArrayOfAllValues(multi); + + CFRelease(multi); + return values; +} + +/* + * Returns the array of labels for a multivalue string property of the specified property id + */ +- (NSArray*)labelsForProperty:(ABPropertyID)propId inRecord:(ABRecordRef)aRecord +{ + ABMultiValueRef multi = ABRecordCopyValue(aRecord, propId); + CFIndex count = ABMultiValueGetCount(multi); + NSMutableArray* labels = [NSMutableArray arrayWithCapacity:count]; + + for (int i = 0; i < count; i++) { + NSString* label = (__bridge_transfer NSString*)ABMultiValueCopyLabelAtIndex(multi, i); + if (label) { + [labels addObject:label]; + } + } + + CFRelease(multi); + return labels; +} + +/* search for values within MultiValue Dictionary properties Address or IM property + * IN: + * (NSArray*) fields - the array of W3C field names to search within + * (ABPropertyID) propId - the AddressBook property that returns a multivalue dictionary + * (NSString*) testValue - the string to search for within the specified fields + * + */ +- (BOOL)searchContactFields:(NSArray*)fields forMVDictionaryProperty:(ABPropertyID)propId withValue:(NSString*)testValue +{ + BOOL bFound = NO; + + NSArray* values = [self valuesForProperty:propId inRecord:self.record]; // array of dictionaries (as CFDictionaryRef) + NSUInteger dictCount = [values count]; + + // for ims dictionary contains with service (w3C type) and username (W3c value) + // for addresses dictionary contains street, city, state, zip, country + for (int i = 0; i < dictCount; i++) { + CFDictionaryRef dict = (__bridge CFDictionaryRef)[values objectAtIndex:i]; + + for (NSString* member in fields) { + NSString* abKey = [[CDVContact defaultW3CtoAB] valueForKey:member]; // im and address fields are all strings + CFStringRef abValue = nil; + if (abKey) { + NSString* testString = nil; + if ([member isEqualToString:kW3ContactImType]) { + if ([CDVContact isValidW3ContactType:testValue]) { + // only search service/types if the filter string is a valid ContactField.type + testString = (NSString*)[CDVContact convertContactTypeToPropertyLabel:testValue]; + } + } else { + testString = testValue; + } + if (testString != nil) { + BOOL bExists = CFDictionaryGetValueIfPresent(dict, (__bridge const void*)abKey, (void*)&abValue); + if (bExists) { + CFRetain(abValue); + NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testString]; + bFound = [containPred evaluateWithObject:(__bridge id)abValue]; + CFRelease(abValue); + } + } + } + if (bFound == YES) { + break; + } + } // end of for each member in fields + + if (bFound == YES) { + break; + } + } // end of for each dictionary + + return bFound; +} + +@end diff --git a/plugins/cordova-plugin-contacts/src/ios/CDVContacts.h b/plugins/cordova-plugin-contacts/src/ios/CDVContacts.h new file mode 100644 index 0000000..479d390 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/ios/CDVContacts.h @@ -0,0 +1,160 @@ +/* + 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 +#import +#import +#import +#import "CDVContact.h" + +@interface CDVContacts : CDVPlugin +{ + ABAddressBookRef addressBook; +} + +/* + * newContact - create a new contact via the GUI + * + * arguments: + * 1: successCallback: this is the javascript function that will be called with the newly created contactId + */ +- (void)newContact:(CDVInvokedUrlCommand*)command; + +/* + * displayContact - IN PROGRESS + * + * arguments: + * 1: recordID of the contact to display in the iPhone contact display + * 2: successCallback - currently not used + * 3: error callback + * options: + * allowsEditing: set to true to allow the user to edit the contact - currently not supported + */ +- (void)displayContact:(CDVInvokedUrlCommand*)command; + +/* + * chooseContact + * + * arguments: + * 1: this is the javascript function that will be called with the contact data as a JSON object (as the first param) + * options: + * allowsEditing: set to true to not choose the contact, but to edit it in the iPhone contact editor + */ +- (void)chooseContact:(CDVInvokedUrlCommand*)command; + +- (void)newPersonViewController:(ABNewPersonViewController*)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person; +- (BOOL)personViewController:(ABPersonViewController*)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person + property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue; +/* + * Launches the Contact Picker to select a single contact. + * + * arguments: + * 1: this is the javascript function that will be called with the contact data as a JSON object (as the first param) + * options: + * desiredFields: ContactFields array to be returned back + */ +- (void)pickContact:(CDVInvokedUrlCommand*)command; + +/* + * search - searches for contacts. Only person records are currently supported. + * + * arguments: + * 1: successcallback - this is the javascript function that will be called with the array of found contacts + * 2: errorCallback - optional javascript function to be called in the event of an error with an error code. + * options: dictionary containing ContactFields and ContactFindOptions + * fields - ContactFields array + * findOptions - ContactFindOptions object as dictionary + * + */ +- (void)search:(CDVInvokedUrlCommand*)command; + +/* + * save - saves a new contact or updates and existing contact + * + * arguments: + * 1: success callback - this is the javascript function that will be called with the JSON representation of the saved contact + * search calls a fixed navigator.service.contacts._findCallback which then calls the success callback stored before making the call into obj-c + */ +- (void)save:(CDVInvokedUrlCommand*)command; + +/* + * remove - removes a contact from the address book + * + * arguments: + * 1: 1: successcallback - this is the javascript function that will be called with a (now) empty contact object + * + * options: dictionary containing Contact object to remove + * contact - Contact object as dictionary + */ +- (void)remove:(CDVInvokedUrlCommand*)command; + +// - (void) dealloc; + +@end + +@interface CDVContactsPicker : ABPeoplePickerNavigationController +{ + BOOL allowsEditing; + NSString* callbackId; + NSDictionary* options; + NSDictionary* pickedContactDictionary; +} + +@property BOOL allowsEditing; +@property (copy) NSString* callbackId; +@property (nonatomic, strong) NSDictionary* options; +@property (nonatomic, strong) NSDictionary* pickedContactDictionary; + +@end + +@interface CDVNewContactsController : ABNewPersonViewController +{ + NSString* callbackId; +} +@property (copy) NSString* callbackId; +@end + +/* ABPersonViewController does not have any UI to dismiss. Adding navigationItems to it does not work properly, the navigationItems are lost when the app goes into the background. + The solution was to create an empty NavController in front of the ABPersonViewController. This + causes the ABPersonViewController to have a back button. By subclassing the ABPersonViewController, + we can override viewWillDisappear and take down the entire NavigationController at that time. + */ +@interface CDVDisplayContactViewController : ABPersonViewController +{} +@property (nonatomic, strong) CDVPlugin* contactsPlugin; + +@end +@interface CDVAddressBookAccessError : NSObject +{} +@property (assign) CDVContactError errorCode; +- (CDVAddressBookAccessError*)initWithCode:(CDVContactError)code; +@end + +typedef void (^ CDVAddressBookWorkerBlock)( + ABAddressBookRef addressBook, + CDVAddressBookAccessError* error + ); +@interface CDVAddressBookHelper : NSObject +{} + +- (void)createAddressBook:(CDVAddressBookWorkerBlock)workerBlock; +@end diff --git a/plugins/cordova-plugin-contacts/src/ios/CDVContacts.m b/plugins/cordova-plugin-contacts/src/ios/CDVContacts.m new file mode 100644 index 0000000..d5dd958 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/ios/CDVContacts.m @@ -0,0 +1,612 @@ +/* + 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 "CDVContacts.h" +#import + +@implementation CDVContactsPicker + +@synthesize allowsEditing; +@synthesize callbackId; +@synthesize options; +@synthesize pickedContactDictionary; + +@end +@implementation CDVNewContactsController + +@synthesize callbackId; + +@end + +@implementation CDVContacts + +// overridden to clean up Contact statics +- (void)onAppTerminate +{ + // NSLog(@"Contacts::onAppTerminate"); +} + +// iPhone only method to create a new contact through the GUI +- (void)newContact:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + + CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init]; + CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles + + [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) { + if (addrBook == NULL) { + // permission was denied or other error just return (no error callback) + return; + } + CDVNewContactsController* npController = [[CDVNewContactsController alloc] init]; + npController.addressBook = addrBook; // a CF retaining assign + CFRelease(addrBook); + + npController.newPersonViewDelegate = self; + npController.callbackId = callbackId; + + UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:npController]; + + [weakSelf.viewController presentViewController:navController animated:YES completion:nil]; + }]; +} + +- (void)newPersonViewController:(ABNewPersonViewController*)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person +{ + ABRecordID recordId = kABRecordInvalidID; + CDVNewContactsController* newCP = (CDVNewContactsController*)newPersonViewController; + NSString* callbackId = newCP.callbackId; + + if (person != NULL) { + // return the contact id + recordId = ABRecordGetRecordID(person); + } + + [[newPersonViewController presentingViewController] dismissViewControllerAnimated:YES completion:nil]; + + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:recordId]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; +} + +- (bool)existsValue:(NSDictionary*)dict val:(NSString*)expectedValue forKey:(NSString*)key +{ + id val = [dict valueForKey:key]; + bool exists = false; + + if (val != nil) { + exists = [(NSString*)val compare : expectedValue options : NSCaseInsensitiveSearch] == 0; + } + + return exists; +} + +- (void)displayContact:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + ABRecordID recordID = [[command argumentAtIndex:0] intValue]; + NSDictionary* options = [command argumentAtIndex:1 withDefault:[NSNull null]]; + bool bEdit = [options isKindOfClass:[NSNull class]] ? false : [self existsValue:options val:@"true" forKey:@"allowsEditing"]; + + CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init]; + CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles + + [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) { + if (addrBook == NULL) { + // permission was denied or other error - return error + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? (int)errCode.errorCode:UNKNOWN_ERROR]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + return; + } + ABRecordRef rec = ABAddressBookGetPersonWithRecordID(addrBook, recordID); + + if (rec) { + CDVDisplayContactViewController* personController = [[CDVDisplayContactViewController alloc] init]; + personController.displayedPerson = rec; + personController.personViewDelegate = self; + personController.allowsEditing = NO; + + // create this so DisplayContactViewController will have a "back" button. + UIViewController* parentController = [[UIViewController alloc] init]; + UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:parentController]; + + [navController pushViewController:personController animated:YES]; + + [self.viewController presentViewController:navController animated:YES completion:nil]; + + if (bEdit) { + // create the editing controller and push it onto the stack + ABPersonViewController* editPersonController = [[ABPersonViewController alloc] init]; + editPersonController.displayedPerson = rec; + editPersonController.personViewDelegate = self; + editPersonController.allowsEditing = YES; + [navController pushViewController:editPersonController animated:YES]; + } + } else { + // no record, return error + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:UNKNOWN_ERROR]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + CFRelease(addrBook); + }]; +} + +- (BOOL)personViewController:(ABPersonViewController*)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person + property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue +{ + return YES; +} + +- (void)chooseContact:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSDictionary* options = [command argumentAtIndex:0 withDefault:[NSNull null]]; + + CDVContactsPicker* pickerController = [[CDVContactsPicker alloc] init]; + + pickerController.peoplePickerDelegate = self; + pickerController.callbackId = callbackId; + pickerController.options = options; + pickerController.pickedContactDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kABRecordInvalidID], kW3ContactId, nil]; + id allowsEditingValue = [options valueForKey:@"allowsEditing"]; + BOOL allowsEditing = NO; + if ([allowsEditingValue isKindOfClass:[NSNumber class]]) { + allowsEditing = [(NSNumber*)allowsEditingValue boolValue]; + } + pickerController.allowsEditing = allowsEditing; + + [self.viewController presentViewController:pickerController animated:YES completion:nil]; +} + +- (void)pickContact:(CDVInvokedUrlCommand *)command +{ + // mimic chooseContact method call with required for us parameters + NSArray* desiredFields = [command argumentAtIndex:0 withDefault:[NSArray array]]; + if (desiredFields == nil || desiredFields.count == 0) { + desiredFields = [NSArray arrayWithObjects:@"*", nil]; + } + NSMutableDictionary* options = [NSMutableDictionary dictionaryWithCapacity:2]; + + [options setObject: desiredFields forKey:@"fields"]; + [options setObject: [NSNumber numberWithBool: FALSE] forKey:@"allowsEditing"]; + + NSArray* args = [NSArray arrayWithObjects:options, nil]; + + CDVInvokedUrlCommand* newCommand = [[CDVInvokedUrlCommand alloc] initWithArguments:args + callbackId:command.callbackId + className:command.className + methodName:command.methodName]; + + // First check for Address book permissions + ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus(); + if (status == kABAuthorizationStatusAuthorized) { + [self chooseContact:newCommand]; + return; + } + + CDVPluginResult *errorResult = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsInt:PERMISSION_DENIED_ERROR]; + + // if the access is already restricted/denied the only way is to fail + if (status == kABAuthorizationStatusRestricted || status == kABAuthorizationStatusDenied) { + [self.commandDelegate sendPluginResult: errorResult callbackId:command.callbackId]; + return; + } + + // if no permissions granted try to request them first + if (status == kABAuthorizationStatusNotDetermined) { + ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { + if (granted) { + [self chooseContact:newCommand]; + return; + } + + [self.commandDelegate sendPluginResult: errorResult callbackId:command.callbackId]; + }); + } +} + +- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker + shouldContinueAfterSelectingPerson:(ABRecordRef)person +{ + [self peoplePickerNavigationController:peoplePicker didSelectPerson:person]; + return NO; +} + +- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker + shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier +{ + return YES; +} + +- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController*)peoplePicker +{ + // return contactId or invalid if none picked + CDVContactsPicker* picker = (CDVContactsPicker*)peoplePicker; + + if (picker.allowsEditing) { + // get the info after possible edit + // if we got this far, user has already approved/ disapproved addressBook access + ABAddressBookRef addrBook = ABAddressBookCreateWithOptions(NULL, NULL); + ABRecordRef person = ABAddressBookGetPersonWithRecordID(addrBook, (int)[[picker.pickedContactDictionary objectForKey:kW3ContactId] integerValue]); + if (person) { + CDVContact* pickedContact = [[CDVContact alloc] initFromABRecord:(ABRecordRef)person]; + NSArray* fields = [picker.options objectForKey:@"fields"]; + NSDictionary* returnFields = [[CDVContact class] calcReturnFields:fields]; + picker.pickedContactDictionary = [pickedContact toDictionary:returnFields]; + } + CFRelease(addrBook); + } + + CDVPluginResult* result = nil; + NSNumber* recordId = picker.pickedContactDictionary[kW3ContactId]; + + if ([recordId isEqualToNumber:[NSNumber numberWithInt:kABRecordInvalidID]]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:OPERATION_CANCELLED_ERROR] ; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:picker.pickedContactDictionary]; + } + + [self.commandDelegate sendPluginResult:result callbackId:picker.callbackId]; + + [[peoplePicker presentingViewController] dismissViewControllerAnimated:YES completion:nil]; +} + +// Called after a person has been selected by the user. +- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person +{ + CDVContactsPicker* picker = (CDVContactsPicker*)peoplePicker; + NSNumber* pickedId = [NSNumber numberWithInt:ABRecordGetRecordID(person)]; + + if (picker.allowsEditing) { + ABPersonViewController* personController = [[ABPersonViewController alloc] init]; + personController.displayedPerson = person; + personController.personViewDelegate = self; + personController.allowsEditing = picker.allowsEditing; + // store id so can get info in peoplePickerNavigationControllerDidCancel + picker.pickedContactDictionary = [NSDictionary dictionaryWithObjectsAndKeys:pickedId, kW3ContactId, nil]; + + [peoplePicker pushViewController:personController animated:YES]; + } else { + // Retrieve and return pickedContact information + CDVContact* pickedContact = [[CDVContact alloc] initFromABRecord:(ABRecordRef)person]; + NSArray* fields = [picker.options objectForKey:@"fields"]; + NSDictionary* returnFields = [[CDVContact class] calcReturnFields:fields]; + picker.pickedContactDictionary = [pickedContact toDictionary:returnFields]; + + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:picker.pickedContactDictionary]; + [self.commandDelegate sendPluginResult:result callbackId:picker.callbackId]; + + [[picker presentingViewController] dismissViewControllerAnimated:YES completion:nil]; + } +} + +// Called after a property has been selected by the user. +- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier +{ + // not implemented +} + +- (void)search:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSArray* fields = [command argumentAtIndex:0]; + NSDictionary* findOptions = [command argumentAtIndex:1 withDefault:[NSNull null]]; + + [self.commandDelegate runInBackground:^{ + // from Apple: Important You must ensure that an instance of ABAddressBookRef is used by only one thread. + // which is why address book is created within the dispatch queue. + // more details here: http: //blog.byadrian.net/2012/05/05/ios-addressbook-framework-and-gcd/ + CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init]; + CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles + // it gets uglier, block within block..... + [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) { + if (addrBook == NULL) { + // permission was denied or other error - return error + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? (int)errCode.errorCode:UNKNOWN_ERROR]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + return; + } + + NSArray* foundRecords = nil; + // get the findOptions values + BOOL multiple = NO; // default is false + NSString* filter = nil; + NSArray* desiredFields = nil; + if (![findOptions isKindOfClass:[NSNull class]]) { + id value = nil; + filter = (NSString*)[findOptions objectForKey:@"filter"]; + value = [findOptions objectForKey:@"multiple"]; + if ([value isKindOfClass:[NSNumber class]]) { + // multiple is a boolean that will come through as an NSNumber + multiple = [(NSNumber*)value boolValue]; + // NSLog(@"multiple is: %d", multiple); + } + desiredFields = [findOptions objectForKey:@"desiredFields"]; + // return all fields if desired fields are not explicitly defined + if (desiredFields == nil || desiredFields.count == 0) { + desiredFields = [NSArray arrayWithObjects:@"*", nil]; + } + } + + NSDictionary* searchFields = [[CDVContact class] calcReturnFields:fields]; + NSDictionary* returnFields = [[CDVContact class] calcReturnFields:desiredFields]; + + NSMutableArray* matches = nil; + if (!filter || [filter isEqualToString:@""]) { + // get all records + foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook); + if (foundRecords && ([foundRecords count] > 0)) { + // create Contacts and put into matches array + // doesn't make sense to ask for all records when multiple == NO but better check + int xferCount = multiple == YES ? (int)[foundRecords count] : 1; + matches = [NSMutableArray arrayWithCapacity:xferCount]; + + for (int k = 0; k < xferCount; k++) { + CDVContact* xferContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:k]]; + [matches addObject:xferContact]; + xferContact = nil; + } + } + } else { + foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook); + matches = [NSMutableArray arrayWithCapacity:1]; + BOOL bFound = NO; + int testCount = (int)[foundRecords count]; + + for (int j = 0; j < testCount; j++) { + CDVContact* testContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:j]]; + if (testContact) { + bFound = [testContact foundValue:filter inFields:searchFields]; + if (bFound) { + [matches addObject:testContact]; + } + testContact = nil; + } + } + } + NSMutableArray* returnContacts = [NSMutableArray arrayWithCapacity:1]; + + if ((matches != nil) && ([matches count] > 0)) { + // convert to JS Contacts format and return in callback + // - returnFields determines what properties to return + @autoreleasepool { + int count = multiple == YES ? (int)[matches count] : 1; + + for (int i = 0; i < count; i++) { + CDVContact* newContact = [matches objectAtIndex:i]; + NSDictionary* aContact = [newContact toDictionary:returnFields]; + [returnContacts addObject:aContact]; + } + } + } + // return found contacts (array is empty if no contacts found) + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:returnContacts]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + // NSLog(@"findCallback string: %@", jsString); + + if (addrBook) { + CFRelease(addrBook); + } + }]; + }]; // end of workQueue block + + return; +} + +- (void)save:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSDictionary* contactDict = [command argumentAtIndex:0]; + + [self.commandDelegate runInBackground:^{ + CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init]; + CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles + + [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errorCode) { + CDVPluginResult* result = nil; + if (addrBook == NULL) { + // permission was denied or other error - return error + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode ? (int)errorCode.errorCode:UNKNOWN_ERROR]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + return; + } + + bool bIsError = FALSE, bSuccess = FALSE; + BOOL bUpdate = NO; + CDVContactError errCode = UNKNOWN_ERROR; + CFErrorRef error; + NSNumber* cId = [contactDict valueForKey:kW3ContactId]; + CDVContact* aContact = nil; + ABRecordRef rec = nil; + if (cId && ![cId isKindOfClass:[NSNull class]]) { + rec = ABAddressBookGetPersonWithRecordID(addrBook, [cId intValue]); + if (rec) { + aContact = [[CDVContact alloc] initFromABRecord:rec]; + bUpdate = YES; + } + } + if (!aContact) { + aContact = [[CDVContact alloc] init]; + } + + bSuccess = [aContact setFromContactDict:contactDict asUpdate:bUpdate]; + if (bSuccess) { + if (!bUpdate) { + bSuccess = ABAddressBookAddRecord(addrBook, [aContact record], &error); + } + if (bSuccess) { + bSuccess = ABAddressBookSave(addrBook, &error); + } + if (!bSuccess) { // need to provide error codes + bIsError = TRUE; + errCode = IO_ERROR; + } else { + // give original dictionary back? If generate dictionary from saved contact, have no returnFields specified + // so would give back all fields (which W3C spec. indicates is not desired) + // for now (while testing) give back saved, full contact + NSDictionary* newContact = [aContact toDictionary:[CDVContact defaultFields]]; + // NSString* contactStr = [newContact JSONRepresentation]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newContact]; + } + } else { + bIsError = TRUE; + errCode = IO_ERROR; + } + CFRelease(addrBook); + + if (bIsError) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:(int)errCode]; + } + + if (result) { + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + }]; + }]; // end of queue +} + +- (void)remove:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSNumber* cId = [command argumentAtIndex:0]; + + CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init]; + CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles + + [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errorCode) { + CDVPluginResult* result = nil; + if (addrBook == NULL) { + // permission was denied or other error - return error + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode ? (int)errorCode.errorCode:UNKNOWN_ERROR]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + return; + } + + bool bIsError = FALSE, bSuccess = FALSE; + CDVContactError errCode = UNKNOWN_ERROR; + CFErrorRef error; + ABRecordRef rec = nil; + if (cId && ![cId isKindOfClass:[NSNull class]] && ([cId intValue] != kABRecordInvalidID)) { + rec = ABAddressBookGetPersonWithRecordID(addrBook, [cId intValue]); + if (rec) { + bSuccess = ABAddressBookRemoveRecord(addrBook, rec, &error); + if (!bSuccess) { + bIsError = TRUE; + errCode = IO_ERROR; + } else { + bSuccess = ABAddressBookSave(addrBook, &error); + if (!bSuccess) { + bIsError = TRUE; + errCode = IO_ERROR; + } else { + // set id to null + // [contactDict setObject:[NSNull null] forKey:kW3ContactId]; + // result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary: contactDict]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + // NSString* contactStr = [contactDict JSONRepresentation]; + } + } + } else { + // no record found return error + bIsError = TRUE; + errCode = UNKNOWN_ERROR; + } + } else { + // invalid contact id provided + bIsError = TRUE; + errCode = INVALID_ARGUMENT_ERROR; + } + + if (addrBook) { + CFRelease(addrBook); + } + if (bIsError) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:(int)errCode]; + } + if (result) { + [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + }]; + return; +} + +@end + +/* ABPersonViewController does not have any UI to dismiss. Adding navigationItems to it does not work properly + * The navigationItems are lost when the app goes into the background. The solution was to create an empty + * NavController in front of the ABPersonViewController. This will cause the ABPersonViewController to have a back button. By subclassing the ABPersonViewController, we can override viewDidDisappear and take down the entire NavigationController. + */ +@implementation CDVDisplayContactViewController +@synthesize contactsPlugin; + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + + [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; +} + +@end +@implementation CDVAddressBookAccessError + +@synthesize errorCode; + +- (CDVAddressBookAccessError*)initWithCode:(CDVContactError)code +{ + self = [super init]; + if (self) { + self.errorCode = code; + } + return self; +} + +@end + +@implementation CDVAddressBookHelper + +/** + * NOTE: workerBlock is responsible for releasing the addressBook that is passed to it + */ +- (void)createAddressBook:(CDVAddressBookWorkerBlock)workerBlock +{ + // TODO: this probably should be reworked - seems like the workerBlock can just create and release its own AddressBook, + // and also this important warning from (http://developer.apple.com/library/ios/#documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Chapters/BasicObjects.html): + // "Important: Instances of ABAddressBookRef cannot be used by multiple threads. Each thread must make its own instance." + ABAddressBookRef addressBook; + + CFErrorRef error = nil; + // CFIndex status = ABAddressBookGetAuthorizationStatus(); + addressBook = ABAddressBookCreateWithOptions(NULL, &error); + // NSLog(@"addressBook access: %lu", status); + ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { + // callback can occur in background, address book must be accessed on thread it was created on + dispatch_sync(dispatch_get_main_queue(), ^{ + if (error) { + workerBlock(NULL, [[CDVAddressBookAccessError alloc] initWithCode:UNKNOWN_ERROR]); + } else if (!granted) { + workerBlock(NULL, [[CDVAddressBookAccessError alloc] initWithCode:PERMISSION_DENIED_ERROR]); + } else { + // access granted + workerBlock(addressBook, [[CDVAddressBookAccessError alloc] initWithCode:UNKNOWN_ERROR]); + } + }); + }); +} + +@end diff --git a/plugins/cordova-plugin-contacts/src/ubuntu/contacts.cpp b/plugins/cordova-plugin-contacts/src/ubuntu/contacts.cpp new file mode 100644 index 0000000..373a276 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/ubuntu/contacts.cpp @@ -0,0 +1,576 @@ +/* + * + * 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. + * +*/ + +#include "contacts.h" + +#if defined QTCONTACTS_USE_NAMESPACE +QTCONTACTS_USE_NAMESPACE +#endif + +Contacts::Contacts(Cordova *cordova): CPlugin(cordova) { + m_fieldNamePairs.clear(); + + m_fieldNamePairs["displayName"] = QContactDetail::TypeDisplayLabel; + m_fieldNamePairs["name"] = QContactDetail::TypeName; + m_fieldNamePairs["nickname"] = QContactDetail::TypeNickname; + m_fieldNamePairs["phoneNumbers"] = QContactDetail::TypePhoneNumber; + m_fieldNamePairs["emails"] = QContactDetail::TypeEmailAddress; + m_fieldNamePairs["addresses"] = QContactDetail::TypeAddress; + m_fieldNamePairs["ims"] = QContactDetail::TypeOnlineAccount; + m_fieldNamePairs["organizations"] = QContactDetail::TypeOrganization; + m_fieldNamePairs["birthday"] = QContactDetail::TypeBirthday; + m_fieldNamePairs["note"] = QContactDetail::TypeNote; + m_fieldNamePairs["photos"] = QContactDetail::TypeAvatar; + m_fieldNamePairs["urls"] = QContactDetail::TypeUrl; + + m_notSupportedFields.clear(); + m_notSupportedFields << "categories"; + m_manager.clear(); + m_manager = QSharedPointer(new QContactManager()); +} + +void Contacts::save(int scId, int ecId, const QVariantMap ¶ms) { + QContact result; + QList detailsToDelete; + + if (params.find("id") != params.end()) { + QString id = params.find("id")->toString(); + if (!id.isEmpty()) { + result = m_manager->contact(QContactId::fromString(id)); + result.clearDetails(); + } + } + + foreach (QString field, params.keys()) { + QContactDetail::DetailType qtDefinition = cordovaFieldNameToQtDefinition(field); + if (qtDefinition == QContactDetail::TypeUndefined) + continue; + + if (field == "nickname") { + QContactNickname *detail = new QContactNickname; + detail->setNickname(params[field].toString()); + detailsToDelete << detail; + result.saveDetail(detail); + } else if (field == "note") { + QContactNote *detail = new QContactNote; + detail->setNote(params[field].toString()); + detailsToDelete << detail; + result.saveDetail(detail); + } else if (field == "phoneNumbers") { + if (params[field].type() != QVariant::List) + continue; + QVariantList phonesList = params[field].toList(); + foreach (const QVariant &phoneDesc, phonesList) { + if (phoneDesc.type() != QVariant::Map) + continue; + QContactPhoneNumber *detail = new QContactPhoneNumber; + detail->setNumber(phoneDesc.toMap()["value"].toString()); + if (!phoneDesc.toMap()["type"].toString().isEmpty() && + phoneDesc.toMap()["type"].toString() != "phone") + detail->setSubTypes(QList() << + subTypePhoneFromString(phoneDesc.toMap()["type"].toString())); + detailsToDelete << detail; + result.saveDetail(detail); + } + } else if (field == "emails") { + if (params[field].type() != QVariant::List) + continue; + QVariantList emailsList = params[field].toList(); + foreach (const QVariant &emailDesc, emailsList) { + if (emailDesc.type() != QVariant::Map) + continue; + if (emailDesc.toMap()["value"].toString().isEmpty()) + continue; + QContactEmailAddress *detail = new QContactEmailAddress; + detail->setEmailAddress(emailDesc.toMap()["value"].toString()); + detailsToDelete << detail; + result.saveDetail(detail); + } + } else if (field == "ims") { + if (params[field].type() != QVariant::List) + continue; + QVariantList imsList = params[field].toList(); + foreach (const QVariant &imDesc, imsList) { + if (imDesc.type() != QVariant::Map) + continue; + QContactOnlineAccount *detail = new QContactOnlineAccount; + detail->setAccountUri(imDesc.toMap()["value"].toString()); + if (!imDesc.toMap()["type"].toString().isEmpty()) + detail->setSubTypes(QList() << + subTypeOnlineAccountFromString(imDesc.toMap()["type"].toString())); + detailsToDelete << detail; + result.saveDetail(detail); + } + } else if (field == "photos") { + if (params[field].type() != QVariant::List) + continue; + QVariantList photosList = params[field].toList(); + foreach (const QVariant &photoDesc, photosList) { + if (photoDesc.type() != QVariant::Map) + continue; + //TODO: we need to decide should we support base64 images or not + if (photoDesc.toMap()["type"].toString() != "url") + continue; + QContactAvatar *detail = new QContactAvatar; + detail->setImageUrl(QUrl(photoDesc.toMap()["value"].toString())); + detailsToDelete << detail; + result.saveDetail(detail); + } + } else if (field == "urls") { + if (params[field].type() != QVariant::List) + continue; + QVariantList urlsList = params[field].toList(); + foreach (const QVariant &urlDesc, urlsList) { + if (urlDesc.type() != QVariant::Map) + continue; + QContactUrl *detail = new QContactUrl; + detail->setUrl(urlDesc.toMap()["value"].toString()); + if (!urlDesc.toMap()["type"].toString().isEmpty()) + detail->setSubType((QContactUrl::SubType) subTypeUrlFromString(urlDesc.toMap()["type"].toString())); + detailsToDelete << detail; + result.saveDetail(detail); + } + } else if (field == "birthday") { + QDateTime birthday; + birthday.setTime_t(params[field].toLongLong() / 1000); + + QContactBirthday *detail = new QContactBirthday; + detail->setDateTime(birthday); + detailsToDelete << detail; + result.saveDetail(detail); + } else if (field == "organizations") { + + if (params[field].type() != QVariant::List) + continue; + QVariantList organizationsList = params[field].toList(); + foreach (const QVariant &organizationDesc, organizationsList) { + if (organizationDesc.type() != QVariant::Map) + continue; + QContactOrganization *detail = new QContactOrganization; + detail->setName(organizationDesc.toMap()["name"].toString()); + detail->setDepartment(QStringList() << organizationDesc.toMap()["department"].toString()); + detail->setRole(organizationDesc.toMap()["title"].toString()); + detailsToDelete << detail; + result.saveDetail(detail); + } + + } else if (field == "name") { + QContactName *detail = new QContactName; + QVariantMap nameMap = params[field].toMap(); + detail->setLastName(nameMap["familyName"].toString()); + detail->setFirstName(nameMap["givenName"].toString()); + detail->setMiddleName(nameMap["middleName"].toString()); + detail->setPrefix(nameMap["honorificPrefix"].toString()); + detail->setSuffix(nameMap["honorificSuffix"].toString()); + detailsToDelete << detail; + result.saveDetail(detail); + } + + } + if (!m_manager->saveContact(&result)) { + switch (m_manager->error()) { + case QContactManager::DoesNotExistError: + case QContactManager::AlreadyExistsError: + case QContactManager::InvalidDetailError: + case QContactManager::InvalidRelationshipError: + case QContactManager::BadArgumentError: + case QContactManager::InvalidContactTypeError: + callback(ecId, "ContactError.INVALID_ARGUMENT_ERROR"); + break; + case QContactManager::DetailAccessError: + case QContactManager::PermissionsError: + callback(ecId, "ContactError.PERMISSION_DENIED_ERROR"); + break; + case QContactManager::NotSupportedError: + callback(ecId, "ContactError.NOT_SUPPORTED_ERROR"); + break; + case QContactManager::TimeoutError: + callback(ecId, "ContactError.TIMEOUT_ERROR"); + break; + case QContactManager::UnspecifiedError: + case QContactManager::LockedError: + case QContactManager::OutOfMemoryError: + case QContactManager::VersionMismatchError: + case QContactManager::LimitReachedError: + case QContactManager::NoError: + default: + callback(ecId, "ContactError.UNKNOWN_ERROR"); + break; + } + } else { + callback(scId, jsonedContact(result)); + } + qDeleteAll(detailsToDelete); +} + +void Contacts::remove(int scId, int ecId, const QString &localId) { + QContactId id = QContactId::fromString(localId); + + if (!m_manager->removeContact(id)) { + switch (m_manager->error()) { + case QContactManager::AlreadyExistsError: + case QContactManager::InvalidDetailError: + case QContactManager::InvalidRelationshipError: + case QContactManager::BadArgumentError: + case QContactManager::InvalidContactTypeError: + callback(ecId, "ContactError.INVALID_ARGUMENT_ERROR"); + break; + case QContactManager::DetailAccessError: + case QContactManager::PermissionsError: + callback(ecId, "ContactError.PERMISSION_DENIED_ERROR"); + break; + case QContactManager::NotSupportedError: + callback(ecId, "ContactError.NOT_SUPPORTED_ERROR"); + break; + case QContactManager::TimeoutError: + callback(ecId, "ContactError.TIMEOUT_ERROR"); + break; + case QContactManager::UnspecifiedError: + case QContactManager::LockedError: + case QContactManager::OutOfMemoryError: + case QContactManager::VersionMismatchError: + case QContactManager::LimitReachedError: + case QContactManager::NoError: + case QContactManager::DoesNotExistError: + default: + callback(ecId, "ContactError.UNKNOWN_ERROR"); + break; + } + + } else { + cb(scId); + } +} + +void Contacts::search(int scId, int ecId, const QStringList &fields, const QVariantMap ¶ms) { + QString filter; + bool multiple = true; + + if (params.find("filter") != params.end()) { + filter = params["filter"].toString(); + } + if (params.find("multiple") != params.end()) { + multiple = params["multiple"].toBool(); + } + + findContacts(scId, ecId, fields, filter, multiple); +} + +void Contacts::findContacts(int scId, int ecId, const QStringList &fields, const QString &filter, bool multiple) { + if (fields.length() <= 0){ + callback(ecId, "new ContactError(ContactError.INVALID_ARGUMENT_ERROR)"); + } + + QContactUnionFilter unionFilter; + + QMap > fieldNames; + fieldNames[QContactDetail::TypeDisplayLabel] << QContactDisplayLabel::FieldLabel; + fieldNames[QContactDetail::TypeName] << QContactName::FieldFirstName << QContactName::FieldLastName << QContactName::FieldMiddleName << QContactName::FieldPrefix << QContactName::FieldSuffix; + fieldNames[QContactDetail::TypeNickname] << QContactNickname::FieldNickname; + fieldNames[QContactDetail::TypePhoneNumber] << QContactPhoneNumber::FieldNumber; + fieldNames[QContactDetail::TypeEmailAddress] << QContactEmailAddress::FieldEmailAddress; + fieldNames[QContactDetail::TypeAddress] << QContactAddress::FieldCountry << QContactAddress::FieldLocality << QContactAddress::FieldPostcode << QContactAddress::FieldPostOfficeBox << QContactAddress::FieldRegion << QContactAddress::FieldStreet; + fieldNames[QContactDetail::TypeOnlineAccount] << QContactOnlineAccount::FieldAccountUri; + fieldNames[QContactDetail::TypeOrganization] << QContactOrganization::FieldAssistantName << QContactOrganization::FieldDepartment << QContactOrganization::FieldLocation << QContactOrganization::FieldName << QContactOrganization::FieldRole << QContactOrganization::FieldTitle; + fieldNames[QContactDetail::TypeBirthday] << QContactBirthday::FieldBirthday; + fieldNames[QContactDetail::TypeNote] << QContactNote::FieldNote; + fieldNames[QContactDetail::TypeUrl] << QContactUrl::FieldUrl; + + foreach (const QContactDetail::DetailType &defName, fieldNames.keys()) { + foreach(int fieldName, fieldNames[defName]) { + QContactDetailFilter subFilter; + subFilter.setDetailType(defName, fieldName); + subFilter.setValue(filter); + subFilter.setMatchFlags(QContactFilter::MatchContains); + unionFilter.append(subFilter); + } + } + + QList contacts = m_manager->contacts(unionFilter); + if (contacts.empty()) { + callback(scId, "[]"); + } else { + QStringList stringifiedContacts; + foreach (const QContact &contact, contacts) { + stringifiedContacts << jsonedContact(contact, fields); + + if (!multiple) + break; + } + callback(scId, QString("[%1]").arg(stringifiedContacts.join(", "))); + } +} + +QContactDetail::DetailType Contacts::cordovaFieldNameToQtDefinition(const QString &cordovaFieldName) const { + if (m_fieldNamePairs.contains(cordovaFieldName)) + return m_fieldNamePairs[cordovaFieldName]; + + return QContactDetail::TypeUndefined; +} + +int Contacts::subTypePhoneFromString(const QString &cordovaSubType) const +{ + QString preparedSubType = cordovaSubType.toLower(); + if (preparedSubType == "mobile") + return QContactPhoneNumber::SubTypeMobile; + else if (preparedSubType == "fax") + return QContactPhoneNumber::SubTypeFax; + else if (preparedSubType == "pager") + return QContactPhoneNumber::SubTypePager; + else if (preparedSubType == "voice") + return QContactPhoneNumber::SubTypeVoice; + else if (preparedSubType == "modem") + return QContactPhoneNumber::SubTypeModem; + else if (preparedSubType == "video") + return QContactPhoneNumber::SubTypeVideo; + else if (preparedSubType == "car") + return QContactPhoneNumber::SubTypeCar; + else if (preparedSubType == "assistant") + return QContactPhoneNumber::SubTypeAssistant; + return QContactPhoneNumber::SubTypeLandline; +} + +int Contacts::subTypeOnlineAccountFromString(const QString &cordovaSubType) const { + QString preparedSubType = cordovaSubType.toLower(); + if (preparedSubType == "aim") + return QContactOnlineAccount::ProtocolAim; + else if (preparedSubType == "icq") + return QContactOnlineAccount::ProtocolIcq; + else if (preparedSubType == "irc") + return QContactOnlineAccount::ProtocolIrc; + else if (preparedSubType == "jabber") + return QContactOnlineAccount::ProtocolJabber; + else if (preparedSubType == "msn") + return QContactOnlineAccount::ProtocolMsn; + else if (preparedSubType == "qq") + return QContactOnlineAccount::ProtocolQq; + else if (preparedSubType == "skype") + return QContactOnlineAccount::ProtocolSkype; + else if (preparedSubType == "yahoo") + return QContactOnlineAccount::ProtocolYahoo; + return QContactOnlineAccount::ProtocolUnknown; +} + +int Contacts::subTypeUrlFromString(const QString &cordovaSubType) const { + QString preparedSubType = cordovaSubType.toLower(); + if (preparedSubType == "blog") + return QContactUrl::SubTypeBlog; + else if (preparedSubType == "favourite") + return QContactUrl::SubTypeFavourite; + return QContactUrl::SubTypeHomePage; +} + +QString Contacts::subTypePhoneToString(int qtSubType) const { + if (qtSubType == QContactPhoneNumber::SubTypeMobile) + return "mobile"; + else if (qtSubType == QContactPhoneNumber::SubTypeFax) + return "fax"; + else if (qtSubType == QContactPhoneNumber::SubTypePager) + return "pager"; + else if (qtSubType == QContactPhoneNumber::SubTypeVoice) + return "voice"; + else if (qtSubType == QContactPhoneNumber::SubTypeModem) + return "modem"; + else if (qtSubType == QContactPhoneNumber::SubTypeVideo) + return "video"; + else if (qtSubType == QContactPhoneNumber::SubTypeCar) + return "car"; + else if (qtSubType == QContactPhoneNumber::SubTypeAssistant) + return "assistant"; + return "home"; +} + +QString Contacts::subTypeOnlineAccountToString(int qtSubType) const { + if (qtSubType == QContactOnlineAccount::ProtocolAim) + return "aim"; + else if (qtSubType == QContactOnlineAccount::ProtocolIcq) + return "icq"; + else if (qtSubType == QContactOnlineAccount::ProtocolIrc) + return "irc"; + else if (qtSubType == QContactOnlineAccount::ProtocolJabber) + return "jabber"; + else if (qtSubType == QContactOnlineAccount::ProtocolMsn) + return "msn"; + else if (qtSubType == QContactOnlineAccount::ProtocolQq) + return "qq"; + else if (qtSubType == QContactOnlineAccount::ProtocolSkype) + return "skype"; + else if (qtSubType == QContactOnlineAccount::ProtocolYahoo) + return "yahoo"; + return "unknown"; +} + +QString Contacts::subTypeUrlToString(int qtSubType) const { + if (qtSubType == QContactUrl::SubTypeBlog) + return "blog"; + else if (qtSubType == QContactUrl::SubTypeFavourite) + return "favourite"; + return "homepage"; +} + +QString Contacts::jsonedContact(const QContact &contact, const QStringList &fields) const { + QStringList resultingFields = fields; + if (resultingFields.empty()) + resultingFields.append(m_fieldNamePairs.keys()); + if (!resultingFields.contains("id")) + resultingFields << "id"; + QStringList fieldValuesList; + foreach (const QString &field, resultingFields) { + QContactDetail::DetailType qtDefinitionName = cordovaFieldNameToQtDefinition(field); + if (field == "id") { + fieldValuesList << QString("%1: \"%2\"") + .arg(field) + .arg(contact.id().toString()); + } else if (field == "displayName") { + QContactDisplayLabel detail = contact.detail(qtDefinitionName); + fieldValuesList << QString("%1: \"%2\"") + .arg(field) + .arg(detail.label()); + } else if (field == "nickname") { + QContactNickname detail = contact.detail(qtDefinitionName); + fieldValuesList << QString("%1: \"%2\"") + .arg(field) + .arg(detail.nickname()); + } else if (field == "note") { + QContactNote detail = contact.detail(qtDefinitionName); + fieldValuesList << QString("%1: \"%2\"") + .arg(field) + .arg(detail.note()); + } else if (field == "phoneNumbers") { + QStringList fieldValues; + QList details = contact.details(qtDefinitionName); + foreach (const QContactDetail &detail, details) { + QContactPhoneNumber castedDetail = detail; + QStringList subTypes; + foreach (int subType, castedDetail.subTypes()) + subTypes << subTypePhoneToString(subType); + + if (subTypes.isEmpty()) + subTypes << "phone"; + foreach(const QString &subType, subTypes) { + fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}") + .arg(subType) + .arg(castedDetail.number()) + .arg("false"); + } + } + fieldValuesList << QString("%1: [%2]") + .arg(field) + .arg(fieldValues.join(", ")); + } else if (field == "emails") { + QStringList fieldValues; + QList details = contact.details(qtDefinitionName); + foreach (const QContactDetail &detail, details) { + QContactEmailAddress castedDetail = detail; + fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}") + .arg("email") + .arg(castedDetail.emailAddress()) + .arg("false"); + } + fieldValuesList << QString("%1: [%2]") + .arg(field) + .arg(fieldValues.join(", ")); + } else if (field == "ims") { + QStringList fieldValues; + QList details = contact.details(qtDefinitionName); + foreach (const QContactDetail &detail, details) { + QContactOnlineAccount castedDetail = detail; + QStringList subTypes; + foreach (int subType, castedDetail.subTypes()) + subTypes << subTypeOnlineAccountToString(subType); + + if (subTypes.isEmpty()) + subTypes << "IM"; + foreach(const QString &subType, subTypes) { + fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}") + .arg(subType) + .arg(castedDetail.accountUri()) + .arg("false"); + } + } + fieldValuesList << QString("%1: [%2]") + .arg(field) + .arg(fieldValues.join(", ")); + } else if (field == "photos") { + QStringList fieldValues; + QList details = contact.details(qtDefinitionName); + foreach (const QContactDetail &detail, details) { + QContactAvatar castedDetail = detail; + fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}") + .arg("url") + .arg(castedDetail.imageUrl().toString()) + .arg("false"); + } + fieldValuesList << QString("%1: [%2]") + .arg(field) + .arg(fieldValues.join(", ")); + } else if (field == "urls") { + QStringList fieldValues; + QList details = contact.details(qtDefinitionName); + + foreach (const QContactDetail &detail, details) { + QContactUrl castedDetail = detail; + QString subType = subTypeUrlToString(castedDetail.subType()); + + fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}") + .arg(subType) + .arg(castedDetail.url()) + .arg("false"); + } + fieldValuesList << QString("%1: [%2]") + .arg(field) + .arg(fieldValues.join(", ")); + } else if (field == "birthday") { + QContactBirthday detail = contact.detail(qtDefinitionName); + fieldValuesList << QString("%1: %2") + .arg(field) + .arg(detail.dateTime().toMSecsSinceEpoch()); + } else if (field == "organizations") { + QStringList fieldValues; + QList details = contact.details(qtDefinitionName); + foreach (const QContactDetail &detail, details) { + QContactOrganization castedDetail = detail; + fieldValues << QString("{type: \"%1\", name: \"%2\", department: \"%3\", title: \"%4\", pref: %5}") + .arg("organization") + .arg(castedDetail.name()) + .arg(castedDetail.department().join(" ")) + .arg(castedDetail.role()) + .arg("false"); + } + fieldValuesList << QString("%1: [%2]") + .arg(field) + .arg(fieldValues.join(", ")); + } else if (field == "name") { + QContactName detail = contact.detail(qtDefinitionName); + fieldValuesList << QString("%1: {familyName: \"%2\", givenName: \"%3\", middleName: \"%4\", honorificPrefix: \"%5\", honorificSuffix: \"%6\"}") + .arg(field) + .arg(detail.lastName()) + .arg(detail.firstName()) + .arg(detail.middleName()) + .arg(detail.prefix()) + .arg(detail.suffix()); + } + + + } + + return QString("{%1}").arg(fieldValuesList.join(", ")); +} diff --git a/plugins/cordova-plugin-contacts/src/ubuntu/contacts.h b/plugins/cordova-plugin-contacts/src/ubuntu/contacts.h new file mode 100644 index 0000000..4c1343b --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/ubuntu/contacts.h @@ -0,0 +1,70 @@ +/* + * + * 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. + * +*/ + +#ifndef CONTACTS_H_SSSSSSS +#define CONTACTS_H_SSSSSSS + +#include + +#include +#include + +QTCONTACTS_USE_NAMESPACE + +class Contacts : public CPlugin { + Q_OBJECT +public: + explicit Contacts(Cordova *cordova); + + virtual const QString fullName() override { + return Contacts::fullID(); + } + + virtual const QString shortName() override { + return "Contacts"; + } + + static const QString fullID() { + return "Contacts"; + } + +public slots: + void save(int scId, int ecId, const QVariantMap ¶ms); + void remove(int scId, int ecId, const QString &localId); + void search(int scId, int ecId, const QStringList &fields, const QVariantMap ¶ms); + +private: + void findContacts(int scId, int ecId, const QStringList &fields, const QString &filter, bool multiple); + QContactDetail::DetailType cordovaFieldNameToQtDefinition(const QString &cordovaFieldName) const; + int subTypePhoneFromString(const QString &cordovaSubType) const; + int subTypeOnlineAccountFromString(const QString &cordovaSubType) const; + int subTypeUrlFromString(const QString &cordovaSubType) const; + QString subTypePhoneToString(int qtSubType) const; + QString subTypeOnlineAccountToString(int qtSubType) const; + QString subTypeUrlToString(int qtSubType) const; + QString jsonedContact(const QContact &contact, const QStringList &fields = QStringList()) const; + + QHash m_fieldNamePairs; + QSet m_notSupportedFields; + QSharedPointer m_manager; +}; + +#endif diff --git a/plugins/cordova-plugin-contacts/src/windows/ContactProxy.js b/plugins/cordova-plugin-contacts/src/windows/ContactProxy.js new file mode 100644 index 0000000..61f1d7a --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/windows/ContactProxy.js @@ -0,0 +1,423 @@ +/* + * + * 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 ContactField = require('./ContactField'), + ContactAddress = require('./ContactAddress'), + ContactOrganization = require('./ContactOrganization'), + ContactName = require('./ContactName'), + ContactError = require('./ContactError'), + Contact = require('./Contact'); + + +function convertToContact(windowsContact) { + var contact = new Contact(); + + // displayName & nickname + contact.displayName = windowsContact.displayName || windowsContact.name; + contact.nickname = windowsContact.name; + contact.id = windowsContact.id; + + // name + // Additional fields like lastName, middleName etc. available on windows8.1/wp8.1 only + contact.name = new ContactName( + windowsContact.displayName || windowsContact.name, + windowsContact.lastName, + windowsContact.firstName || windowsContact.name, + windowsContact.middleName, + windowsContact.honorificNamePrefix || windowsContact.honorificPrefix, + windowsContact.honorificNameSuffix || windowsContact.honorificSuffix); + + // phoneNumbers + contact.phoneNumbers = []; + var phoneSource = windowsContact.phoneNumbers || windowsContact.phones; + for (var i = 0; i < phoneSource.size; i++) { + var rawPhone = phoneSource[i]; + var phone = new ContactField(rawPhone.category || rawPhone.kind, rawPhone.value || rawPhone.number); + contact.phoneNumbers.push(phone); + } + + // emails + contact.emails = []; + var emailSource = windowsContact.emails; + for (var i = 0; i < emailSource.size; i++) { + var rawEmail = emailSource[i]; + var email = new ContactField(rawEmail.category || rawEmail.kind, rawEmail.value || rawEmail.address); + contact.emails.push(email); + } + + // addressres + contact.addresses = []; + var addressSource = windowsContact.locations || windowsContact.addresses; + for (var i = 0; i < addressSource.size; i++) { + var rawAddress = addressSource[i]; + var address = new ContactAddress( + null, + rawAddress.category || rawAddress.kind, + rawAddress.unstructuredAddress, + rawAddress.street || rawAddress.streetAddress, + rawAddress.city || rawAddress.locality, + rawAddress.region, + rawAddress.postalCode, + rawAddress.country); + contact.addresses.push(address); + } + + // ims + contact.ims = []; + var imSource = windowsContact.instantMessages || windowsContact.connectedServiceAccounts; + for (var i = 0; i < imSource.size; i++) { + var rawIm = imSource[i]; + var im = new ContactField(rawIm.category || rawIm.serviceName, rawIm.userName || rawIm.id); + contact.ims.push(im); + } + + // jobInfo field available on Windows 8.1/WP8.1 only + var jobInfo = windowsContact.jobInfo; + if (jobInfo) { + contact.organizations = []; + for (var j = 0; j < jobInfo.size; j++) { + var rawJob = jobInfo[i]; + contact.organizations.push(new ContactOrganization(false, null, + rawJob.companyName, rawJob.department, rawJob.title)); + } + } + + // note field available on Windows 8.1/WP8.1 only + var contactNotes = windowsContact.notes; + if (contactNotes) { + contact.note = contactNotes; + } + + // returned is a file, a blob url can be made + var contactPhoto = windowsContact.thumbnail; + if (contactPhoto && contactPhoto.path) { + contact.photos = [new ContactField('url', URL.createObjectURL(contactPhoto) , false)]; + } + + return contact; +} + +// Win API Contacts namespace +var contactsNS = Windows.ApplicationModel.Contacts; + +function cdvContactToWindowsContact(contact) { + var result = new contactsNS.Contact(); + + // name first + if (contact.name) { + result.displayNameOverride = contact.name.formatted; + result.firstName = contact.name.givenName; + result.middleName = contact.name.middleName; + result.lastName = contact.name.familyName; + result.honorificNamePrefix = contact.name.honorificPrefix; + result.honorificNameSuffix = contact.name.honorificSuffix; + } + + result.nickname = contact.nickname; + + // phone numbers + if (contact.phoneNumbers) { + contact.phoneNumbers.forEach(function(contactPhone) { + var resultPhone = new contactsNS.ContactPhone(); + resultPhone.description = contactPhone.type; + resultPhone.number = contactPhone.value; + result.phones.push(resultPhone); + }); + } + + // emails + if (contact.emails) { + contact.emails.forEach(function(contactEmail) { + var resultEmail = new contactsNS.ContactEmail(); + resultEmail.description = contactEmail.type; + resultEmail.address = contactEmail.value; + result.emails.push(resultEmail); + }); + } + + // Addresses + if (contact.addresses) { + contact.addresses.forEach(function(contactAddress) { + var address = new contactsNS.ContactAddress(); + address.description = contactAddress.type; + address.streetAddress = contactAddress.streetAddress; + address.locality = contactAddress.locality; + address.region = contactAddress.region; + address.postalCode = contactAddress.postalCode; + address.country = contactAddress.country; + result.addresses.push(address); + }); + } + + // IMs + if (contact.ims) { + contact.ims.forEach(function(contactIM) { + var acct = new contactsNS.ContactConnectedServiceAccount(); + acct.serviceName = contactIM.type; + acct.id = contactIM.value; + result.connectedServiceAccounts.push(acct); + }); + } + + // JobInfo + if (contact.organizations) { + contact.organizations.forEach(function(contactOrg) { + var job = new contactsNS.ContactJobInfo(); + job.companyName = contactOrg.name; + job.department = contactOrg.department; + job.description = contactOrg.type; + job.title = contactOrg.title; + result.jobInfo.push(job); + }); + } + + result.notes = contact.note; + + if (contact.photos) { + var eligiblePhotos = contact.photos.filter(function(photo) { + return typeof photo.value !== 'undefined'; + }); + if (eligiblePhotos.length > 0) { + var supportedPhoto = eligiblePhotos[0]; + var path = supportedPhoto.value; + + try { + var streamRef; + if (/^([a-z][a-z0-9+\-.]*):\/\//i.test(path)) { + streamRef = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(new Windows.Foundation.Uri(path)); + } else { + streamRef = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(path); + } + result.thumbnail = streamRef; + } + catch (e) { + // incompatible reference to the photo + } + } + } + + return result; +} + +module.exports = { + + pickContact: function (win, fail, args) { + + // ContactPicker class works differently on Windows8/8.1 and Windows Phone 8.1 + // so we need to detect when we are running on phone + var runningOnPhone = navigator.userAgent.indexOf('Windows Phone') !== -1; + + var picker = new contactsNS.ContactPicker(); + if (runningOnPhone) { + // TODO: Windows Phone 8.1 requires this specification. This should be noted in quirks + // See http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactpicker.desiredfieldswithcontactfieldtype.aspx for details + // Multiple ContactFieldType items, appended to array causes `Request not suported` error. + picker.desiredFieldsWithContactFieldType.append(Windows.ApplicationModel.Contacts.ContactFieldType.phoneNumber); + } + + // pickContactAsync is available on Windows 8.1 or later, instead of + // pickSingleContactAsync, which is deprecated in Windows 8.1, + // so try to use newer method, if available. + // see http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactpicker.picksinglecontactasync.aspx + + var pickRequest = picker.pickContactAsync ? picker.pickContactAsync() : picker.pickSingleContactAsync(); + pickRequest.done(function (contact) { + // if contact was not picked + if (!contact) { + var cancelledError = new ContactError(ContactError.OPERATION_CANCELLED_ERROR); + cancelledError.message = "User did not pick a contact."; + fail(cancelledError); + return; + } + // If we are on desktop, just send em back + if (!runningOnPhone) { + win(convertToContact(contact)); + return; + } + // On WP8.1 fields set in resulted Contact object depends on desiredFieldsWithContactFieldType property of contact picker + // so we retrieve full contact by its' Id + contactsNS.ContactManager.requestStoreAsync().done(function (contactStore) { + contactStore.getContactAsync(contact.id).done(function(con) { + win(convertToContact(con)); + }, function() { + fail(new ContactError(ContactError.UNKNOWN_ERROR)); + }); + }, function () { + fail(new ContactError(ContactError.UNKNOWN_ERROR)); + }); + }); + }, + + save: function (win, fail, args) { + if (typeof contactsNS.ContactList === 'undefined') { + // Not supported yet since WinJS API do not provide methods to manage contactStore + // On Windows Phone 8.1 this can be implemented using native class library + // See Windows.Phone.PersonalInformation namespace + // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.phone.personalinformation.aspx + + //We don't need to create Error object here since it will be created at navigator.contacts.find() method + fail && fail(ContactError.NOT_SUPPORTED_ERROR); + return; + } + + var winContact = cdvContactToWindowsContact(args[0]); + + contactsNS.ContactManager.requestStoreAsync(contactsNS.ContactStoreAccessType.appContactsReadWrite).then(function(store) { + return store.findContactListsAsync().then(function(lists) { + if (lists.length > 0) { + return lists[0]; + } else { + return store.createContactListAsync(''); + } + }, function(error) { + return store.createContactListAsync(''); + }); + }).then(function(list) { + return list.saveContactAsync(winContact); + }).done(function(result) { + win(convertToContact(winContact)); + }, function(error) { + fail(error); + }); + }, + + remove: function(win, fail, args) { + if (typeof contactsNS.ContactList === 'undefined') { + // Not supported yet since WinJS API do not provide methods to manage contactStore + // On Windows Phone 8.1 this can be implemented using native class library + // See Windows.Phone.PersonalInformation namespace + // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.phone.personalinformation.aspx + + //We don't need to create Error object here since it will be created at navigator.contacts.find() method + fail && fail(ContactError.NOT_SUPPORTED_ERROR); + return; + } + + // This is a complicated scenario because in Win10, there is a notion of 'app contacts' vs 'global contacts'. + // search() returns all global contacts, which are "aggregate contacts", so the IDs of contacts that Cordova + // creates never match the IDs of the contacts returned from search(). + // In order to work around this, we need to: + // - Get two Stores: one that is read-write to the app-contacts list, one which is read-only for global contacts + // - Read the app-local store to see if a contact with the passed-in ID matches + // - Grab the global aggregate contact manager, then ask it for raw contacts (app-local ACM returns access denied) + // - Find my app-list of contacts + // - Enumerate the raw contacts and see if there is a raw contact whose parent list matches the app-list + // - If so, remove the raw contact from the app-list + // - If any of this fails, the operation fails + WinJS.Promise.join([contactsNS.ContactManager.requestStoreAsync(contactsNS.ContactStoreAccessType.appContactsReadWrite), + contactsNS.ContactManager.requestStoreAsync(contactsNS.ContactStoreAccessType.allContactsReadOnly)]).then(function(stores) { + var readOnlyStore = stores[1]; + var writableStore = stores[0]; + + var storeReader = writableStore.getContactReader(); + return storeReader.readBatchAsync().then(function(batch) { + if (batch.status !== contactsNS.ContactBatchStatus.success) { + // Couldn't read contacts store + throw new ContactError(ContactError.IO_ERROR); + } + + var candidates = batch.contacts.filter(function(testContact) { + return testContact.id === args[0]; + }); + + if (candidates.length === 0) { + // No matching contact from aggregate store + throw new ContactError(ContactError.IO_ERROR); + } + + return candidates[0]; + }).then(function(contactToDelete) { + return readOnlyStore.aggregateContactManager.findRawContactsAsync(contactToDelete); + }).then(function(rawContacts) { + return writableStore.findContactListsAsync().then(function(lists) { + var deleteList = null; + var deleteContact = null; + var matched = lists.some(function(list) { + for (var i = 0; i < rawContacts.length; i++) { + if (rawContacts[i].contactListId === list.id) { + deleteList = list; + deleteContact = rawContacts[i]; + return true; + } + } + return false; + }); + + if (!matched) { + throw new ContactError(ContactError.IO_ERROR); + } + + return deleteList.deleteContactAsync(deleteContact); + }); + }); + }).done(function() { + win(); + }, function(error) { + fail(error); + }); + }, + + search: function (win, fail, options) { + + // searchFields is not supported yet due to WP8.1 API limitations. + // findContactsAsync(String) method will attempt to match the name, email address, or phone number of a contact. + // see http://msdn.microsoft.com/en-us/library/windows/apps/dn624861.aspx for details + var searchFields = options[0], + searchOptions = options[1], + searchFilter = searchOptions.filter, + searchMultiple = searchOptions && searchOptions.multiple; + + // Check if necessary API is available. + // If not available, we are running on desktop, which doesn't support searching for contacts + if (!(contactsNS.ContactManager && contactsNS.ContactManager.requestStoreAsync)) { + fail(new ContactError(ContactError.NOT_SUPPORTED_ERROR)); + return; + } + + // Retrieve contact store instance + var contactStoreRequest = contactsNS.ContactManager.requestStoreAsync(); + + // When contact store retrieved + contactStoreRequest.done(function (contactStore) { + // determine, which function we use depending on whether searchOptions.filter specified or not + var contactsRequest = searchFilter ? contactStore.findContactsAsync(searchFilter) : contactStore.findContactsAsync(); + // request contacts and resolve either with success or error callback + contactsRequest.done(function (contacts) { + var result = []; + if (contacts.size !== 0) { + // Depending on searchOptions we should return all contacts found or only first + var outputContactsArray = searchMultiple ? contacts : [contacts[0]]; + outputContactsArray.forEach(function (contact) { + // Convert windows contacts to plugin's contact objects + result.push(convertToContact(contact)); + }); + } + win(result); + }, function() { + fail(new ContactError(ContactError.UNKNOWN_ERROR)); + }); + }, function() { + fail(new ContactError(ContactError.UNKNOWN_ERROR)); + }); + } +}; + +require("cordova/exec/proxy").add("Contacts", module.exports); diff --git a/plugins/cordova-plugin-contacts/src/windows8/ContactProxy.js b/plugins/cordova-plugin-contacts/src/windows8/ContactProxy.js new file mode 100644 index 0000000..c67e771 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/windows8/ContactProxy.js @@ -0,0 +1,121 @@ +/* + * + * 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 ContactField = require('./ContactField'), + ContactAddress = require('./ContactAddress'), + ContactName = require('./ContactName'), + Contact = require('./Contact'); + + +function convertToContact(windowsContact) { + var contact = new Contact(); + + // displayName & nickname + contact.displayName = windowsContact.name; + contact.nickname = windowsContact.name; + + // name + contact.name = new ContactName(windowsContact.name); + + // phoneNumbers + contact.phoneNumbers = []; + for (var i = 0; i < windowsContact.phoneNumbers.size; i++) { + var phone = new ContactField(windowsContact.phoneNumbers[i].category, windowsContact.phoneNumbers[i].value); + contact.phoneNumbers.push(phone); + } + + // emails + contact.emails = []; + for (var i = 0; i < windowsContact.emails.size; i++) { + var email = new ContactField(windowsContact.emails[i].category, windowsContact.emails[i].value); + contact.emails.push(email); + } + + // addressres + contact.addresses = []; + for (var i = 0; i < windowsContact.locations.size; i++) { + var address = new ContactAddress(null, windowsContact.locations[i].category, + windowsContact.locations[i].unstructuredAddress, windowsContact.locations[i].street, + null, windowsContact.locations[i].region, windowsContact.locations[i].postalCode, + windowsContact.locations[i].country); + contact.addresses.push(address); + } + + // ims + contact.ims = []; + for (var i = 0; i < windowsContact.instantMessages.size; i++) { + var im = new ContactField(windowsContact.instantMessages[i].category, windowsContact.instantMessages[i].userName); + contact.ims.push(im); + } + + return contact; +}; + +module.exports = { + pickContact: function(win, fail, args) { + + var picker = new Windows.ApplicationModel.Contacts.ContactPicker(); + picker.selectionMode = Windows.ApplicationModel.Contacts.ContactSelectionMode.contacts; // select entire contact + + // pickContactAsync is available on Windows 8.1 or later, instead of + // pickSingleContactAsync, which is deprecated after Windows 8, + // so try to use newer method, if available. + // see http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactpicker.picksinglecontactasync.aspx + if (picker.pickContactAsync) { + // TODO: 8.1 has better contact support via the 'Contact' object + } else { + + function success(con) { + // if contact was not picked + if (!con) { + fail && setTimeout(function() { + fail(new Error("User did not pick a contact.")); + }, 0); + return; + } + + // send em back + win(convertToContact(con)); + + } + + picker.pickSingleContactAsync().done(success, fail); + } + }, + + save:function(win,fail,args){ + console && console.error && console.error("Error : Windows 8 does not support creating/saving contacts"); + fail && setTimeout(function () { + fail(new Error("Contact create/save not supported on Windows 8")); + }, 0); + }, + + search: function(win, fail, args) { + console && console.error && console.error("Error : Windows 8 does not support searching contacts"); + fail && setTimeout(function() { + fail(new Error("Contact search not supported on Windows 8")); + }, 0); + } + + +} + +require("cordova/exec/proxy").add("Contacts", module.exports); diff --git a/plugins/cordova-plugin-contacts/src/wp/ContactPicker.xaml b/plugins/cordova-plugin-contacts/src/wp/ContactPicker.xaml new file mode 100644 index 0000000..cef3681 --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/wp/ContactPicker.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/src/wp/ContactPicker.xaml.cs b/plugins/cordova-plugin-contacts/src/wp/ContactPicker.xaml.cs new file mode 100644 index 0000000..ea3916e --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/wp/ContactPicker.xaml.cs @@ -0,0 +1,125 @@ +/* + 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. +*/ + +namespace WPCordovaClassLib.Cordova.Commands +{ + using System; + using System.Linq; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Navigation; + using Microsoft.Phone.Tasks; + using Microsoft.Phone.UserData; + using DeviceContacts = Microsoft.Phone.UserData.Contacts; + + /// + /// Custom implemented class for picking single contact + /// + public partial class ContactPicker + { + #region Fields + + /// + /// Result of ContactPicker call, represent contact returned. + /// + private ContactPickerTask.PickResult result; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public ContactPicker() + { + InitializeComponent(); + var cons = new DeviceContacts(); + cons.SearchCompleted += this.OnSearchCompleted; + cons.SearchAsync(string.Empty, FilterKind.None, string.Empty); + } + + #endregion + + #region Callbacks + + /// + /// Occurs when contact is selected or pick operation cancelled. + /// + public event EventHandler Completed; + + #endregion + + /// + /// The on navigated from. + /// + /// + /// The e. + /// + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + if (this.result == null) + { + this.Completed(this, new ContactPickerTask.PickResult(TaskResult.Cancel)); + } + + base.OnNavigatedFrom(e); + } + + /// + /// Called when contacts retrieval completed. + /// + /// The sender. + /// The instance containing the event data. + private void OnSearchCompleted(object sender, ContactsSearchEventArgs e) + { + if (e.Results.Count() != 0) + { + lstContacts.ItemsSource = e.Results.ToList(); + lstContacts.Visibility = Visibility.Visible; + NoContactsBlock.Visibility = Visibility.Collapsed; + } + else + { + lstContacts.Visibility = Visibility.Collapsed; + NoContactsBlock.Visibility = Visibility.Visible; + } + } + + /// + /// Called when any contact is selected. + /// + /// + /// The sender. + /// + /// + /// The e. + /// + private void ContactsListSelectionChanged(object sender, SelectionChangedEventArgs e) + { + this.result = new ContactPickerTask.PickResult(TaskResult.OK) { Contact = e.AddedItems[0] as Contact }; + this.Completed(this, this.result); + + if (NavigationService.CanGoBack) + { + NavigationService.GoBack(); + } + } + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/src/wp/ContactPickerTask.cs b/plugins/cordova-plugin-contacts/src/wp/ContactPickerTask.cs new file mode 100644 index 0000000..b11170f --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/wp/ContactPickerTask.cs @@ -0,0 +1,122 @@ +/* + 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. +*/ + +namespace WPCordovaClassLib.Cordova.Commands +{ + using System; + using System.Windows; + using Microsoft.Phone.Controls; + using Microsoft.Phone.Tasks; + using Microsoft.Phone.UserData; + + /// + /// Allows an application to pick contact. + /// Use this to allow users to pick contact from your application. + /// + public class ContactPickerTask + { + /// + /// Occurs when a Pick task is completed. + /// + public event EventHandler Completed; + + /// + /// Shows Contact pick application + /// + public void Show() + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + var root = Application.Current.RootVisual as PhoneApplicationFrame; + + string baseUrl = "/"; + + if (root != null) + { + root.Navigated += this.OnNavigate; + + // dummy parameter is used to always open a fresh version + root.Navigate( + new Uri( + baseUrl + "Plugins/cordova-plugin-contacts/ContactPicker.xaml?dummy=" + + Guid.NewGuid(), + UriKind.Relative)); + } + }); + } + + /// + /// Performs additional configuration of the picker application. + /// + /// The source of the event. + /// The instance containing the event data. + private void OnNavigate(object sender, System.Windows.Navigation.NavigationEventArgs e) + { + if (!(e.Content is ContactPicker)) + { + return; + } + + var phoneApplicationFrame = Application.Current.RootVisual as PhoneApplicationFrame; + if (phoneApplicationFrame != null) + { + phoneApplicationFrame.Navigated -= this.OnNavigate; + } + + ContactPicker contactPicker = (ContactPicker)e.Content; + + if (contactPicker != null) + { + contactPicker.Completed += this.Completed; + } + else if (this.Completed != null) + { + this.Completed(this, new PickResult(TaskResult.Cancel)); + } + } + + /// + /// Represents contact returned + /// + public class PickResult : TaskEventArgs + { + /// + /// Initializes a new instance of the PickResult class. + /// + public PickResult() + { + } + + /// + /// Initializes a new instance of the PickResult class + /// with the specified Microsoft.Phone.Tasks.TaskResult. + /// + /// Associated Microsoft.Phone.Tasks.TaskResult + public PickResult(TaskResult taskResult) + : base(taskResult) + { + } + + /// + /// Gets the contact. + /// + public Contact Contact { get; internal set; } + } + } +} diff --git a/plugins/cordova-plugin-contacts/src/wp/Contacts.cs b/plugins/cordova-plugin-contacts/src/wp/Contacts.cs new file mode 100644 index 0000000..89af66c --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/wp/Contacts.cs @@ -0,0 +1,592 @@ +/* + 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. +*/ + +using Microsoft.Phone.Tasks; +using Microsoft.Phone.UserData; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Runtime.Serialization; +using System.Windows; +using DeviceContacts = Microsoft.Phone.UserData.Contacts; + + +namespace WPCordovaClassLib.Cordova.Commands +{ + [DataContract] + public class SearchOptions + { + [DataMember] + public string filter { get; set; } + + [DataMember] + public bool multiple { get; set; } + + [DataMember] + public string[] desiredFields { get; set; } + } + + [DataContract] + public class ContactSearchParams + { + [DataMember] + public string[] fields { get; set; } + + [DataMember] + public SearchOptions options { get; set; } + } + + [DataContract] + public class JSONContactAddress + { + [DataMember] + public string formatted { get; set; } + + [DataMember] + public string type { get; set; } + + [DataMember] + public string streetAddress { get; set; } + + [DataMember] + public string locality { get; set; } + + [DataMember] + public string region { get; set; } + + [DataMember] + public string postalCode { get; set; } + + [DataMember] + public string country { get; set; } + + [DataMember] + public bool pref { get; set; } + } + + [DataContract] + public class JSONContactName + { + [DataMember] + public string formatted { get; set; } + + [DataMember] + public string familyName { get; set; } + + [DataMember] + public string givenName { get; set; } + + [DataMember] + public string middleName { get; set; } + + [DataMember] + public string honorificPrefix { get; set; } + + [DataMember] + public string honorificSuffix { get; set; } + } + + [DataContract] + public class JSONContactField + { + [DataMember] + public string type { get; set; } + + [DataMember] + public string value { get; set; } + + [DataMember] + public bool pref { get; set; } + } + + [DataContract] + public class JSONContactOrganization + { + [DataMember] + public string type { get; set; } + + [DataMember] + public string name { get; set; } + + [DataMember] + public bool pref { get; set; } + + [DataMember] + public string department { get; set; } + + [DataMember] + public string title { get; set; } + } + + [DataContract] + public class JSONContact + { + [DataMember] + public string id { get; set; } + + [DataMember] + public string rawId { get; set; } + + [DataMember] + public string displayName { get; set; } + + [DataMember] + public string nickname { get; set; } + + [DataMember] + public string note { get; set; } + + [DataMember] + public JSONContactName name { get; set; } + + [DataMember] + public JSONContactField[] emails { get; set; } + + [DataMember] + public JSONContactField[] phoneNumbers { get; set; } + + [DataMember] + public JSONContactField[] ims { get; set; } + + [DataMember] + public JSONContactField[] photos { get; set; } + + [DataMember] + public JSONContactField[] categories { get; set; } + + [DataMember] + public JSONContactField[] urls { get; set; } + + [DataMember] + public JSONContactOrganization[] organizations { get; set; } + + [DataMember] + public JSONContactAddress[] addresses { get; set; } + } + + + public class Contacts : BaseCommand + { + public const int UNKNOWN_ERROR = 0; + public const int INVALID_ARGUMENT_ERROR = 1; + public const int TIMEOUT_ERROR = 2; + public const int PENDING_OPERATION_ERROR = 3; + public const int IO_ERROR = 4; + public const int NOT_SUPPORTED_ERROR = 5; + public const int PERMISSION_DENIED_ERROR = 20; + public const int SYNTAX_ERR = 8; + + // refer here for contact properties we can access: http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.savecontacttask_members%28v=VS.92%29.aspx + public void save(string jsonContact) + { + // jsonContact is actually an array of 1 {contact} + string[] args = JSON.JsonHelper.Deserialize(jsonContact); + + + JSONContact contact = JSON.JsonHelper.Deserialize(args[0]); + + SaveContactTask contactTask = new SaveContactTask(); + + if (contact.nickname != null) + { + contactTask.Nickname = contact.nickname; + } + if (contact.urls != null && contact.urls.Length > 0) + { + contactTask.Website = contact.urls[0].value; + } + if (contact.note != null) + { + contactTask.Notes = contact.note; + } + + #region contact.name + + if (contact.name != null) + { + if (contact.name.givenName != null) + contactTask.FirstName = contact.name.givenName; + if (contact.name.familyName != null) + contactTask.LastName = contact.name.familyName; + if (contact.name.middleName != null) + contactTask.MiddleName = contact.name.middleName; + if (contact.name.honorificSuffix != null) + contactTask.Suffix = contact.name.honorificSuffix; + if (contact.name.honorificPrefix != null) + contactTask.Title = contact.name.honorificPrefix; + } + + #endregion + + #region contact.org + + if (contact.organizations != null && contact.organizations.Count() > 0) + { + contactTask.Company = contact.organizations[0].name; + contactTask.JobTitle = contact.organizations[0].title; + } + + #endregion + + #region contact.phoneNumbers + + if (contact.phoneNumbers != null && contact.phoneNumbers.Length > 0) + { + foreach (JSONContactField field in contact.phoneNumbers) + { + string fieldType = field.type.ToLower(); + if (fieldType == "work") + { + contactTask.WorkPhone = field.value; + } + else if (fieldType == "home") + { + contactTask.HomePhone = field.value; + } + else if (fieldType == "mobile") + { + contactTask.MobilePhone = field.value; + } + } + } + + #endregion + + #region contact.emails + + if (contact.emails != null && contact.emails.Length > 0) + { + // set up different email types if they are not explicitly defined + foreach (string type in new[] {"personal", "work", "other"}) + { + foreach (JSONContactField field in contact.emails) + { + if (field != null && String.IsNullOrEmpty(field.type)) + { + field.type = type; + break; + } + } + } + + foreach (JSONContactField field in contact.emails) + { + if (field != null) + { + if (field.type != null && field.type != "other") + { + string fieldType = field.type.ToLower(); + if (fieldType == "work") + { + contactTask.WorkEmail = field.value; + } + else if (fieldType == "home" || fieldType == "personal") + { + contactTask.PersonalEmail = field.value; + } + } + else + { + contactTask.OtherEmail = field.value; + } + } + } + } + + #endregion + + if (contact.note != null && contact.note.Length > 0) + { + contactTask.Notes = contact.note; + } + + #region contact.addresses + + if (contact.addresses != null && contact.addresses.Length > 0) + { + foreach (JSONContactAddress address in contact.addresses) + { + if (address.type == null) + { + address.type = "home"; // set a default + } + string fieldType = address.type.ToLower(); + if (fieldType == "work") + { + contactTask.WorkAddressCity = address.locality; + contactTask.WorkAddressCountry = address.country; + contactTask.WorkAddressState = address.region; + contactTask.WorkAddressStreet = address.streetAddress; + contactTask.WorkAddressZipCode = address.postalCode; + } + else if (fieldType == "home" || fieldType == "personal") + { + contactTask.HomeAddressCity = address.locality; + contactTask.HomeAddressCountry = address.country; + contactTask.HomeAddressState = address.region; + contactTask.HomeAddressStreet = address.streetAddress; + contactTask.HomeAddressZipCode = address.postalCode; + } + else + { + // no other address fields available ... + Debug.WriteLine("Creating contact with unsupported address type :: " + address.type); + } + } + } + + #endregion + + contactTask.Completed += ContactSaveTaskCompleted; + contactTask.Show(); + } + + private void ContactSaveTaskCompleted(object sender, SaveContactResult e) + { + SaveContactTask task = sender as SaveContactTask; + + if (e.TaskResult == TaskResult.OK) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + var deviceContacts = new DeviceContacts(); + deviceContacts.SearchCompleted += + postAdd_SearchCompleted; + + if (task != null) + { + string displayName = String.Format("{0}{2}{1}", task.FirstName, task.LastName, + String.IsNullOrEmpty(task.FirstName) ? "" : " "); + + deviceContacts.SearchAsync(displayName, FilterKind.DisplayName, task); + } + }); + } + else if (e.TaskResult == TaskResult.Cancel) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled.")); + } + } + + private void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e) + { + if (e.Results.Any()) + { + new List(); + + int n = (from Contact contact in e.Results select contact.GetHashCode()).Max(); + Contact newContact = (from Contact contact in e.Results + where contact.GetHashCode() == n + select contact).First(); + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, newContact.ToJson(null))); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT)); + } + } + + + public void remove(string id) + { + // note id is wrapped in [] and always has exactly one string ... + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{\"code\":" + NOT_SUPPORTED_ERROR + "}")); + } + + public void pickContact(string arguments) + { + string[] args = JSON.JsonHelper.Deserialize(arguments); + + // Use custom contact picker because WP8 api doesn't provide its' own + // contact picker, only PhoneNumberChooser or EmailAddressChooserTask + var task = new ContactPickerTask(); + var desiredFields = JSON.JsonHelper.Deserialize(args[0]); + + task.Completed += delegate(Object sender, ContactPickerTask.PickResult e) + { + if (e.TaskResult == TaskResult.OK) + { + string strResult = e.Contact.ToJson(desiredFields); + var result = new PluginResult(PluginResult.Status.OK) + { + Message = strResult + }; + DispatchCommandResult(result); + } + if (e.TaskResult == TaskResult.Cancel) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled.")); + } + }; + + task.Show(); + } + + public void search(string searchCriteria) + { + string[] args = JSON.JsonHelper.Deserialize(searchCriteria); + + ContactSearchParams searchParams = new ContactSearchParams(); + try + { + searchParams.fields = JSON.JsonHelper.Deserialize(args[0]); + searchParams.options = JSON.JsonHelper.Deserialize(args[1]); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_ARGUMENT_ERROR)); + return; + } + + if (searchParams.options == null) + { + searchParams.options = new SearchOptions(); + searchParams.options.filter = ""; + searchParams.options.multiple = true; + } + else if (searchParams.options.filter == null) + { + searchParams.options.filter = ""; + } + + DeviceContacts deviceContacts = new DeviceContacts(); + deviceContacts.SearchCompleted += contacts_SearchCompleted; + + // default is to search all fields + FilterKind filterKind = FilterKind.None; + // if only one field is specified, we will try the 3 available DeviceContact search filters + if (searchParams.fields.Count() == 1) + { + if (searchParams.fields.Contains("name")) + { + filterKind = FilterKind.DisplayName; + } + else if (searchParams.fields.Contains("emails")) + { + filterKind = FilterKind.EmailAddress; + } + else if (searchParams.fields.Contains("phoneNumbers")) + { + filterKind = FilterKind.PhoneNumber; + } + } + + try + { + deviceContacts.SearchAsync(searchParams.options.filter, filterKind, searchParams); + } + catch (Exception ex) + { + Debug.WriteLine("search contacts exception :: " + ex.Message); + } + } + + private void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e) + { + var searchParams = (ContactSearchParams) e.State; + + List foundContacts = null; + // used for comparing strings, "" instantiates with InvariantCulture + CultureInfo culture = new CultureInfo(""); + // make the search comparisons case insensitive. + CompareOptions compare_option = CompareOptions.IgnoreCase; + + // if we have multiple search fields + + if (!String.IsNullOrEmpty(searchParams.options.filter) && searchParams.fields.Count() > 1) + { + foundContacts = new List(); + if (searchParams.fields.Contains("emails")) + { + foundContacts.AddRange(from Contact con in e.Results + from ContactEmailAddress a in con.EmailAddresses + where + culture.CompareInfo.IndexOf(a.EmailAddress, searchParams.options.filter, + compare_option) >= 0 + select con); + } + if (searchParams.fields.Contains("displayName")) + { + foundContacts.AddRange(from Contact con in e.Results + where + culture.CompareInfo.IndexOf(con.DisplayName, searchParams.options.filter, + compare_option) >= 0 + select con); + } + if (searchParams.fields.Contains("name")) + { + foundContacts.AddRange( + from Contact con in e.Results + where con.CompleteName != null && ( + (con.CompleteName.FirstName != null && culture.CompareInfo.IndexOf(con.CompleteName.FirstName, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.LastName != null && culture.CompareInfo.IndexOf(con.CompleteName.LastName, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.MiddleName != null && culture.CompareInfo.IndexOf(con.CompleteName.MiddleName, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.Nickname != null && culture.CompareInfo.IndexOf(con.CompleteName.Nickname, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.Suffix != null && culture.CompareInfo.IndexOf(con.CompleteName.Suffix, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.Title != null && culture.CompareInfo.IndexOf(con.CompleteName.Title, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.YomiFirstName != null && culture.CompareInfo.IndexOf(con.CompleteName.YomiFirstName, searchParams.options.filter, compare_option) >= 0) || + (con.CompleteName.YomiLastName != null && culture.CompareInfo.IndexOf(con.CompleteName.YomiLastName, searchParams.options.filter, compare_option) >= 0)) + select con); + } + if (searchParams.fields.Contains("phoneNumbers")) + { + foundContacts.AddRange(from Contact con in e.Results + from ContactPhoneNumber a in con.PhoneNumbers + where + culture.CompareInfo.IndexOf(a.PhoneNumber, searchParams.options.filter, + compare_option) >= 0 + select con); + } + if (searchParams.fields.Contains("urls")) + { + foundContacts.AddRange(from Contact con in e.Results + from string a in con.Websites + where + culture.CompareInfo.IndexOf(a, searchParams.options.filter, + compare_option) >= 0 + select con); + } + } + else + { + foundContacts = new List(e.Results); + } + + string strResult = ""; + + IEnumerable distinctContacts = foundContacts.Distinct(); + + foreach (Contact contact in distinctContacts) + { + strResult += contact.ToJson(searchParams.options.desiredFields) + ","; + + if (!searchParams.options.multiple) + { + break; // just return the first item + } + } + PluginResult result = new PluginResult(PluginResult.Status.OK); + result.Message = "[" + strResult.TrimEnd(',') + "]"; + DispatchCommandResult(result); + } + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/src/wp/ContactsHelper.cs b/plugins/cordova-plugin-contacts/src/wp/ContactsHelper.cs new file mode 100644 index 0000000..80166fa --- /dev/null +++ b/plugins/cordova-plugin-contacts/src/wp/ContactsHelper.cs @@ -0,0 +1,335 @@ +/* + 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. +*/ + +namespace WPCordovaClassLib.Cordova.Commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using Microsoft.Phone.UserData; + using System.IO; + + /// + /// Implements helper functionality to serialize contact to JSON string. + /// + internal static class ContactsHelper + { + /// + /// Converts Contact object to string representation + /// + /// Contact object + /// array of fields names + /// JSON string + public static string ToJson(this Contact contact, string[] desiredFields) + { + var contactFieldsWithJsonVals = contact.PopulateContactDictionary(); + + // if desiredFields are not defined, use all avilable fields + if (desiredFields == null || desiredFields.Length == 0) + { + desiredFields = contactFieldsWithJsonVals.Keys.ToArray(); + } + + return FillResultWithFields(desiredFields, contactFieldsWithJsonVals); + } + + /// + /// Returns JSON string with desired fields only. + /// + /// The desired fields. + /// The contact fields with JSON values. + /// JSON string + private static string FillResultWithFields(string[] desiredFields, Dictionary> contactFieldsWithJsonVals) + { + var result = new StringBuilder(); + for (int i = 0; i < desiredFields.Count(); i++) + { + if (!contactFieldsWithJsonVals.ContainsKey(desiredFields[i])) + { + continue; + } + + result.Append(contactFieldsWithJsonVals[desiredFields[i]]()); + if (i != desiredFields.Count() - 1) + { + result.Append(","); + } + } + + return "{" + result + "}"; + } + + /// + /// Populates the contact dictionary. + /// + /// Contact, that converted to JSON + /// JSON string with populated data + private static Dictionary> PopulateContactDictionary(this Contact contact) + { + var contactFieldsJsonValsDictionary = new Dictionary>(StringComparer.InvariantCultureIgnoreCase) + { + { "id", () => string.Format("\"id\":\"{0}\"", contact.GetHashCode()) }, + { "displayName", () => string.Format("\"displayName\":\"{0}\"", EscapeJson(contact.DisplayName)) }, + { + "nickname", + () => + string.Format( + "\"nickname\":\"{0}\"", + EscapeJson(contact.CompleteName != null ? contact.CompleteName.Nickname : string.Empty)) + }, + { "phoneNumbers", () => string.Format("\"phoneNumbers\":[{0}]", FormatJsonPhoneNumbers(contact)) }, + { "emails", () => string.Format("\"emails\":[{0}]", FormatJsonEmails(contact)) }, + { "addresses", () => string.Format("\"addresses\":[{0}]", FormatJsonAddresses(contact)) }, + { "urls", () => string.Format("\"urls\":[{0}]", FormatJsonWebsites(contact)) }, + { "photos", () => string.Format("\"photos\":[{0}]", FormatJsonPhotos(contact)) }, + { "name", () => string.Format("\"name\":{0}", FormatJsonName(contact)) }, + { "note", () => string.Format("\"note\":\"{0}\"", EscapeJson(contact.Notes.FirstOrDefault())) }, + { + "birthday", + () => + string.Format( + "\"birthday\":\"{0}\"", + EscapeJson(Convert.ToString(contact.Birthdays.FirstOrDefault()))) + } + }; + return contactFieldsJsonValsDictionary; + } + + /// + /// Add escape characters to the JSON string. + /// + /// Input JSON formatted string + /// Escaped JSON string + private static string EscapeJson(string str) + { + if (string.IsNullOrEmpty(str)) + { + return str; + } + + return str.Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\t", "\\t") + .Replace("\"", "\\\"") + .Replace("&", "\\&"); + } + + /// + /// Formats phone numbers to JSON string. + /// + /// Contact object + /// JSON string + private static string FormatJsonPhoneNumbers(Contact con) + { + string retVal = string.Empty; + const string ContactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\""; + foreach (ContactPhoneNumber number in con.PhoneNumbers) + { + string contactField = string.Format(ContactFieldFormat, number.Kind.ToString(), number.PhoneNumber); + retVal += "{" + contactField + "},"; + } + + return retVal.TrimEnd(','); + } + + /* + * formatted: The complete name of the contact. (DOMString) + familyName: The contacts family name. (DOMString) + givenName: The contacts given name. (DOMString) + middleName: The contacts middle name. (DOMString) + honorificPrefix: The contacts prefix (example Mr. or Dr.) (DOMString) + honorificSuffix: The contacts suffix (example Esq.). (DOMString) + */ + + /// + /// Formats the name to JSON string. + /// + /// Contact object + /// JSON string + private static string FormatJsonName(Contact con) + { + string retVal; + const string FormatStr = "\"formatted\":\"{0}\"," + + "\"familyName\":\"{1}\"," + + "\"givenName\":\"{2}\"," + + "\"middleName\":\"{3}\"," + + "\"honorificPrefix\":\"{4}\"," + + "\"honorificSuffix\":\"{5}\""; + + if (con.CompleteName != null) + { + retVal = string.Format( + FormatStr, + EscapeJson(con.CompleteName.FirstName + " " + con.CompleteName.LastName), + //// TODO: does this need suffix? middlename? + EscapeJson(con.CompleteName.LastName), + EscapeJson(con.CompleteName.FirstName), + EscapeJson(con.CompleteName.MiddleName), + EscapeJson(con.CompleteName.Title), + EscapeJson(con.CompleteName.Suffix)); + } + else + { + retVal = string.Format(FormatStr, "", "", "", "", "", ""); + } + + return "{" + retVal + "}"; + } + + /// + /// Format Emails to JSON string. + /// + /// Contact object + /// JSON string + private static string FormatJsonEmails(Contact con) + { + string retVal = string.Empty; + const string ContactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\""; + foreach (ContactEmailAddress address in con.EmailAddresses) + { + string contactField = string.Format( + ContactFieldFormat, + address.Kind.ToString(), + EscapeJson(address.EmailAddress)); + + retVal += "{" + contactField + "},"; + } + + return retVal.TrimEnd(','); + } + + /// + /// Format Addresses to JSON string. + /// + /// Contact object + /// JSON string + private static string FormatJsonAddresses(Contact con) + { + string retVal = string.Empty; + foreach (ContactAddress address in con.Addresses) + { + retVal += GetFormattedJsonAddress(address, false) + ","; + } + + return retVal.TrimEnd(','); + } + + /// + /// Format Websites to JSON string. + /// + /// Contact object + /// JSON string + private static string FormatJsonWebsites(Contact con) + { + string retVal = string.Empty; + foreach (string website in con.Websites) + { + retVal += "\"" + EscapeJson(website) + "\","; + } + + return retVal.TrimEnd(','); + } + + /// + /// Format single address to JSON string. + /// + /// + /// Contact address. + /// + /// + /// Contact is preferred? + /// + /// + /// JSON string + /// + private static string GetFormattedJsonAddress(ContactAddress address, bool isPrefered) + { + const string AddressFormatString = "\"pref\":{0}," + // bool + "\"type\":\"{1}\"," + + "\"formatted\":\"{2}\"," + + "\"streetAddress\":\"{3}\"," + + "\"locality\":\"{4}\"," + + "\"region\":\"{5}\"," + + "\"postalCode\":\"{6}\"," + + "\"country\":\"{7}\""; + + string formattedAddress = EscapeJson(address.PhysicalAddress.AddressLine1 + " " + + address.PhysicalAddress.AddressLine2 + " " + + address.PhysicalAddress.City + " " + + address.PhysicalAddress.StateProvince + " " + + address.PhysicalAddress.CountryRegion + " " + + address.PhysicalAddress.PostalCode); + + string jsonAddress = string.Format( + AddressFormatString, + isPrefered ? "\"true\"" : "\"false\"", + address.Kind.ToString(), + formattedAddress, + EscapeJson(address.PhysicalAddress.AddressLine1 + " " + address.PhysicalAddress.AddressLine2), + address.PhysicalAddress.City, + address.PhysicalAddress.StateProvince, + address.PhysicalAddress.PostalCode, + address.PhysicalAddress.CountryRegion); + + return "{" + jsonAddress + "}"; + } + + /// + /// Formats contact photos to JSON string. + /// + /// Contact object + /// JSON string + private static string FormatJsonPhotos(Contact con) { + + // we return single photo since contact object instance contains single picture only + var photoStream = con.GetPicture(); + + if (photoStream == null) { + return ""; + } + + return String.Format("{{value:\"{0}\", type: \"data\", pref: false}}", GetImageContent(photoStream)); + } + + /// + /// Returns image content in a form of base64 string + /// + /// Image stream + /// Base64 representation of the image + private static string GetImageContent(Stream stream) + { + byte[] imageContent = null; + + try + { + int streamLength = (int)stream.Length; + imageContent = new byte[streamLength + 1]; + stream.Read(imageContent, 0, streamLength); + + } + finally + { + stream.Dispose(); + } + + return Convert.ToBase64String(imageContent); + } + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-contacts/tests/plugin.xml b/plugins/cordova-plugin-contacts/tests/plugin.xml new file mode 100644 index 0000000..a15e4b5 --- /dev/null +++ b/plugins/cordova-plugin-contacts/tests/plugin.xml @@ -0,0 +1,31 @@ + + + + + Cordova Contacts Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-contacts/tests/tests.js b/plugins/cordova-plugin-contacts/tests/tests.js new file mode 100644 index 0000000..376e5a9 --- /dev/null +++ b/plugins/cordova-plugin-contacts/tests/tests.js @@ -0,0 +1,897 @@ +/* + * + * 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.defineAutoTests = function() { + // global to store a contact so it doesn't have to be created or retrieved multiple times + // all of the setup/teardown test methods can reference the following variables to make sure to do the right cleanup + var gContactObj = null, + isWindowsPhone8 = cordova.platformId == 'windowsphone', + isWindows = (cordova.platformId === "windows") || (cordova.platformId === "windows8"), + isWindowsPhone81 = isWindows && WinJS.Utilities.isPhone; + + var isIOSPermissionBlocked = false; + + var fail = function(done) { + expect(true).toBe(false); + done(); + }; + + var MEDIUM_TIMEOUT = 30000; + + var removeContact = function(done) { + if (!gContactObj) { + done(); + return; + } + + gContactObj.remove(function() { + gContactObj = null; + done(); + }, function() { + done(); + }); + }; + + function removeContactsByFields(fields, filter, done) { + var obj = new ContactFindOptions(); + obj.filter = filter; + obj.multiple = true; + navigator.contacts.find(fields, function(contacts) { + var removes = []; + contacts.forEach(function(contact) { + removes.push(contact); + }); + if (removes.length == 0) { + done(); + return; + } + + var nextToRemove = undefined; + if (removes.length > 0) { + nextToRemove = removes.shift(); + } + + function removeNext(item) { + if (typeof item === 'undefined') { + done(); + return; + } + + if (removes.length > 0) { + nextToRemove = removes.shift(); + } else { + nextToRemove = undefined; + } + + item.remove(function removeSucceeded() { + removeNext(nextToRemove); + }, function removeFailed() { + removeNext(nextToRemove); + }); + } + removeNext(nextToRemove); + }, done, obj); + } + + describe("Contacts (navigator.contacts)", function() { + it("contacts.spec.1 should exist", function() { + expect(navigator.contacts).toBeDefined(); + }); + + it("contacts.spec.2 should contain a find function", function() { + expect(navigator.contacts.find).toBeDefined(); + expect(typeof navigator.contacts.find).toBe('function'); + }); + + describe("find method", function() { + it("contacts.spec.3 success callback should be called with an array", function(done) { + // Find method is not supported on Windows platform + if (isWindows && !isWindowsPhone81) { + pending(); + return; + } + var win = function(result) { + expect(result).toBeDefined(); + expect(result instanceof Array).toBe(true); + done(); + }, + obj = new ContactFindOptions(); + + obj.filter = ""; + obj.multiple = true; + + function failed(err) { + if (err.code == ContactError.PERMISSION_DENIED_ERROR) { + isIOSPermissionBlocked = true; + done(); + } + } + navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], win, failed, obj); + }); + + it("contacts.spec.4 success callback should be called with an array, even if partial ContactFindOptions specified", function(done) { + // Find method is not supported on Windows platform + if ((isWindows && !isWindowsPhone81) || isIOSPermissionBlocked) { + pending(); + return; + } + var win = function(result) { + expect(result).toBeDefined(); + expect(result instanceof Array).toBe(true); + done(); + }; + + navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], win, fail.bind(null, done), { + multiple: true + }); + }); + + it("contacts.spec.5 should throw an exception if success callback is empty", function() { + var obj = new ContactFindOptions(); + obj.filter = ""; + obj.multiple = true; + + expect(function() { + navigator.contacts.find(["displayName", "name", "emails", "phoneNumbers"], null, fail.bind(null, done), obj); + }).toThrow(); + }); + + it("contacts.spec.6 error callback should be called when no fields are specified", function(done) { + var win = fail, + // we don't want this to be called + error = function(result) { + expect(result).toBeDefined(); + expect(result.code).toBe(ContactError.INVALID_ARGUMENT_ERROR); + done(); + }, + obj = new ContactFindOptions(); + + obj.filter = ""; + obj.multiple = true; + navigator.contacts.find([], win, error, obj); + }); + + describe("with newly-created contact", function() { + + afterEach(function (done) { + removeContact(done); + }); + + it("contacts.spec.7 should be able to find a contact by name", function(done) { + // Find method is not supported on Windows Store apps. + // also this test will be skipped for Windows Phone 8.1 because function "save" not supported on WP8.1 + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + + var foundName = function(result) { + var bFound = false; + try { + for (var i = 0; i < result.length; i++) { + if (result[i].name.familyName == "Delete") { + bFound = true; + break; + } + } + } catch (e) { + return false; + } + return bFound; + }, + test = function(savedContact) { + // update so contact will get removed + gContactObj = savedContact; + // ---- + // Find asserts + // --- + var findWin = function(object) { + expect(object instanceof Array).toBe(true); + expect(object.length >= 1).toBe(true); + expect(foundName(object)).toBe(true); + done(); + }, + findFail = fail, + obj = new ContactFindOptions(); + + obj.filter = "Delete"; + obj.multiple = true; + + navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail.bind(null, done), obj); + }; + + gContactObj = new Contact(); + gContactObj.name = new ContactName(); + gContactObj.name.familyName = "Delete"; + gContactObj.save(test, fail.bind(null, done)); + }); + }); + }); + + describe('create method', function() { + it("contacts.spec.8 should exist", function() { + expect(navigator.contacts.create).toBeDefined(); + expect(typeof navigator.contacts.create).toBe('function'); + }); + + it("contacts.spec.9 should return a Contact object", function() { + var bDay = new Date(1976, 7, 4); + var obj = navigator.contacts.create({ + "displayName": "test name", + "gender": "male", + "note": "my note", + "name": { + "formatted": "Mr. Test Name" + }, + "emails": [{ + "value": "here@there.com" + }, { + "value": "there@here.com" + }], + "birthday": bDay + }); + + expect(obj).toBeDefined(); + expect(obj.displayName).toBe('test name'); + expect(obj.note).toBe('my note'); + expect(obj.name.formatted).toBe('Mr. Test Name'); + expect(obj.emails.length).toBe(2); + expect(obj.emails[0].value).toBe('here@there.com'); + expect(obj.emails[1].value).toBe('there@here.com'); + expect(obj.nickname).toBe(null); + expect(obj.birthday).toBe(bDay); + }); + }); + + describe("Contact object", function() { + it("contacts.spec.10 should be able to create instance", function() { + var contact = new Contact("a", "b", new ContactName("a", "b", "c", "d", "e", "f"), "c", [], [], [], [], [], "f", "i", [], [], []); + expect(contact).toBeDefined(); + expect(contact.id).toBe("a"); + expect(contact.displayName).toBe("b"); + expect(contact.name.formatted).toBe("a"); + expect(contact.nickname).toBe("c"); + expect(contact.phoneNumbers).toBeDefined(); + expect(contact.emails).toBeDefined(); + expect(contact.addresses).toBeDefined(); + expect(contact.ims).toBeDefined(); + expect(contact.organizations).toBeDefined(); + expect(contact.birthday).toBe("f"); + expect(contact.note).toBe("i"); + expect(contact.photos).toBeDefined(); + expect(contact.categories).toBeDefined(); + expect(contact.urls).toBeDefined(); + }); + + it("contacts.spec.11 should be able to define a ContactName object", function() { + var contactName = new ContactName("Dr. First Last Jr.", "Last", "First", "Middle", "Dr.", "Jr."); + expect(contactName).toBeDefined(); + expect(contactName.formatted).toBe("Dr. First Last Jr."); + expect(contactName.familyName).toBe("Last"); + expect(contactName.givenName).toBe("First"); + expect(contactName.middleName).toBe("Middle"); + expect(contactName.honorificPrefix).toBe("Dr."); + expect(contactName.honorificSuffix).toBe("Jr."); + }); + + it("contacts.spec.12 should be able to define a ContactField object", function() { + var contactField = new ContactField("home", "8005551212", true); + expect(contactField).toBeDefined(); + expect(contactField.type).toBe("home"); + expect(contactField.value).toBe("8005551212"); + expect(contactField.pref).toBe(true); + }); + + it("contacts.spec.13 ContactField object should coerce type and value properties to strings", function() { + var contactField = new ContactField(12345678, 12345678, true); + expect(contactField.type).toBe("12345678"); + expect(contactField.value).toBe("12345678"); + }); + + it("contacts.spec.14 should be able to define a ContactAddress object", function() { + var contactAddress = new ContactAddress(true, "home", "a", "b", "c", "d", "e", "f"); + expect(contactAddress).toBeDefined(); + expect(contactAddress.pref).toBe(true); + expect(contactAddress.type).toBe("home"); + expect(contactAddress.formatted).toBe("a"); + expect(contactAddress.streetAddress).toBe("b"); + expect(contactAddress.locality).toBe("c"); + expect(contactAddress.region).toBe("d"); + expect(contactAddress.postalCode).toBe("e"); + expect(contactAddress.country).toBe("f"); + }); + + it("contacts.spec.15 should be able to define a ContactOrganization object", function() { + var contactOrg = new ContactOrganization(true, "home", "a", "b", "c", "d", "e", "f", "g"); + expect(contactOrg).toBeDefined(); + expect(contactOrg.pref).toBe(true); + expect(contactOrg.type).toBe("home"); + expect(contactOrg.name).toBe("a"); + expect(contactOrg.department).toBe("b"); + expect(contactOrg.title).toBe("c"); + }); + + it("contacts.spec.16 should be able to define a ContactFindOptions object", function() { + var contactFindOptions = new ContactFindOptions("a", true, "b"); + expect(contactFindOptions).toBeDefined(); + expect(contactFindOptions.filter).toBe("a"); + expect(contactFindOptions.multiple).toBe(true); + }); + + it("contacts.spec.17 should contain a clone function", function() { + var contact = new Contact(); + expect(contact.clone).toBeDefined(); + expect(typeof contact.clone).toBe('function'); + }); + + it("contacts.spec.18 clone function should make deep copy of Contact Object", function() { + var contact = new Contact(); + contact.id = 1; + contact.displayName = "Test Name"; + contact.nickname = "Testy"; + contact.gender = "male"; + contact.note = "note to be cloned"; + contact.name = new ContactName("Mr. Test Name"); + + var clonedContact = contact.clone(); + + expect(contact.id).toBe(1); + expect(clonedContact.id).toBe(null); + expect(clonedContact.displayName).toBe(contact.displayName); + expect(clonedContact.nickname).toBe(contact.nickname); + expect(clonedContact.gender).toBe(contact.gender); + expect(clonedContact.note).toBe(contact.note); + expect(clonedContact.name.formatted).toBe(contact.name.formatted); + expect(clonedContact.connected).toBe(contact.connected); + }); + + it("contacts.spec.19 should contain a save function", function() { + var contact = new Contact(); + expect(contact.save).toBeDefined(); + expect(typeof contact.save).toBe('function'); + }); + + it("contacts.spec.20 should contain a remove function", function() { + var contact = new Contact(); + expect(contact.remove).toBeDefined(); + expect(typeof contact.remove).toBe('function'); + }); + }); + + describe('save method', function() { + + afterEach(function (done) { + removeContact(done); + }); + + it("contacts.spec.21 should be able to save a contact", function(done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + + var bDay = new Date(1976, 6, 4); + var obj = { + "gender": "male", + "note": "my note", + "name": { + "familyName": "Delete", + "givenName": "Test" + }, + "emails": [{ + "value": "here@there.com" + }, { + "value": "there@here.com" + }], + "birthday": bDay + }; + + var saveSuccess = function(obj) { + expect(obj).toBeDefined(); + expect(obj.note).toBe('my note'); + expect(obj.name.familyName).toBe('Delete'); + expect(obj.name.givenName).toBe('Test'); + expect(obj.emails.length).toBe(2); + expect(obj.emails[0].value).toBe('here@there.com'); + expect(obj.emails[1].value).toBe('there@here.com'); + expect(obj.birthday.toDateString()).toBe(bDay.toDateString()); + expect(obj.addresses).toBe(null); + gContactObj = obj; + done(); + }, + saveFail = fail.bind(null, done); + + navigator.contacts + .create(obj) + .save(saveSuccess, saveFail); + }); + + it("contacts.spec.22 update a contact", function(done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + + var aDay = new Date(1976, 6, 4); + var bDay; + var noteText = "an UPDATED note"; + + var obj = { + "gender": "male", + "note": "my note", + "name": { + "familyName": "Delete", + "givenName": "Test" + }, + "emails": [{ + "value": "here@there.com" + }, { + "value": "there@here.com" + }], + "birthday": aDay + }; + + var saveFail = fail.bind(null, done); + + var saveSuccess = function(obj) { + gContactObj = obj; + gContactObj.emails[1].value = ""; + bDay = new Date(1975, 5, 4); + gContactObj.birthday = bDay; + gContactObj.note = noteText; + gContactObj.save(updateSuccess, saveFail); + }; + + var updateSuccess = function(obj) { + expect(obj).toBeDefined(); + expect(obj.id).toBe(gContactObj.id); + expect(obj.note).toBe(noteText); + expect(obj.birthday.toDateString()).toBe(bDay.toDateString()); + expect(obj.emails.length).toBe(1); + expect(obj.emails[0].value).toBe('here@there.com'); + done(); + }; + + navigator.contacts + .create(obj) + .save(saveSuccess, saveFail); + + }, MEDIUM_TIMEOUT); + }); + + describe('Contact.remove method', function(done) { + afterEach(function (done) { + removeContact(done); + }); + + it("contacts.spec.23 calling remove on a contact that has an id of null should return ContactError.UNKNOWN_ERROR", function(done) { + var expectedFail = function(result) { + expect(result.code).toBe(ContactError.UNKNOWN_ERROR); + done(); + }; + + var rmContact = new Contact(); + rmContact.remove(fail.bind(null, done), expectedFail); + }); + + it("contacts.spec.24 calling remove on a contact that does not exist should return ContactError.UNKNOWN_ERROR", function(done) { + // remove method is not supported on Windows platform + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + var rmWin = fail.bind(null, done); + var rmFail = function(result) { + expect(result.code).toBe(ContactError.UNKNOWN_ERROR); + done(); + }; + + // this is a bit risky as some devices may have contact ids that large + var contact = new Contact("this string is supposed to be a unique identifier that will never show up on a device"); + contact.remove(rmWin, rmFail); + }, MEDIUM_TIMEOUT); + }); + + describe("Round trip Contact tests (creating + save + delete + find)", function() { + var saveAndFindBy = function (fields, filter, done) { + removeContactsByFields(["note"], "DeleteMe", function() { + gContactObj.save(function(c_obj) { + var findWin = function(cs) { + // update to have proper saved id + gContactObj = cs[0]; + expect(cs.length).toBe(1); + done(); + }; + var findFail = fail; + var obj = new ContactFindOptions(); + obj.filter = filter; + obj.multiple = true; + navigator.contacts.find(fields, findWin, findFail, obj); + }, fail); + }); + }; + + afterEach(function (done) { + removeContact(done); + }); + + it("contacts.spec.25 Creating, saving, finding a contact should work", function(done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + var contactName = "DeleteMe"; + gContactObj = new Contact(); + gContactObj.name = new ContactName(); + gContactObj.name.familyName = contactName; + saveAndFindBy(["displayName", "name"], contactName, done); + }, MEDIUM_TIMEOUT); + + it("contacts.spec.26 Creating, saving, finding a contact should work, removing it should work", function(done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + var contactName = "DeleteMe"; + gContactObj = new Contact(); + gContactObj.name = new ContactName(); + gContactObj.name.familyName = contactName; + saveAndFindBy(["displayName", "name"], contactName, function() { + gContactObj.remove(function() { + done(); + }, function(e) { + throw ("Newly created contact's remove function invoked error callback. Test failed."); + }); + }); + }, MEDIUM_TIMEOUT); + + it("contacts.spec.27 Should not be able to delete the same contact twice", function(done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8 || isIOSPermissionBlocked) { + pending(); + } + var contactName = "DeleteMe"; + gContactObj = new Contact(); + gContactObj.name = new ContactName(); + gContactObj.name.familyName = contactName; + saveAndFindBy(["displayName", "name"], contactName, function() { + gContactObj.remove(function() { + var findWin = function(seas) { + expect(seas.length).toBe(0); + gContactObj.remove(function() { + throw ("Success callback called after non-existent Contact object called remove(). Test failed."); + }, function(e) { + expect(e.code).toBe(ContactError.UNKNOWN_ERROR); + done(); + }); + }; + var obj = new ContactFindOptions(); + obj.filter = contactName; + obj.multiple = true; + navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, fail, obj); + }, fail); + }); + }, MEDIUM_TIMEOUT); + + it("contacts.spec.28 should find a contact with unicode name", function (done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8) { + pending(); + } + var contactName = "\u2602"; + gContactObj = new Contact(); + gContactObj.note = "DeleteMe"; + gContactObj.name = new ContactName(); + gContactObj.name.familyName = contactName; + saveAndFindBy(["displayName", "name"], contactName, done); + }, MEDIUM_TIMEOUT); + + it("contacts.spec.29 should find a contact without a name", function (done) { + // Save method is not supported on Windows platform + if (isWindows || isWindowsPhone8) { + pending(); + } + + gContactObj = new Contact(); + var phoneNumbers = [1]; + phoneNumbers[0] = new ContactField('work', '555-555-1234', true); + gContactObj.phoneNumbers = phoneNumbers; + + saveAndFindBy(["phoneNumbers"], "555-555-1234", done); + + }, MEDIUM_TIMEOUT); + }); + + describe('ContactError interface', function() { + it("contacts.spec.30 ContactError constants should be defined", function() { + expect(ContactError.UNKNOWN_ERROR).toBe(0); + expect(ContactError.INVALID_ARGUMENT_ERROR).toBe(1); + expect(ContactError.TIMEOUT_ERROR).toBe(2); + expect(ContactError.PENDING_OPERATION_ERROR).toBe(3); + expect(ContactError.IO_ERROR).toBe(4); + expect(ContactError.NOT_SUPPORTED_ERROR).toBe(5); + expect(ContactError.OPERATION_CANCELLED_ERROR).toBe(6); + expect(ContactError.PERMISSION_DENIED_ERROR).toBe(20); + }); + }); + }); +}; + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +exports.defineManualTests = function(contentEl, createActionButton) { + function getContacts(filter) { + var results = document.getElementById('contact_results'); + var obj = new ContactFindOptions(); + if (filter) { + obj.filter = filter; + } + obj.multiple = true; + navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails", "urls", "note"], function(contacts) { + var s = ""; + if (contacts.length == 0) { + s = "No contacts found"; + } else { + s = "Number of contacts: " + contacts.length + "
"; + for (var i = 0; i < contacts.length; i++) { + var contact = contacts[i]; + var contactNameTag = contact.name ? ""; + } + s = s + "
NamePhoneEmail
" + contact.name.formatted + "" : "
(No Name)"; + s = s + contactNameTag; + if (contact.phoneNumbers && contact.phoneNumbers.length > 0) { + s = s + contact.phoneNumbers[0].value; + } + s = s + "" + if (contact.emails && contact.emails.length > 0) { + s = s + contact.emails[0].value; + } + s = s + "
"; + } + + results.innerHTML = s; + }, function(e) { + if (e.code === ContactError.NOT_SUPPORTED_ERROR) { + results.innerHTML = "Searching for contacts is not supported."; + } else { + results.innerHTML = "Search failed: error " + e.code; + } + }, obj); + } + + function filterContacts() { + var filter = document.getElementById('searchstring'); + getContacts(filter.value); + } + + function pickContact() { + var results = document.getElementById('contact_results'); + navigator.contacts.pickContact( + function (contact) { + results.innerHTML = contact ? + "Picked contact:
" + JSON.stringify(contact, null, 4) + "
" : + "No contacts found"; + + }, + function (e) { + results.innerHTML = (e && e.code === ContactError.NOT_SUPPORTED_ERROR) ? + "Searching for contacts is not supported." : + (e && e.code === ContactError.OPERATION_CANCELLED_ERROR) ? + "Pick cancelled" : + "Pick failed: error " + (e && e.code); + } + ); + } + + function addContact(displayName, name, phoneNumber, birthday) { + try { + var results = document.getElementById('contact_results'); + var contact = navigator.contacts.create({ "displayName": displayName, "name": name, "birthday": birthday, "note": "DeleteMe" }); + + var phoneNumbers = [1]; + phoneNumbers[0] = new ContactField('work', phoneNumber, true); + contact.phoneNumbers = phoneNumbers; + + contact.save(function() { + results.innerHTML = (displayName || "Nameless contact") + " saved."; + }, function(e) { + if (e.code === ContactError.NOT_SUPPORTED_ERROR) { + results.innerHTML = "Saving contacts not supported."; + } else { + results.innerHTML = "Contact save failed: error " + e.code; + } + }); + } catch (e) { + console.error(e.message); + } + } + + function addDooneyEvans() { + var displayName = "Dooney Evans"; + var contactName = { + formatted: "Dooney Evans", + familyName: "Evans", + givenName: "Dooney", + middleName: "" + }; + var phoneNumber = '512-555-1234'; + var birthday = new Date(1985, 0, 23); + + addContact(displayName, contactName, phoneNumber, birthday); + } + + function addNamelessContact() { + addContact(); + } + + function addUnicodeContact() { + var displayName = "Ѐйромонах \nФеофаЊ"; + var contactName = { + formatted: "Ѐйромонах \nФеофаЊ", + familyName: "\nФеофаЊ", + givenName: "Ѐйромонах", + middleName: "" + }; + + addContact(displayName, contactName); + } + + function renameDooneyEvans() { + var results = document.getElementById('contact_results'); + var obj = new ContactFindOptions(); + obj.filter = 'Dooney Evans'; + obj.multiple = false; + + navigator.contacts.find(['displayName', 'name'], function(contacts) { + if (contacts.length == 0) { + results.innerHTML = 'No contacts to update.'; + return; + } + var contact = contacts[0]; + contact.displayName = "Urist McContact"; + var name = new ContactName(); + name.givenName = "Urist"; + name.familyName = "McContact"; + contact.name = name; + contact.save(function(updated) { + results.innerHTML = 'Contact updated.'; + },function(e) { + results.innerHTML = 'Update failed: error ' + e.code; + }); + }, function(e) { + if (e.code === ContactError.NOT_SUPPORTED_ERROR) { + results.innerHTML = 'Searching for contacts is not supported.'; + } else { + results.innerHTML = 'Search failed: error ' + e.code; + } + }, obj) + } + + function removeTestContacts() { + var results = document.getElementById('contact_results'); + results.innerHTML = ""; + var obj = new ContactFindOptions(); + obj.filter = 'DeleteMe'; + obj.multiple = true; + navigator.contacts.find(['note'], function(contacts) { + var removes = []; + contacts.forEach(function(contact) { + removes.push(contact); + }); + if (removes.length == 0) { + results.innerHTML = "No contacts to remove"; + return; + } + + var nextToRemove = undefined; + if (removes.length > 0) { + nextToRemove = removes.shift(); + } + + function removeNext(item) { + if (typeof item === 'undefined') { + return; + } + + if (removes.length > 0) { + nextToRemove = removes.shift(); + } else { + nextToRemove = undefined; + } + + item.remove(function removeSucceeded() { + results.innerHTML += "Removed a contact with ID " + item.id + "
"; + removeNext(nextToRemove); + }, function removeFailed() { + results.innerHTML += "Failed to remove a contact with ID " + item.id + "
"; + removeNext(nextToRemove); + }); + } + removeNext(nextToRemove); + }, function(e) { + if (e.code === ContactError.NOT_SUPPORTED_ERROR) { + results.innerHTML = 'Searching for contacts is not supported.'; + } else { + results.innerHTML = 'Search failed: error ' + e.code; + } + }, obj); + } + + function nameMatches(contact, contactName) { + if (contactName === null && (contact.name === null || contact.name.formatted === null)) { + return true; + } else if (contact.name && contact.name.formatted && contact.name.formatted.indexOf(contactName) > -1) { + return true; + } + return false; + } + + /******************************************************************************/ + + contentEl.innerHTML = '
' + + 'Results:
' + + '
' + + '
' + + '
' + + '

Expected result: Status box will show number of contacts and list them. May be empty on a fresh device until you click Add.

' + + '
Search:
' + + '

Expected result: Will return only contacts which contain specified string

' + + '
' + + '

Expected result: Device\'s address book will be shown. After picking a contact status box will show Contact object, passed to success callback

' + + '
' + + '

Expected result: Will add a new contact. Log will say "Contact saved." or "Saving contacts not supported." if not supported on current platform. Verify by running Get phone contacts again

' + + '
' + + '

Expected result: Will rename "Dooney Evans" to "Urist McContact".

' + + '
' + + '

Expected result: Will remove all contacts created by these tests. Log will output success or failure and ID of the deleted contacts.

'; + + createActionButton("Get phone's contacts", function() { + getContacts(); + }, 'get_contacts'); + + createActionButton("Filter contacts", function() { + filterContacts(); + }, 'filter_contacts'); + + createActionButton("Pick contact", function() { + pickContact(); + }, 'pick_contact'); + + createActionButton("Add a new contact 'Dooney Evans'", function() { + addDooneyEvans(); + }, 'add_contact'); + + createActionButton("Add new nameless contact", function() { + addNamelessContact(); + }, 'add_contact'); + + createActionButton("Add new unicode contact", function() { + addUnicodeContact(); + }, 'add_contact'); + + createActionButton("Rename 'Dooney Evans'", function() { + renameDooneyEvans(); + }, 'update_contact'); + + createActionButton("Delete all test contacts", function() { + removeTestContacts(); + }, 'remove_contacts'); +}; diff --git a/plugins/cordova-plugin-contacts/www/Contact.js b/plugins/cordova-plugin-contacts/www/Contact.js new file mode 100644 index 0000000..9c46a0c --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/Contact.js @@ -0,0 +1,177 @@ +/* + * + * 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'), + exec = require('cordova/exec'), + ContactError = require('./ContactError'), + utils = require('cordova/utils'); + +/** +* Converts primitives into Complex Object +* Currently only used for Date fields +*/ +function convertIn(contact) { + var value = contact.birthday; + try { + contact.birthday = new Date(parseFloat(value)); + } catch (exception){ + console.log("Cordova Contact convertIn error: exception creating date."); + } + return contact; +} + +/** +* Converts Complex objects into primitives +* Only conversion at present is for Dates. +**/ + +function convertOut(contact) { + var value = contact.birthday; + if (value !== null) { + // try to make it a Date object if it is not already + if (!utils.isDate(value)){ + try { + value = new Date(value); + } catch(exception){ + value = null; + } + } + if (utils.isDate(value)){ + value = value.valueOf(); // convert to milliseconds + } + contact.birthday = value; + } + return contact; +} + +/** +* Contains information about a single contact. +* @constructor +* @param {DOMString} id unique identifier +* @param {DOMString} displayName +* @param {ContactName} name +* @param {DOMString} nickname +* @param {Array.} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + argscheck.checkArgs('FF', 'Contact.remove', arguments); + var fail = errorCB && function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + clonedContact.id = null; + clonedContact.rawId = null; + + function nullIds(arr) { + if (arr) { + for (var i = 0; i < arr.length; ++i) { + arr[i].id = null; + } + } + } + + // Loop through and clear out any id's in phones, emails, etc. + nullIds(clonedContact.phoneNumbers); + nullIds(clonedContact.emails); + nullIds(clonedContact.addresses); + nullIds(clonedContact.ims); + nullIds(clonedContact.organizations); + nullIds(clonedContact.categories); + nullIds(clonedContact.photos); + nullIds(clonedContact.urls); + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + argscheck.checkArgs('FFO', 'Contact.save', arguments); + var fail = errorCB && function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (successCB) { + var fullContact = require('./contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; diff --git a/plugins/cordova-plugin-contacts/www/ContactAddress.js b/plugins/cordova-plugin-contacts/www/ContactAddress.js new file mode 100644 index 0000000..3d39086 --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactAddress.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. + * +*/ + +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; diff --git a/plugins/cordova-plugin-contacts/www/ContactError.js b/plugins/cordova-plugin-contacts/www/ContactError.js new file mode 100644 index 0000000..6d4545d --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactError.js @@ -0,0 +1,43 @@ +/* + * + * 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. + * +*/ + +/** + * ContactError. + * An error code assigned by an implementation when an error has occurred + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.OPERATION_CANCELLED_ERROR = 6; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; diff --git a/plugins/cordova-plugin-contacts/www/ContactField.js b/plugins/cordova-plugin-contacts/www/ContactField.js new file mode 100644 index 0000000..e84107a --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactField.js @@ -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. + * +*/ + +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; diff --git a/plugins/cordova-plugin-contacts/www/ContactFieldType.js b/plugins/cordova-plugin-contacts/www/ContactFieldType.js new file mode 100644 index 0000000..8e012de --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactFieldType.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. + * +*/ + + // Possible field names for various platforms. + // Some field names are platform specific + + var fieldType = { + addresses: "addresses", + birthday: "birthday", + categories: "categories", + country: "country", + department: "department", + displayName: "displayName", + emails: "emails", + familyName: "familyName", + formatted: "formatted", + givenName: "givenName", + honorificPrefix: "honorificPrefix", + honorificSuffix: "honorificSuffix", + id: "id", + ims: "ims", + locality: "locality", + middleName: "middleName", + name: "name", + nickname: "nickname", + note: "note", + organizations: "organizations", + phoneNumbers: "phoneNumbers", + photos: "photos", + postalCode: "postalCode", + region: "region", + streetAddress: "streetAddress", + title: "title", + urls: "urls" + }; + + module.exports = fieldType; diff --git a/plugins/cordova-plugin-contacts/www/ContactFindOptions.js b/plugins/cordova-plugin-contacts/www/ContactFindOptions.js new file mode 100644 index 0000000..73a497d --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactFindOptions.js @@ -0,0 +1,38 @@ +/* + * + * 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. + * +*/ + +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + * @param desiredFields + * @param hasPhoneNumber boolean used to filter the search and only return contacts that have a phone number informed + */ + +var ContactFindOptions = function(filter, multiple, desiredFields, hasPhoneNumber) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); + this.desiredFields = typeof desiredFields != 'undefined' ? desiredFields : []; + this.hasPhoneNumber = typeof hasPhoneNumber != 'undefined' ? hasPhoneNumber : false; +}; + +module.exports = ContactFindOptions; diff --git a/plugins/cordova-plugin-contacts/www/ContactName.js b/plugins/cordova-plugin-contacts/www/ContactName.js new file mode 100644 index 0000000..15cf60b --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactName.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. + * +*/ + +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; diff --git a/plugins/cordova-plugin-contacts/www/ContactOrganization.js b/plugins/cordova-plugin-contacts/www/ContactOrganization.js new file mode 100644 index 0000000..fdfefe2 --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ContactOrganization.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. + * +*/ + +/** +* Contact organization. +* @constructor +* @param pref +* @param type +* @param name +* @param dept +* @param title +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; diff --git a/plugins/cordova-plugin-contacts/www/contacts.js b/plugins/cordova-plugin-contacts/www/contacts.js new file mode 100644 index 0000000..178a20a --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/contacts.js @@ -0,0 +1,98 @@ +/* + * + * 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'), + exec = require('cordova/exec'), + ContactError = require('./ContactError'), + utils = require('cordova/utils'), + Contact = require('./Contact'), + fieldType = require('./ContactFieldType'); + + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + fieldType: fieldType, + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + argscheck.checkArgs('afFO', 'contacts.find', arguments); + if (!fields.length) { + errorCB && errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } else { + // missing 'options' param means return all contacts + options = options || {filter: '', multiple: true} + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function picks contact from phone using contact picker UI + * @returns new Contact object + */ + pickContact: function (successCB, errorCB) { + + argscheck.checkArgs('fF', 'contacts.pick', arguments); + + var win = function (result) { + // if Contacts.pickContact return instance of Contact object + // don't create new Contact object, use current + var contact = result instanceof Contact ? result : contacts.create(result); + successCB(contact); + }; + exec(win, errorCB, "Contacts", "pickContact", []); + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object whose properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + argscheck.checkArgs('O', 'contacts.create', arguments); + var contact = new Contact(); + for (var i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; diff --git a/plugins/cordova-plugin-contacts/www/ios/Contact.js b/plugins/cordova-plugin-contacts/www/ios/Contact.js new file mode 100644 index 0000000..b40c41a --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ios/Contact.js @@ -0,0 +1,51 @@ +/* + * + * 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'), + ContactError = require('./ContactError'); + +/** + * Provides iOS Contact.display API. + */ +module.exports = { + display : function(errorCB, options) { + /* + * Display a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + */ + + if (this.id === null) { + if (typeof errorCB === "function") { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + } + else { + exec(null, errorCB, "Contacts","displayContact", [this.id, options]); + } + } +}; diff --git a/plugins/cordova-plugin-contacts/www/ios/contacts.js b/plugins/cordova-plugin-contacts/www/ios/contacts.js new file mode 100644 index 0000000..67cf421 --- /dev/null +++ b/plugins/cordova-plugin-contacts/www/ios/contacts.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'); + +/** + * Provides iOS enhanced contacts API. + */ +module.exports = { + newContactUI : function(successCallback) { + /* + * Create a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * returns: the id of the created contact as param to successCallback + */ + exec(successCallback, null, "Contacts","newContact", []); + }, + chooseContact : function(successCallback, options) { + /* + * Select a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + * fields: array of fields to return in contact object (see ContactOptions.fields) + * + * @returns + * id of contact selected + * ContactObject + * if no fields provided contact contains just id information + * if fields provided contact object contains information for the specified fields + * + */ + var win = function(result) { + var fullContact = require('./contacts').create(result); + successCallback(fullContact.id, fullContact); + }; + exec(win, null, "Contacts","chooseContact", [options]); + } +}; diff --git a/plugins/cordova-plugin-device-motion/CONTRIBUTING.md b/plugins/cordova-plugin-device-motion/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-device-motion/LICENSE b/plugins/cordova-plugin-device-motion/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-device-motion/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/plugins/cordova-plugin-device-motion/NOTICE b/plugins/cordova-plugin-device-motion/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/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/plugins/cordova-plugin-device-motion/README.md b/plugins/cordova-plugin-device-motion/README.md new file mode 100644 index 0000000..2d82667 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/README.md @@ -0,0 +1,185 @@ +--- +title: Device Motion +description: Access accelerometer data. +--- + + +|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI | +|:-:|:-:|:-:|:-:|:-:|:-:| +|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-device-motion)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-device-motion/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-device-motion)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-device-motion/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-device-motion)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-device-motion/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-device-motion)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-device-motion/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-device-motion)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-device-motion/)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device-motion)| + +# cordova-plugin-device-motion + +This plugin provides access to the device's accelerometer. The accelerometer is +a motion sensor that detects the change (_delta_) in movement relative to the +current device orientation, in three dimensions along the _x_, _y_, and _z_ +axis. + +Access is via a global `navigator.accelerometer` object. + +Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + +Report issues with this plugin on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Device%20Motion%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + + cordova plugin add cordova-plugin-device-motion + +## Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Browser +- Firefox OS +- iOS +- Tizen +- Windows Phone 8 +- Windows + +## Methods + +- navigator.accelerometer.getCurrentAcceleration +- navigator.accelerometer.watchAcceleration +- navigator.accelerometer.clearWatch + +## Objects + +- Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Get the current acceleration along the _x_, _y_, and _z_ axes. + +These acceleration values are returned to the `accelerometerSuccess` +callback function. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Example + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + } + + function onError() { + alert('onError!'); + } + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + +### Browser Quirks + +Values for X, Y, Z motion are all randomly generated in order to simulate the accelerometer. + +### Android Quirks + +The accelerometer is called with the `SENSOR_DELAY_UI` flag, which limits the maximum readout frequency to something between 20 and 60 Hz, depending on the device. Values for __period__ corresponding to higher frequencies will result in duplicate samples. More details can be found in the [Android API Guide](http://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-monitor). + + +### iOS Quirks + +- iOS doesn't recognize the concept of getting the current acceleration at any given point. + +- You must watch the acceleration and capture the data at given time intervals. + +- Thus, the `getCurrentAcceleration` function yields the last value reported from a `watchAccelerometer` call. + +## navigator.accelerometer.watchAcceleration + +Retrieves the device's current `Acceleration` at a regular interval, executing +the `accelerometerSuccess` callback function each time. Specify the interval in +milliseconds via the `acceleratorOptions` object's `frequency` parameter. + +The returned watch ID references the accelerometer's watch interval, +and can be used with `navigator.accelerometer.clearWatch` to stop watching the +accelerometer. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + +- __accelerometerOptions__: An object with the following optional keys: + - __frequency__: requested frequency of calls to accelerometerSuccess with acceleration data in Milliseconds. _(Number)_ (Default: 10000) + + +### Example + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + } + + function onError() { + alert('onError!'); + } + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + +### iOS Quirks + +The API calls the success callback function at the interval requested, +but restricts the range of requests to the device between 40ms and +1000ms. For example, if you request an interval of 3 seconds, +(3000ms), the API requests data from the device every 1 second, but +only executes the success callback every 3 seconds. + +## navigator.accelerometer.clearWatch + +Stop watching the `Acceleration` referenced by the `watchID` parameter. + + navigator.accelerometer.clearWatch(watchID); + +- __watchID__: The ID returned by `navigator.accelerometer.watchAcceleration`. + +### Example + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + +## Acceleration + +Contains `Accelerometer` data captured at a specific point in time. +Acceleration values include the effect of gravity (9.81 m/s^2), so that when a +device lies flat and facing up, _x_, _y_, and _z_ values returned should be +`0`, `0`, and `9.81`. + +### Properties + +- __x__: Amount of acceleration on the x-axis. (in m/s^2) _(Number)_ +- __y__: Amount of acceleration on the y-axis. (in m/s^2) _(Number)_ +- __z__: Amount of acceleration on the z-axis. (in m/s^2) _(Number)_ +- __timestamp__: Creation timestamp in milliseconds. _(DOMTimeStamp)_ diff --git a/plugins/cordova-plugin-device-motion/RELEASENOTES.md b/plugins/cordova-plugin-device-motion/RELEASENOTES.md new file mode 100644 index 0000000..20ca53c --- /dev/null +++ b/plugins/cordova-plugin-device-motion/RELEASENOTES.md @@ -0,0 +1,311 @@ + +# Release Notes + +### 1.2.3 (Dec 07, 2016) +* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Updated version and RELEASENOTES.md for release 1.2.2 +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* [CB-11482](https://issues.apache.org/jira/browse/CB-11482) Fix unreliable tests on Android +* [CB-11531](https://issues.apache.org/jira/browse/CB-11531) Restart Accelerometer on CyanogenMod 13 +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-11188](https://issues.apache.org/jira/browse/CB-11188) cordova-plugin-device-motion-tests are failing in CI +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md +* Update README.md +* [CB-11091](https://issues.apache.org/jira/browse/CB-11091) Incremented plugin version. +* Updated version and RELEASENOTES.md for release 1.2.1 +* Minor changes to readme +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins +* chore: edit package.json license to match SPDX id +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Incremented plugin version. +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) linked issues in RELEASENOTES.md +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated version and RELEASENOTES.md for release 1.2.0 +* removed r prefix from tags +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated RELEASENOTES to be newest to oldest +* This closes #37 +* access all accel properties via getters +* Return error when accelerometer not available, skip/pending tests when accel not available, use getters for properties +* revert wp8 commit: 90ef274 +* Close #22 +* Returning an OK PluginResult.Status when starting +* Update README.md +* Added android quirk as written by @notsure13 This closes #23 +* add JIRA issue tracker link +* Actually fixing the contribute link. +* Fixing contribute link. +* [CB-9426](https://issues.apache.org/jira/browse/CB-9426) Fix exception when using device motion plugin on browser platform. +* [CB-9339](https://issues.apache.org/jira/browse/CB-9339) Increase the default sensor accuracy +* remove travis-ci integration +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Incremented plugin version. +* [CB-9202](https://issues.apache.org/jira/browse/CB-9202) updated repo url to github mirror in package.json +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Updated version and RELEASENOTES.md for release 1.1.1 +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* fix npm md issue +* [CB-8842](https://issues.apache.org/jira/browse/CB-8842) Return cached values on Android if there is no updates from sensor +* [CB-8959](https://issues.apache.org/jira/browse/CB-8959) Incremented plugin version. +* [CB-8959](https://issues.apache.org/jira/browse/CB-8959) Updated version and RELEASENOTES.md for release 1.1.0 +* [CB-8926](https://issues.apache.org/jira/browse/CB-8926) The tests module tries to access an undefined global 'Accelerometer' on fail callbacks. This results in another JS error, "ReferenceError: 'Accelerometer' is undefined." This change passes through the error message instead of attempting to index into it. +* [CB-8876](https://issues.apache.org/jira/browse/CB-8876) Introduced a small timeout between tests +* [CB-8876](https://issues.apache.org/jira/browse/CB-8876) Rewrote wp8 impementation to be more stable +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version in package.json for release 1.0.0 +* Revert "CB-8858 Incremented plugin version." +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version and RELEASENOTES.md for release 1.0.0 +* [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) updated windows and tizen specific references of old id to new id +* [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 +* [CB-8312](https://issues.apache.org/jira/browse/CB-8312) Multiply accelerometer values by -g on Windows +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* [CB-8562](https://issues.apache.org/jira/browse/CB-8562) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8096](https://issues.apache.org/jira/browse/CB-8096) Pended recently added spec.12 if accelerometer doesn't exist on the device +* [CB-8096](https://issues.apache.org/jira/browse/CB-8096) Pended auto tests if accelerometer doesn't exist on the device +* [CB-8083](https://issues.apache.org/jira/browse/CB-8083) Adds test to make sure success callback is called each time +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Incremented plugin version. +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Updated version and RELEASENOTES.md for release 0.2.11 +* [CB-8083](https://issues.apache.org/jira/browse/CB-8083) Fix accelerometer callback on Windows +* Renamed windows8 -> windows +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention deviceready in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Incremented plugin version. +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Updated version and RELEASENOTES.md for release 0.2.10 +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* Updated doc for browser +* Added support for the browser +* CB-7249cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* This closes #16 +* [CB-7313](https://issues.apache.org/jira/browse/CB-7313) minor tweak to documentation of watchAcceleration function parameters +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Incremented plugin version. +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Updated version and RELEASENOTES.md for release 0.2.9 +* CB-7249cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* [CB-7160](https://issues.apache.org/jira/browse/CB-7160) move to tests dir, add nested plugin.xml +* Removed js-module for tests from plugin.xml +* [CB-7160](https://issues.apache.org/jira/browse/CB-7160) added manual tests +* update accelerometer.js +* added documentation for manual tests +* Removed js-module for tests from plugin.xml +* [CB-7160](https://issues.apache.org/jira/browse/CB-7160) added manual tests +* Changing cdvtest format to use module exports +* register tests using new style +* update +* Feature Branch: First attempt at new-style-tests +* CB-6127lisa7cordova-plugin-consolecordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Incremented plugin version. +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Updated version and RELEASENOTES.md for release 0.2.8 +* FFOS added to supported platforms +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #10. Github close #12. Github close #11 +* ubuntu: don't destroy callback after use +* [CB-6798](https://issues.apache.org/jira/browse/CB-6798) Add license +* documentation translation: cordova-plugin-device-motion +* Lisa testing pulling in plugins for plugin: cordova-plugin-device-motion +* Lisa testing pulling in plugins for plugin: cordova-plugin-device-motion +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* FFOS added to supported platforms +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Incremented plugin version on dev branch. +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Updated version and RELEASENOTES.md for release 0.2.7 +* [CB-6465](https://issues.apache.org/jira/browse/CB-6465) Add license headers to Tizen code +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460) Update license headers +* Add NOTICE file +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Incremented plugin version on dev branch. +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422) [windows8] use cordova/exec/proxy +* Lisa testing pulling in plugins for plugin: cordova-plugin-device-motion +* Lisa testing pulling in plugins for plugin: cordova-plugin-device-motion +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 0.2.6 +* Add Tizen plugin. +* Delete stale test/ directory +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Incremented plugin version on dev branch. +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Updated version and RELEASENOTES.md for release 0.2.5 +* [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 Device Motion plugin +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Incremented plugin version on dev branch. +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Updated version and RELEASENOTES.md for release 0.2.4 +* add ubuntu platform +* 1. Added amazon-fireos platform. 2. Change to use amazon-fireos as the platform if the user agent string contains 'cordova-amazon-fireos' +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) Updated version and RELEASENOTES.md for release 0.2.3 +* tweak scoping +* fixed the scope +* properly stop watching... +* adding timestamp to the response +* fix acceleromter for firefox os +* update firefoxos integration +* fixed callbacks +* accelerometer registers, but is not responding +* fxos added, not working +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128) added repo + issue tag to plugin.xml for device motion +* [CB-5012](https://issues.apache.org/jira/browse/CB-5012) ensure result is returned +* [CB-4825](https://issues.apache.org/jira/browse/CB-4825) Add CoreMotion.framework to plugin.xml +* [CB-4825](https://issues.apache.org/jira/browse/CB-4825) avoid retain cycle in update block +* [CB-4825](https://issues.apache.org/jira/browse/CB-4825) use CoreMotion framework for accelerometer +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Updated version and RELEASENOTES.md for release 0.2.2 +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [windows8] commandProxy was moved +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming core inside windows8 +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.device-motion to org.apache.cordova.device-motion +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. +* Add empty CHANGELOG.md +* [CB-4595](https://issues.apache.org/jira/browse/CB-4595) updated version +* [CB-4417](https://issues.apache.org/jira/browse/CB-4417) Move cordova-plugin-device-motion to its own Java package. +* updated namespace +* updated nametag and readme +* [plugin.xml] standardizing license + meta +* [license] adding apache license file +* [Windows8][CB-4438] added windows 8 support +* updating plugin.xml with registry data + +### 1.2.2 (Sep 08, 2016) +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* [CB-11482](https://issues.apache.org/jira/browse/CB-11482) Fix unreliable tests on **Android** +* [CB-11531](https://issues.apache.org/jira/browse/CB-11531) Restart `Accelerometer` on `CyanogenMod 13` +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-11188](https://issues.apache.org/jira/browse/CB-11188) `cordova-plugin-device-motion-tests` are failing in CI +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md + +### 1.2.1 (Apr 15, 2016) +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins + +### 1.2.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* access all `accel` properties via getters +* Return error when `accelerometer` not available, skip/pending tests when accel not available, use getters for properties +* Returning an `OK PluginResult.Status` when starting +* Update `README.md` +* Added **Android** quirk +* Fixing contribute link. +* [CB-9426](https://issues.apache.org/jira/browse/CB-9426) Fix exception when using device motion plugin on **browser** platform. +* [CB-9339](https://issues.apache.org/jira/browse/CB-9339) Increase the default sensor accuracy + +### 1.1.1 (Jun 17, 2015) +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* fix npm md issue +* [CB-8842](https://issues.apache.org/jira/browse/CB-8842) Return cached values on Android if there is no updates from sensor + +### 1.1.0 (May 06, 2015) +* [CB-8926](https://issues.apache.org/jira/browse/CB-8926): The tests module tries to access an undefined global `Accelerometer` on fail callbacks. This results in another JS error, `ReferenceError: 'Accelerometer' is undefined.` This change passes through the error message instead of attempting to index into it. +* [CB-8876](https://issues.apache.org/jira/browse/CB-8876) Introduced a small timeout between tests +* [CB-8876](https://issues.apache.org/jira/browse/CB-8876) Rewrote **wp8** impementation to be more stable + +### 1.0.0 (Apr 15, 2015) +* [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) updated windows and tizen specific references of old id to new id +* [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 +* [CB-8312](https://issues.apache.org/jira/browse/CB-8312) Multiply accelerometer values by -g on Windows +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* [CB-8562](https://issues.apache.org/jira/browse/CB-8562) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8096](https://issues.apache.org/jira/browse/CB-8096) Pended recently added spec.12 if accelerometer doesn't exist on the device +* [CB-8096](https://issues.apache.org/jira/browse/CB-8096) Pended auto tests if accelerometer doesn't exist on the device +* [CB-8083](https://issues.apache.org/jira/browse/CB-8083) Adds test to make sure success callback is called each time + +### 0.2.11 (Dec 02, 2014) +* [CB-8083](https://issues.apache.org/jira/browse/CB-8083) Fix `accelerometer` callback on **Windows** +* Renamed **Windows8** -> **Windows** +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin + +### 0.2.10 (Sep 17, 2014) +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-device-motion documentation translation: cordova-plugin-device-motion +* Updated doc for browser +* Added support for the browser +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-device-motion documentation translation +* [CB-7313](https://issues.apache.org/jira/browse/CB-7313) minor tweak to documentation of watchAcceleration function parameters +* [CB-7160](https://issues.apache.org/jira/browse/CB-7160) move to tests dir, add nested plugin.xml +* Removed js-module for tests from plugin.xml +* [CB-7160](https://issues.apache.org/jira/browse/CB-7160) added manual tests +* added documentation for manual tests +* Removed js-module for tests from plugin.xml +* [CB-7160](https://issues.apache.org/jira/browse/CB-7160) added manual tests +* Changing cdvtest format to use module exports +* register tests using new style +* update +* Feature Branch: First attempt at new-style-tests + +### 0.2.9 (Aug 06, 2014) +* [FFOS] update accelerometer.js +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* FFOS added to supported platforms + +### 0.2.8 (Jun 05, 2014) +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #10. Github close #12. Github close #11 +* ubuntu: don't destroy callback after use +* [CB-6798](https://issues.apache.org/jira/browse/CB-6798) Add license +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* FFOS added to supported platforms + +### 0.2.7 (Apr 17, 2014) +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* [CB-6465](https://issues.apache.org/jira/browse/CB-6465): Add license headers to Tizen code +* Add NOTICE file + +### 0.2.6 (Feb 05, 2014) +* Add Tizen support + +### 0.2.5 (Jan 02, 2014) +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Device Motion plugin + +### 0.2.4 (Dec 4, 2013) +* add ubuntu platform +* 1. Added amazon-fireos platform. 2. Change to use amazon-fireos as the platform if the user agent string contains 'cordova-amazon-fireos' + +### 0.2.3 (Oct 28, 2013) +* tweak scoping +* fixed the scope +* properly stop watching... +* adding timestamp to the response +* fix acceleromter for firefox os +* update firefoxos integration +* fixed callbacks +* accelerometer registers, but is not responding +* fxos added, not working +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for device motion +* [CB-5012](https://issues.apache.org/jira/browse/CB-5012) ensure result is returned +* [CB-4825](https://issues.apache.org/jira/browse/CB-4825) Add CoreMotion.framework to plugin.xml +* [CB-4825](https://issues.apache.org/jira/browse/CB-4825) avoid retain cycle in update block +* [CB-4825](https://issues.apache.org/jira/browse/CB-4825) use CoreMotion framework for accelerometer +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.2.2 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [windows8] commandProxy was moved +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming core inside windows8 +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.device-motion to org.apache.cordova.device-motion +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. diff --git a/plugins/cordova-plugin-device-motion/doc/de/README.md b/plugins/cordova-plugin-device-motion/doc/de/README.md new file mode 100644 index 0000000..c8c8fca --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/de/README.md @@ -0,0 +1,163 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +Dieses Plugin ermöglicht den Zugriff auf das Gerät Beschleunigungsmesser. Der Beschleunigungsmesser ist ein Bewegungssensor, der die Änderung (*Delta*) erkennt Bewegung im Verhältnis zu der aktuellen Geräte-Orientierung, in drei Dimensionen entlang der *x-*, *y-*und *Z* -Achse. + +Der Zugang ist über eine globale `navigator.accelerometer`-Objekt. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## Installation + + cordova plugin add cordova-plugin-device-motion + + +## Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS + * iOS + * Tizen + * Windows Phone 8 + * Windows + +## Methoden + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## Objekte + + * Beschleunigung + +## navigator.accelerometer.getCurrentAcceleration + +Erhalten Sie die aktuelle Beschleunigung entlang der *x-*, *y-* und *z*-Achsen. + +Diese Beschleunigungswerte werden an die `accelerometerSuccess`-Callback-Funktion zurückgegeben. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Beispiel + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Browser-Eigenheiten + +Werte für X, Y, Z-Bewegung sind alle zufällig generierten in Ordnung, den Beschleunigungsmesser zu simulieren. + +### iOS Macken + + * iOS erkennt nicht das Konzept die aktuelle Beschleunigung zu einem bestimmten Zeitpunkt zu bekommen. + + * Müssen Sie die Beschleunigung zu sehen und erfassen die Daten zu bestimmten Zeitintervallen. + + * So die `getCurrentAcceleration` -Funktion führt zu den letzten Wert berichtet von einer `watchAccelerometer` rufen. + +## navigator.accelerometer.watchAcceleration + +Ruft das Gerät aktuelle `Accelerometer` in regelmäßigen Abständen, die `accelerometerSuccess`-Callback-Funktion jedes Mal ausgeführt. Gibt das Intervall in Millisekunden über das `AcceleratorOptions`-Objekt-`frequency`-Parameter. + +Die zurückgegebenen Watch-ID verweist der Beschleunigungsmesser Uhr Intervall und kann mit `navigator.accelerometer.clearWatch` um zu stoppen, beobachten den Beschleunigungsmesser verwendet werden. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + + * **accelerometerOptions**: Ein Objekt mit den folgenden optionalen Elementen: + * **Zeitraum**: gewünschten Zeitraum der Aufrufe von AccelerometerSuccess mit Beschleunigungsdaten in Millisekunden. *(Anzahl)* (Default: 10000) + +### Beispiel + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS Macken + +Die API ruft die Erfolg-Callback-Funktion im Intervall angefordert, aber schränkt den Bereich der Anforderungen an das Gerät zwischen 40ms und 1000ms. Beispielsweise wenn Sie ein Intervall von 3 Sekunden, (3000ms), beantragen die API fordert Daten vom Gerät jede 1 Sekunde, aber nur den Erfolg-Rückruf führt alle 3 Sekunden. + +## navigator.accelerometer.clearWatch + +Hör auf, beobachten die `Beschleunigung` durch den `watchID`-Parameter verwiesen. + + navigator.accelerometer.clearWatch(watchID); + + + * **WatchID**: die ID von zurückgegeben`navigator.accelerometer.watchAcceleration`. + +### Beispiel + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Beschleunigung + +Zu einem bestimmten Zeitpunkt im Zeit erfasste `Accelerometer`-Daten enthält. Beschleunigungswerte sind die Auswirkungen der Schwerkraft (9.81 m/s ^ 2), so dass wenn ein Gerät flach und nach oben, *X*, *y liegt*, und *Z*-Werte zurückgegeben werden, ``, `` und `9.81 sollte`. + +### Eigenschaften + + * **X**: Betrag der Beschleunigung auf der x-Achse. (in m/s ^ 2) *(Anzahl)* + * **y**: Betrag der Beschleunigung auf der y-Achse. (in m/s ^ 2) *(Anzahl)* + * **Z**: Betrag der Beschleunigung auf die z-Achse. (in m/s ^ 2) *(Anzahl)* + * **Timestamp**: Zeitstempel der Erstellung in Millisekunden. *(DOMTimeStamp)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/de/index.md b/plugins/cordova-plugin-device-motion/doc/de/index.md new file mode 100644 index 0000000..87efd73 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/de/index.md @@ -0,0 +1,161 @@ + + +# cordova-plugin-device-motion + +Dieses Plugin ermöglicht den Zugriff auf das Gerät Beschleunigungsmesser. Der Beschleunigungsmesser ist ein Bewegungssensor, der die Änderung (*Delta*) erkennt Bewegung im Verhältnis zu der aktuellen Geräte-Orientierung, in drei Dimensionen entlang der *x-*, *y-*und *Z* -Achse. + +Der Zugang ist über eine globale `navigator.accelerometer`-Objekt. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## Installation + + cordova plugin add cordova-plugin-device-motion + + +## Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS +* iOS +* Tizen +* Windows Phone 8 +* Windows + +## Methoden + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## Objekte + +* Beschleunigung + +## navigator.accelerometer.getCurrentAcceleration + +Erhalten Sie die aktuelle Beschleunigung entlang der *x-*, *y-* und *z*-Achsen. + +Diese Beschleunigungswerte werden an die `accelerometerSuccess`-Callback-Funktion zurückgegeben. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Beispiel + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Browser-Eigenheiten + +Werte für X, Y, Z-Bewegung sind alle zufällig generierten in Ordnung, den Beschleunigungsmesser zu simulieren. + +### iOS Macken + +* iOS erkennt nicht das Konzept die aktuelle Beschleunigung zu einem bestimmten Zeitpunkt zu bekommen. + +* Müssen Sie die Beschleunigung zu sehen und erfassen die Daten zu bestimmten Zeitintervallen. + +* So die `getCurrentAcceleration` -Funktion führt zu den letzten Wert berichtet von einer `watchAccelerometer` rufen. + +## navigator.accelerometer.watchAcceleration + +Ruft das Gerät aktuelle `Accelerometer` in regelmäßigen Abständen, die `accelerometerSuccess`-Callback-Funktion jedes Mal ausgeführt. Gibt das Intervall in Millisekunden über das `AcceleratorOptions`-Objekt-`frequency`-Parameter. + +Die zurückgegebenen Watch-ID verweist der Beschleunigungsmesser Uhr Intervall und kann mit `navigator.accelerometer.clearWatch` um zu stoppen, beobachten den Beschleunigungsmesser verwendet werden. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: Ein Objekt mit den folgenden optionalen Elementen: + * **Zeitraum**: gewünschten Zeitraum der Aufrufe von AccelerometerSuccess mit Beschleunigungsdaten in Millisekunden. *(Anzahl)* (Default: 10000) + +### Beispiel + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS Macken + +Die API ruft die Erfolg-Callback-Funktion im Intervall angefordert, aber schränkt den Bereich der Anforderungen an das Gerät zwischen 40ms und 1000ms. Beispielsweise wenn Sie ein Intervall von 3 Sekunden, (3000ms), beantragen die API fordert Daten vom Gerät jede 1 Sekunde, aber nur den Erfolg-Rückruf führt alle 3 Sekunden. + +## navigator.accelerometer.clearWatch + +Hör auf, beobachten die `Beschleunigung` durch den `watchID`-Parameter verwiesen. + + navigator.accelerometer.clearWatch(watchID); + + +* **WatchID**: die ID von zurückgegeben`navigator.accelerometer.watchAcceleration`. + +### Beispiel + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Beschleunigung + +Zu einem bestimmten Zeitpunkt im Zeit erfasste `Accelerometer`-Daten enthält. Beschleunigungswerte sind die Auswirkungen der Schwerkraft (9.81 m/s ^ 2), so dass wenn ein Gerät flach und nach oben, *X*, *y liegt*, und *Z*-Werte zurückgegeben werden, ``, `` und `9.81 sollte`. + +### Eigenschaften + +* **X**: Betrag der Beschleunigung auf der x-Achse. (in m/s ^ 2) *(Anzahl)* +* **y**: Betrag der Beschleunigung auf der y-Achse. (in m/s ^ 2) *(Anzahl)* +* **Z**: Betrag der Beschleunigung auf die z-Achse. (in m/s ^ 2) *(Anzahl)* +* **Timestamp**: Zeitstempel der Erstellung in Millisekunden. *(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/es/README.md b/plugins/cordova-plugin-device-motion/doc/es/README.md new file mode 100644 index 0000000..1d8793d --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/es/README.md @@ -0,0 +1,159 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +Este plugin proporciona acceso a acelerómetro del dispositivo. El acelerómetro es un sensor de movimiento que detecta el cambio (*delta*) en movimiento con respecto a la orientación actual del dispositivo, en tres dimensiones sobre el eje *x*, *y*y *z* . + +El acceso es por un global `navigator.accelerometer` objeto. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(navigator.accelerometer)}; + + +## Instalación + + cordova plugin add cordova-plugin-device-motion + + +## Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Explorador + * Firefox OS + * iOS + * Tizen + * Windows Phone 8 + * Windows + +## Métodos + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## Objetos + + * Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Tienes la aceleración actual a lo largo de los ejes *x*, *y*y *z* . + +Estos valores de aceleración son devueltos a la `accelerometerSuccess` función de callback. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Ejemplo + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Navegador rarezas + +Los valores para X, Y, movimiento Z son todo generada aleatoriamente en orden para simular el acelerómetro. + +### iOS rarezas + + * iOS no reconoce el concepto de conseguir la aceleración actual en cualquier momento dado. + + * Debes ver la aceleración y capturar los datos en determinados intervalos de tiempo. + + * Así, la función de `getCurrentAcceleration` rinde el último valor informado de una llamada de `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Recupera el dispositivo actual de `Acceleration` a intervalos regulares, ejecutar el `accelerometerSuccess` función callback cada vez. Especificar el intervalo en milisegundos mediante la `acceleratorOptions` del objeto `frequency` parámetro. + +El vuelto ver referencias ID intervalo del acelerómetro reloj y puede ser utilizado con `navigator.accelerometer.clearWatch` para dejar de ver el acelerómetro. + + var watchID = navigator.accelerometer.watchAcceleration (accelerometerSuccess, accelerometerError, accelerometerOptions); + + + * **accelerometerOptions**: Un objeto con las llaves opcionales siguientes: + * **periodo**: periodo solicitado de llamadas a accelerometerSuccess con los datos de aceleración en milisegundos. *(Número)* (Por defecto: 10000) + +### Ejemplo + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS rarezas + +La API llama a la función de devolución de llamada de éxito en el intervalo solicitado, pero restringe la gama de solicitudes que el dispositivo entre 40ms y 1000ms. Por ejemplo, si usted solicita un intervalo de 3 segundos, (3000ms), la API solicita datos desde el dispositivo cada 1 segundo, pero sólo ejecuta el callback de éxito cada 3 segundos. + +## navigator.accelerometer.clearWatch + +Dejar de mirar el `Acceleration` referenciado por el `watchID` parámetro. + + navigator.accelerometer.clearWatch(watchID); + + + * **watchID**: el identificador devuelto por`navigator.accelerometer.watchAcceleration`. + +### Ejemplo + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Acceleration + +Contiene `Accelerometer` datos capturados en un punto específico en el tiempo. Valores de aceleración incluyen el efecto de la gravedad (9,81 m/s ^ 2), de modo que cuando se encuentra un dispositivo plano y hacia arriba, *x*, *y*, y *z* valores devueltos deben ser `` , `` , y`9.81`. + +### Propiedades + + * **x**: Cantidad de aceleración en el eje X. (en m/s^2) *(Number)* + * **y**: Cantidad de aceleración en el eje Y. (en m/s^2) *(Number)* + * **z**: Cantidad de aceleración en el eje Z. (en m/s^2) *(Number)* + * **timestamp**: Momento de la captura en milisegundos.*(DOMTimeStamp)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/es/index.md b/plugins/cordova-plugin-device-motion/doc/es/index.md new file mode 100644 index 0000000..5f15dff --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/es/index.md @@ -0,0 +1,139 @@ + + +# cordova-plugin-device-motion + +Este plugin proporciona acceso a acelerómetro del dispositivo. El acelerómetro es un sensor de movimiento que detecta el cambio (*delta*) en movimiento con respecto a la orientación actual del dispositivo, en tres dimensiones sobre el eje *x*, *y*y *z* . + +El acceso es por un global `navigator.accelerometer` objeto. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(navigator.accelerometer)}; + + +## Instalación + + Cordova plugin añade cordova-plugin-device-movimiento + + +## Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Explorador +* Firefox OS +* iOS +* Tizen +* Windows Phone 8 +* Windows + +## Métodos + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## Objetos + +* Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Tienes la aceleración actual a lo largo de los ejes *x*, *y*y *z* . + +Estos valores de aceleración son devueltos a la `accelerometerSuccess` función de callback. + + navigator.accelerometer.getCurrentAcceleration (accelerometerSuccess, accelerometerError); + + +### Ejemplo + + function onSuccess(acceleration) {alert ('Aceleración X:' + acceleration.x + '\n' + 'Aceleración Y:' + acceleration.y + '\n' + 'Aceleración Z:' + acceleration.z + '\n' + ' Timestamp: ' + acceleration.timestamp + '\n');}; + + función onError() {alert('onError!');}; + + navigator.accelerometer.getCurrentAcceleration (onSuccess, onError); + + +### Navegador rarezas + +Los valores para X, Y, movimiento Z son todo generada aleatoriamente en orden para simular el acelerómetro. + +### iOS rarezas + +* iOS no reconoce el concepto de conseguir la aceleración actual en cualquier momento dado. + +* Debes ver la aceleración y capturar los datos en determinados intervalos de tiempo. + +* Así, la función de `getCurrentAcceleration` rinde el último valor informado de una llamada de `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Recupera el dispositivo actual de `Acceleration` a intervalos regulares, ejecutar el `accelerometerSuccess` función callback cada vez. Especificar el intervalo en milisegundos mediante la `acceleratorOptions` del objeto `frequency` parámetro. + +El vuelto ver referencias ID intervalo del acelerómetro reloj y puede ser utilizado con `navigator.accelerometer.clearWatch` para dejar de ver el acelerómetro. + + var watchID = navigator.accelerometer.watchAcceleration (accelerometerSuccess, accelerometerError, accelerometerOptions); + + +* **accelerometerOptions**: Un objeto con las llaves opcionales siguientes: + * **periodo**: periodo solicitado de llamadas a accelerometerSuccess con los datos de aceleración en milisegundos. *(Número)* (Por defecto: 10000) + +### Ejemplo + + function onSuccess(acceleration) {alert ('Aceleración X:' + acceleration.x + '\n' + 'Aceleración Y:' + acceleration.y + '\n' + 'Aceleración Z:' + acceleration.z + '\n' + ' Timestamp: ' + acceleration.timestamp + '\n');}; + + función onError() {alert('onError!');}; + + var opciones = { frequency: 3000 }; Actualizar cada 3 segundos var watchID = navigator.accelerometer.watchAcceleration (onSuccess, onError, opciones); + + +### iOS rarezas + +La API llama a la función de devolución de llamada de éxito en el intervalo solicitado, pero restringe la gama de solicitudes que el dispositivo entre 40ms y 1000ms. Por ejemplo, si usted solicita un intervalo de 3 segundos, (3000ms), la API solicita datos desde el dispositivo cada 1 segundo, pero sólo ejecuta el callback de éxito cada 3 segundos. + +## navigator.accelerometer.clearWatch + +Dejar de mirar el `Acceleration` referenciado por el `watchID` parámetro. + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: el identificador devuelto por`navigator.accelerometer.watchAcceleration`. + +### Ejemplo + + var watchID = navigator.accelerometer.watchAcceleration (onSuccess, onError, opciones); + + ... adelante... navigator.accelerometer.clearWatch(watchID); + + +## Acceleration + +Contiene `Accelerometer` datos capturados en un punto específico en el tiempo. Valores de aceleración incluyen el efecto de la gravedad (9,81 m/s ^ 2), de modo que cuando se encuentra un dispositivo plano y hacia arriba, *x*, *y*, y *z* valores devueltos deben ser `` , `` , y`9.81`. + +### Propiedades + +* **x**: Cantidad de aceleración en el eje X. (en m/s^2) *(Number)* +* **y**: Cantidad de aceleración en el eje Y. (en m/s^2) *(Number)* +* **z**: Cantidad de aceleración en el eje Z. (en m/s^2) *(Number)* +* **timestamp**: Momento de la captura en milisegundos.*(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/fr/README.md b/plugins/cordova-plugin-device-motion/doc/fr/README.md new file mode 100644 index 0000000..82f2488 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/fr/README.md @@ -0,0 +1,141 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +Ce plugin permet d'accéder à l'accéléromètre de l'appareil. L'accéléromètre est un capteur de mouvement qui détecte la modification (*delta*) en mouvement par rapport à l'orientation actuelle de l'appareil, en trois dimensions le long de l'axe *x*, *y*et *z* . + +Accès se fait par un global `navigator.accelerometer` objet. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.accelerometer);} + + +## Installation + + cordova plugin add cordova-plugin-device-motion + + +## Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Navigateur + * Firefox OS + * iOS + * Paciarelli + * Windows Phone 8 + * Windows + +## Méthodes + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## Objets + + * Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Obtenir l'accélération courante le long des axes *x*, *y*et *z* . + +Ces valeurs d'accélération sont retournés à la `accelerometerSuccess` fonction de rappel. + + navigator.accelerometer.getCurrentAcceleration (accelerometerSuccess, accelerometerError) ; + + +### Exemple + + function onSuccess(acceleration) {alert ("Accélération X:" + acceleration.x + « \n » + "Accélération Y:" + acceleration.y + « \n » + « Accélération Z: » + acceleration.z + « \n » + ' Timestamp: "+ acceleration.timestamp + « \n »);} ; + + fonction onError() {alert('onError!');} ; + + navigator.accelerometer.getCurrentAcceleration (onSuccess, onError) ; + + +### Bizarreries navigateur + +Les valeurs x, Y, motion de Z sont tous ordre généré de manière aléatoire dans pour simuler l'accéléromètre. + +### Notes au sujet d'iOS + + * iOS ne permet pas d'obtenir l'accélération en cours à un instant donné. + + * Vous devez observer l'accélération et capturer ses données à un intervalle de temps donné. + + * De ce fait, la fonction `getCurrentAcceleration` renvoie la dernière valeur retournée par un appel à `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Récupère le dispositif actuel de `Acceleration` à intervalle régulier, l'exécution de la `accelerometerSuccess` fonction de rappel chaque fois. Spécifiez l'intervalle, en millisecondes, via le `acceleratorOptions` de l'objet `frequency` paramètre. + +Le retourné regarder ID références intervalle de surveillance de l'accéléromètre et peut être utilisé avec `navigator.accelerometer.clearWatch` d'arrêter de regarder l'accéléromètre. + + var watchID = navigator.accelerometer.watchAcceleration (accelerometerSuccess, accelerometerError, accelerometerOptions) ; + + + * **accelerometerOptions**: Un objet avec les clés facultatives suivantes : + * **période**: période demandée d'appels à accelerometerSuccess avec les données d'accélération en millisecondes. *(Nombre)* (Par défaut : 10000) + +### Exemple + + function onSuccess(acceleration) {alert ("Accélération X:" + acceleration.x + « \n » + "Accélération Y:" + acceleration.y + « \n » + « Accélération Z: » + acceleration.z + « \n » + ' Timestamp: "+ acceleration.timestamp + « \n »);} ; + + fonction onError() {alert('onError!');} ; + + options de var = { frequency: 3000 } ; Mise à jour chaque 3 secondes var watchID = navigator.accelerometer.watchAcceleration (onSuccess, onError, options) ; + + +### Notes au sujet d'iOS + +L'API appelle la fonction de rappel de succès à l'intervalle demandé, mais restreint l'éventail des demandes à l'appareil entre 40ms et 1000ms. Par exemple, si vous demandez un intervalle de 3 secondes, (3000ms), l'API demande des données de l'appareil toutes les 1 seconde, mais seulement exécute le rappel réussi toutes les 3 secondes. + +## navigator.accelerometer.clearWatch + +Arrêter de regarder le `Acceleration` référencé par le `watchID` paramètre. + + navigator.accelerometer.clearWatch(watchID) ; + + + * **watchID**: l'ID retourné par`navigator.accelerometer.watchAcceleration`. + +### Exemple + + var watchID = navigator.accelerometer.watchAcceleration (onSuccess, onError, options) ; + + ... plus tard... navigator.accelerometer.clearWatch(watchID) ; + + +## Acceleration + +Contient `Accelerometer` données capturées à un point précis dans le temps. Valeurs d'accélération comprennent l'effet de la pesanteur (9,81 m/s ^ 2), de sorte que lorsqu'un périphérique se trouve plat et face vers le haut, *x*, *y*, et *z* valeurs retournées doivent être `` , `` , et`9.81`. + +### Propriétés + + * **x**: Valeur de l'accélération sur l'axe des x. (en m/s^2) *(Number)* + * **y**: Valeur de l'accélération sur l'axe des y. (en m/s^2) *(Number)* + * **y**: Valeur de l'accélération sur l'axe des z. (en m/s^2) *(Number)* + * **timestamp**: Date de création en millisecondes. *(DOMTimeStamp)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/fr/index.md b/plugins/cordova-plugin-device-motion/doc/fr/index.md new file mode 100644 index 0000000..880543f --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/fr/index.md @@ -0,0 +1,139 @@ + + +# cordova-plugin-device-motion + +Ce plugin permet d'accéder à l'accéléromètre de l'appareil. L'accéléromètre est un capteur de mouvement qui détecte la modification (*delta*) en mouvement par rapport à l'orientation actuelle de l'appareil, en trois dimensions le long de l'axe *x*, *y*et *z* . + +Accès se fait par un global `navigator.accelerometer` objet. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.accelerometer);} + + +## Installation + + Cordova plugin ajouter cordova-plugin-device-motion + + +## Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Navigateur +* Firefox OS +* iOS +* Paciarelli +* Windows Phone 8 +* Windows + +## Méthodes + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## Objets + +* Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Obtenir l'accélération courante le long des axes *x*, *y*et *z* . + +Ces valeurs d'accélération sont retournés à la `accelerometerSuccess` fonction de rappel. + + navigator.accelerometer.getCurrentAcceleration (accelerometerSuccess, accelerometerError) ; + + +### Exemple + + function onSuccess(acceleration) {alert ("Accélération X:" + acceleration.x + « \n » + "Accélération Y:" + acceleration.y + « \n » + « Accélération Z: » + acceleration.z + « \n » + ' Timestamp: "+ acceleration.timestamp + « \n »);} ; + + fonction onError() {alert('onError!');} ; + + navigator.accelerometer.getCurrentAcceleration (onSuccess, onError) ; + + +### Bizarreries navigateur + +Les valeurs x, Y, motion de Z sont tous ordre généré de manière aléatoire dans pour simuler l'accéléromètre. + +### iOS Quirks + +* iOS ne permet pas d'obtenir l'accélération en cours à un instant donné. + +* Vous devez observer l'accélération et capturer ses données à un intervalle de temps donné. + +* De ce fait, la fonction `getCurrentAcceleration` renvoie la dernière valeur retournée par un appel à `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Récupère le dispositif actuel de `Acceleration` à intervalle régulier, l'exécution de la `accelerometerSuccess` fonction de rappel chaque fois. Spécifiez l'intervalle, en millisecondes, via le `acceleratorOptions` de l'objet `frequency` paramètre. + +Le retourné regarder ID références intervalle de surveillance de l'accéléromètre et peut être utilisé avec `navigator.accelerometer.clearWatch` d'arrêter de regarder l'accéléromètre. + + var watchID = navigator.accelerometer.watchAcceleration (accelerometerSuccess, accelerometerError, accelerometerOptions) ; + + +* **accelerometerOptions**: Un objet avec les clés facultatives suivantes : + * **période**: période demandée d'appels à accelerometerSuccess avec les données d'accélération en millisecondes. *(Nombre)* (Par défaut : 10000) + +### Exemple + + function onSuccess(acceleration) {alert ("Accélération X:" + acceleration.x + « \n » + "Accélération Y:" + acceleration.y + « \n » + « Accélération Z: » + acceleration.z + « \n » + ' Timestamp: "+ acceleration.timestamp + « \n »);} ; + + fonction onError() {alert('onError!');} ; + + options de var = { frequency: 3000 } ; Mise à jour chaque 3 secondes var watchID = navigator.accelerometer.watchAcceleration (onSuccess, onError, options) ; + + +### iOS Quirks + +L'API appelle la fonction de rappel de succès à l'intervalle demandé, mais restreint l'éventail des demandes à l'appareil entre 40ms et 1000ms. Par exemple, si vous demandez un intervalle de 3 secondes, (3000ms), l'API demande des données de l'appareil toutes les 1 seconde, mais seulement exécute le rappel réussi toutes les 3 secondes. + +## navigator.accelerometer.clearWatch + +Arrêter de regarder le `Acceleration` référencé par le `watchID` paramètre. + + navigator.accelerometer.clearWatch(watchID) ; + + +* **watchID**: l'ID retourné par`navigator.accelerometer.watchAcceleration`. + +### Exemple + + var watchID = navigator.accelerometer.watchAcceleration (onSuccess, onError, options) ; + + ... plus tard... navigator.accelerometer.clearWatch(watchID) ; + + +## Accélération + +Contient `Accelerometer` données capturées à un point précis dans le temps. Valeurs d'accélération comprennent l'effet de la pesanteur (9,81 m/s ^ 2), de sorte que lorsqu'un périphérique se trouve plat et face vers le haut, *x*, *y*, et *z* valeurs retournées doivent être `` , `` , et`9.81`. + +### Propriétés + +* **x**: Valeur de l'accélération sur l'axe des x. (en m/s^2) *(Number)* +* **y**: Valeur de l'accélération sur l'axe des y. (en m/s^2) *(Number)* +* **y**: Valeur de l'accélération sur l'axe des z. (en m/s^2) *(Number)* +* **timestamp**: Date de création en millisecondes. *(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/it/README.md b/plugins/cordova-plugin-device-motion/doc/it/README.md new file mode 100644 index 0000000..b350c45 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/it/README.md @@ -0,0 +1,163 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +Questo plugin consente di accedere all'accelerometro del dispositivo. L'accelerometro è un sensore di movimento che rileva il cambiamento (*delta*) nel movimento relativo l'orientamento corrente del dispositivo, in tre dimensioni lungo l'asse *x*, *y*e *z* . + +L'accesso avviene tramite un oggetto globale `navigator.accelerometer`. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## Installazione + + cordova plugin add cordova-plugin-device-motion + + +## Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS + * iOS + * Tizen + * Windows Phone 8 + * Windows + +## Metodi + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## Oggetti + + * Accelerazione + +## navigator.accelerometer.getCurrentAcceleration + +Ottenere l'attuale accelerazione lungo gli assi *x*, *y* e *z*. + +I valori di accelerazione vengono restituiti alla funzione di callback `accelerometerSuccess`. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Esempio + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Stranezze browser + +I valori per X, Y, movimento Z sono tutti generati casualmente in ordine per simulare l'accelerometro. + +### iOS stranezze + + * iOS non riconosce il concetto di ottenere l'accelerazione della corrente in un dato punto. + + * Si deve guardare l'accelerazione e acquisire i dati di intervalli di tempo dato. + + * Così, il `getCurrentAcceleration` funzione restituisce l'ultimo valore segnalato da un `watchAccelerometer` chiamare. + +## navigator.accelerometer.watchAcceleration + +Recupera corrente del dispositivo `Acceleration` a intervalli regolari, l'esecuzione della funzione di callback `accelerometerSuccess` ogni volta. Specificare l'intervallo in millisecondi tramite parametro `frequency` dell'oggetto `acceleratorOptions`. + +L'orologio restituito ID fa riferimento intervallo orologio di accelerometro e può essere utilizzato con `navigator.accelerometer.clearWatch` a smettere di guardare l'accelerometro. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + + * **accelerometerOptions**: Un oggetto con le seguenti chiavi opzionali: + * **periodo**: periodo richiesto di chiamate a accelerometerSuccess con i dati di accelerazione in millisecondi. *(Numero)* (Default: 10000) + +### Esempio + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS stranezze + +L'API chiama la funzione di callback di successo nell'intervallo richiesto, ma limita la gamma di richieste alla periferica tra 40ms e 1000ms. Ad esempio, se si richiede un intervallo di 3 secondi, (3000ms), l'API richiede i dati dal dispositivo ogni secondo, ma esegue solo il callback di successo ogni 3 secondi. + +## navigator.accelerometer.clearWatch + +Smettere di guardare l' `Acceleration` a cui fa riferimento il parametro `watchID`. + + navigator.accelerometer.clearWatch(watchID); + + + * **watchID**: l'ID restituito da`navigator.accelerometer.watchAcceleration`. + +### Esempio + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Accelerazione + +Contiene i dati dell'`Accelerometer` catturati in un punto specifico nel tempo. I valori di accelerazione includono l'effetto della gravità (9,81 m/s ^ 2), in modo che quando un dispositivo si trova piatta e rivolto in su, *x*, *y*, e *z* valori restituiti dovrebbero essere ``, `` e `9,81`. + +### Proprietà + + * **x**: quantità di accelerazione sull'asse x. (in m/s ^ 2) *(Numero)* + * **y**: quantità di accelerazione sull'asse y. (in m/s ^ 2) *(Numero)* + * **z**: quantità di accelerazione sull'asse z. (in m/s ^ 2) *(Numero)* + * **timestamp**: creazione timestamp in millisecondi. *(DOMTimeStamp)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/it/index.md b/plugins/cordova-plugin-device-motion/doc/it/index.md new file mode 100644 index 0000000..c81cfe0 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/it/index.md @@ -0,0 +1,161 @@ + + +# cordova-plugin-device-motion + +Questo plugin consente di accedere all'accelerometro del dispositivo. L'accelerometro è un sensore di movimento che rileva il cambiamento (*delta*) nel movimento relativo l'orientamento corrente del dispositivo, in tre dimensioni lungo l'asse *x*, *y*e *z* . + +L'accesso avviene tramite un oggetto globale `navigator.accelerometer`. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## Installazione + + cordova plugin add cordova-plugin-device-motion + + +## Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS +* iOS +* Tizen +* Windows Phone 8 +* Windows + +## Metodi + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## Oggetti + +* Accelerazione + +## navigator.accelerometer.getCurrentAcceleration + +Ottenere l'attuale accelerazione lungo gli assi *x*, *y* e *z*. + +I valori di accelerazione vengono restituiti alla funzione di callback `accelerometerSuccess`. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Esempio + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Stranezze Browser + +I valori per X, Y, movimento Z sono tutti generati casualmente in ordine per simulare l'accelerometro. + +### iOS stranezze + +* iOS non riconosce il concetto di ottenere l'accelerazione della corrente in un dato punto. + +* Si deve guardare l'accelerazione e acquisire i dati di intervalli di tempo dato. + +* Così, il `getCurrentAcceleration` funzione restituisce l'ultimo valore segnalato da un `watchAccelerometer` chiamare. + +## navigator.accelerometer.watchAcceleration + +Recupera corrente del dispositivo `Acceleration` a intervalli regolari, l'esecuzione della funzione di callback `accelerometerSuccess` ogni volta. Specificare l'intervallo in millisecondi tramite parametro `frequency` dell'oggetto `acceleratorOptions`. + +L'orologio restituito ID fa riferimento intervallo orologio di accelerometro e può essere utilizzato con `navigator.accelerometer.clearWatch` a smettere di guardare l'accelerometro. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: Un oggetto con le seguenti chiavi opzionali: + * **periodo**: periodo richiesto di chiamate a accelerometerSuccess con i dati di accelerazione in millisecondi. *(Numero)* (Default: 10000) + +### Esempio + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS stranezze + +L'API chiama la funzione di callback di successo nell'intervallo richiesto, ma limita la gamma di richieste alla periferica tra 40ms e 1000ms. Ad esempio, se si richiede un intervallo di 3 secondi, (3000ms), l'API richiede i dati dal dispositivo ogni secondo, ma esegue solo il callback di successo ogni 3 secondi. + +## navigator.accelerometer.clearWatch + +Smettere di guardare l' `Acceleration` a cui fa riferimento il parametro `watchID`. + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: l'ID restituito da`navigator.accelerometer.watchAcceleration`. + +### Esempio + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Accelerazione + +Contiene i dati dell'`Accelerometer` catturati in un punto specifico nel tempo. I valori di accelerazione includono l'effetto della gravità (9,81 m/s ^ 2), in modo che quando un dispositivo si trova piatta e rivolto in su, *x*, *y*, e *z* valori restituiti dovrebbero essere ``, `` e `9,81`. + +### Proprietà + +* **x**: quantità di accelerazione sull'asse x. (in m/s ^ 2) *(Numero)* +* **y**: quantità di accelerazione sull'asse y. (in m/s ^ 2) *(Numero)* +* **z**: quantità di accelerazione sull'asse z. (in m/s ^ 2) *(Numero)* +* **timestamp**: creazione timestamp in millisecondi. *(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/ja/README.md b/plugins/cordova-plugin-device-motion/doc/ja/README.md new file mode 100644 index 0000000..d6c66e7 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/ja/README.md @@ -0,0 +1,163 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®åŠ é€Ÿåº¦è¨ˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã—ã¾ã™ã€‚ 加速度計ã®ç¾åœ¨ã®ãƒ‡ãƒã‚¤ã‚¹ã®å‘ã〠*x* *y*ã€ãŠã‚ˆã³*z*è»¸ã«æ²¿ã£ã¦ 3 ã¤ã®æ¬¡å…ƒã®ç›¸å¯¾é‹å‹•ã®å¤‰æ›´ (*デルタ*) を検出ã™ã‚‹ãƒ¢ãƒ¼ã‚·ãƒ§ãƒ³ センサーã§ã™ã€‚ + +アクセスã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.accelerometer` オブジェクトを介ã—ã¦ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## インストール + + cordova plugin add cordova-plugin-device-motion + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * ブラウザー + * Firefox ã® OS + * iOS + * Tizen + * Windows Phone 8 + * Windows + +## メソッド + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## オブジェクト + + * 加速 + +## navigator.accelerometer.getCurrentAcceleration + +*X* *y*ã€ãŠã‚ˆã³ *z* è»¸ã«æ²¿ã£ã¦ç¾åœ¨ã®åŠ é€Ÿã‚’å–å¾—ã—ã¾ã™ã€‚ + +ã“れらã®åŠ é€Ÿåº¦å€¤ãŒ `accelerometerSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«è¿”ã•れã¾ã™ã€‚ + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### 例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### ブラウザーã®ç™– + +値 Xã€Yã€Z モーションã¯ã€åŠ é€Ÿåº¦è¨ˆã‚’ã‚·ãƒŸãƒ¥ãƒ¬ãƒ¼ãƒˆã™ã‚‹ãŸã‚ã«ã™ã¹ã¦ã®ãƒ©ãƒ³ãƒ€ãƒ ã«ç”Ÿæˆã•れる順åºã§ã™ã€‚ + +### iOS ã®ç™– + + * iOS ã¯ã€ä»»æ„ã®æ™‚点ã§ç¾åœ¨ã®åŠ é€Ÿåº¦ã‚’å¾—ã‚‹ã“ã¨ã®æ¦‚念をèªè­˜ã—ã¾ã›ã‚“。 + + * 加速度を見るã—ã€ãƒ‡ãƒ¼ã‚¿ã‚’キャプãƒãƒ£ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™æŒ‡å®šã—ãŸæ™‚é–“é–“éš”ã§ã€‚ + + * ã—ãŸãŒã£ã¦ã€ `getCurrentAcceleration` 関数ã‹ã‚‰å ±å‘Šã•ã‚ŒãŸæœ€å¾Œã®å€¤ãŒç”Ÿæˆã•れã¾ã™ã€ `watchAccelerometer` を呼ã³å‡ºã—ã¾ã™ã€‚ + +## navigator.accelerometer.watchAcceleration + +ãŸã³ã« `accelerometerSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã‚’実行ã™ã‚‹å®šæœŸçš„ãªé–“éš”ã§ã€ãƒ‡ãƒã‚¤ã‚¹ã®ç¾åœ¨ã® `Acceleration` ã‚’å–å¾—ã—ã¾ã™ã€‚ `acceleratorOptions` オブジェクト㮠`frquency` パラメーターを介ã—ã¦ãƒŸãƒªç§’å˜ä½ã§é–“隔を指定ã—ã¾ã™ã€‚ + +è¿”ã•れる時計 IDã€åŠ é€Ÿåº¦è¨ˆè…•æ™‚è¨ˆé–“éš”ã‚’å‚ç…§ã—ã€åŠ é€Ÿåº¦è¨ˆã‚’è¦‹ã¦åœæ­¢ã™ã‚‹ `navigator.accelerometer.clearWatch` を使用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + + * **accelerometerOptions**: 次ã®ã‚ªãƒ—ションã®ã‚­ãƒ¼ã‚’æŒã¤ã‚ªãƒ–ジェクト: + * **期間**: ミリ秒å˜ä½ã§ã®åŠ é€Ÿåº¦ãƒ‡ãƒ¼ã‚¿ã¨ accelerometerSuccess ã¸ã®å‘¼ã³å‡ºã—ã®è¦æ±‚ã•ã‚ŒãŸæœŸé–“。*(æ•°)*(デフォルト: 10000) + +### 例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS ã®ç™– + +API ã¯ã€è¦æ±‚ã•れãŸé–“éš”ã§ã€æˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯é–¢æ•°ã‚’呼ã³å‡ºã—ã¾ã™ãŒ 40 ms ã®é–“デãƒã‚¤ã‚¹ã¸ã®è¦æ±‚ã®ç¯„囲を制é™ã—ã€1000 ミリ秒ã«ãªã‚Šã¾ã™ã€‚ ãŸã¨ãˆã°ã€(ms) 3 ç§’ã®é–“éš”ã‚’è¦æ±‚ã—ãŸå ´åˆã€API 1 ç§’ã”ã¨ã«ã€ãƒ‡ãƒã‚¤ã‚¹ã‹ã‚‰ãƒ‡ãƒ¼ã‚¿ã‚’è¦æ±‚ãŒã®ã¿æˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ 3 ç§’ã”ã¨ã‚’実行ã—ã¾ã™ã€‚ + +## navigator.accelerometer.clearWatch + +`watchID` パラメーターã«ã‚ˆã£ã¦å‚ç…§ã•れる `加速` を見ã¦åœæ­¢ã—ã¾ã™ã€‚ + + navigator.accelerometer.clearWatch(watchID); + + + * **watchID**: ã«ã‚ˆã£ã¦è¿”ã•れる ID`navigator.accelerometer.watchAcceleration`. + +### 例 + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## 加速 + +ç‰¹å®šã®æ™‚点ã§ã‚­ãƒ£ãƒ—ãƒãƒ£ã—㟠`Accelerometer` データãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ 加速度値ã®ã¨ãŠã‚Šé‡åŠ›ã®åŠ¹æžœ (9.81 m/s ^2) デãƒã‚¤ã‚¹ã«ã‚るフラット㨠*x* *y*ã€ç›´é¢ã—ã¦ã„ã‚‹ã¨è¿”ã•れ㟠*z* 値㯠`` ``ã€ãŠã‚ˆã³ `9.81` ã‚’ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã‚ˆã†ã«ã€. + +### プロパティ + + * **x**: x 軸ã®åŠ é€Ÿåº¦ã®é‡ã§ã™ã€‚(m/s ^2)*(æ•°)* + * **y**: y 軸ã®åŠ é€Ÿåº¦ã®é‡ã§ã™ã€‚(m/s ^2)*(æ•°)* + * **z**: z 軸ã®åŠ é€Ÿåº¦ã®é‡ã§ã™ã€‚(m/s ^2)*(æ•°)* + * **タイムスタンプ**: ä½œæˆæ™‚ã®ã‚¿ã‚¤ãƒ ã‚¹ã‚¿ãƒ³ãƒ— (ミリ秒å˜ä½)。*(,)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/ja/index.md b/plugins/cordova-plugin-device-motion/doc/ja/index.md new file mode 100644 index 0000000..4dc0289 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/ja/index.md @@ -0,0 +1,161 @@ + + +# cordova-plugin-device-motion + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®åŠ é€Ÿåº¦è¨ˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã—ã¾ã™ã€‚ 加速度計ã®ç¾åœ¨ã®ãƒ‡ãƒã‚¤ã‚¹ã®å‘ã〠*x* *y*ã€ãŠã‚ˆã³*z*è»¸ã«æ²¿ã£ã¦ 3 ã¤ã®æ¬¡å…ƒã®ç›¸å¯¾é‹å‹•ã®å¤‰æ›´ (*デルタ*) を検出ã™ã‚‹ãƒ¢ãƒ¼ã‚·ãƒ§ãƒ³ センサーã§ã™ã€‚ + +アクセスã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.accelerometer` オブジェクトを介ã—ã¦ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## インストール + + cordova plugin add cordova-plugin-device-motion + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* ブラウザー +* Firefox ã® OS +* iOS +* Tizen +* Windows Phone 8 +* Windows + +## メソッド + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## オブジェクト + +* 加速 + +## navigator.accelerometer.getCurrentAcceleration + +*X* *y*ã€ãŠã‚ˆã³ *z* è»¸ã«æ²¿ã£ã¦ç¾åœ¨ã®åŠ é€Ÿã‚’å–å¾—ã—ã¾ã™ã€‚ + +ã“れらã®åŠ é€Ÿåº¦å€¤ãŒ `accelerometerSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«è¿”ã•れã¾ã™ã€‚ + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### 例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### ブラウザーã®ç™– + +値 Xã€Yã€Z モーションã¯ã€åŠ é€Ÿåº¦è¨ˆã‚’ã‚·ãƒŸãƒ¥ãƒ¬ãƒ¼ãƒˆã™ã‚‹ãŸã‚ã«ã™ã¹ã¦ã®ãƒ©ãƒ³ãƒ€ãƒ ã«ç”Ÿæˆã•れる順åºã§ã™ã€‚ + +### iOS ã®ç™– + +* iOS ã¯ã€ä»»æ„ã®æ™‚点ã§ç¾åœ¨ã®åŠ é€Ÿåº¦ã‚’å¾—ã‚‹ã“ã¨ã®æ¦‚念をèªè­˜ã—ã¾ã›ã‚“。 + +* 加速度を見るã—ã€ãƒ‡ãƒ¼ã‚¿ã‚’キャプãƒãƒ£ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™æŒ‡å®šã—ãŸæ™‚é–“é–“éš”ã§ã€‚ + +* ã—ãŸãŒã£ã¦ã€ `getCurrentAcceleration` 関数ã‹ã‚‰å ±å‘Šã•ã‚ŒãŸæœ€å¾Œã®å€¤ãŒç”Ÿæˆã•れã¾ã™ã€ `watchAccelerometer` を呼ã³å‡ºã—ã¾ã™ã€‚ + +## navigator.accelerometer.watchAcceleration + +ãŸã³ã« `accelerometerSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã‚’実行ã™ã‚‹å®šæœŸçš„ãªé–“éš”ã§ã€ãƒ‡ãƒã‚¤ã‚¹ã®ç¾åœ¨ã® `Acceleration` ã‚’å–å¾—ã—ã¾ã™ã€‚ `acceleratorOptions` オブジェクト㮠`frquency` パラメーターを介ã—ã¦ãƒŸãƒªç§’å˜ä½ã§é–“隔を指定ã—ã¾ã™ã€‚ + +è¿”ã•れる時計 IDã€åŠ é€Ÿåº¦è¨ˆè…•æ™‚è¨ˆé–“éš”ã‚’å‚ç…§ã—ã€åŠ é€Ÿåº¦è¨ˆã‚’è¦‹ã¦åœæ­¢ã™ã‚‹ `navigator.accelerometer.clearWatch` を使用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: 次ã®ã‚ªãƒ—ションã®ã‚­ãƒ¼ã‚’æŒã¤ã‚ªãƒ–ジェクト: + * **期間**: ミリ秒å˜ä½ã§ã®åŠ é€Ÿåº¦ãƒ‡ãƒ¼ã‚¿ã¨ accelerometerSuccess ã¸ã®å‘¼ã³å‡ºã—ã®è¦æ±‚ã•ã‚ŒãŸæœŸé–“。*(æ•°)*(デフォルト: 10000) + +### 例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS ã®ç™– + +API ã¯ã€è¦æ±‚ã•れãŸé–“éš”ã§ã€æˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯é–¢æ•°ã‚’呼ã³å‡ºã—ã¾ã™ãŒ 40 ms ã®é–“デãƒã‚¤ã‚¹ã¸ã®è¦æ±‚ã®ç¯„囲を制é™ã—ã€1000 ミリ秒ã«ãªã‚Šã¾ã™ã€‚ ãŸã¨ãˆã°ã€(ms) 3 ç§’ã®é–“éš”ã‚’è¦æ±‚ã—ãŸå ´åˆã€API 1 ç§’ã”ã¨ã«ã€ãƒ‡ãƒã‚¤ã‚¹ã‹ã‚‰ãƒ‡ãƒ¼ã‚¿ã‚’è¦æ±‚ãŒã®ã¿æˆåŠŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ 3 ç§’ã”ã¨ã‚’実行ã—ã¾ã™ã€‚ + +## navigator.accelerometer.clearWatch + +`watchID` パラメーターã«ã‚ˆã£ã¦å‚ç…§ã•れる `加速` を見ã¦åœæ­¢ã—ã¾ã™ã€‚ + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: ã«ã‚ˆã£ã¦è¿”ã•れる ID`navigator.accelerometer.watchAcceleration`. + +### 例 + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## 加速 + +ç‰¹å®šã®æ™‚点ã§ã‚­ãƒ£ãƒ—ãƒãƒ£ã—㟠`Accelerometer` データãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ 加速度値ã®ã¨ãŠã‚Šé‡åŠ›ã®åŠ¹æžœ (9.81 m/s ^2) デãƒã‚¤ã‚¹ã«ã‚るフラット㨠*x* *y*ã€ç›´é¢ã—ã¦ã„ã‚‹ã¨è¿”ã•れ㟠*z* 値㯠`` ``ã€ãŠã‚ˆã³ `9.81` ã‚’ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã‚ˆã†ã«ã€. + +### プロパティ + +* **x**: x 軸ã®åŠ é€Ÿåº¦ã®é‡ã§ã™ã€‚(m/s ^2)*(æ•°)* +* **y**: y 軸ã®åŠ é€Ÿåº¦ã®é‡ã§ã™ã€‚(m/s ^2)*(æ•°)* +* **z**: z 軸ã®åŠ é€Ÿåº¦ã®é‡ã§ã™ã€‚(m/s ^2)*(æ•°)* +* **タイムスタンプ**: ä½œæˆæ™‚ã®ã‚¿ã‚¤ãƒ ã‚¹ã‚¿ãƒ³ãƒ— (ミリ秒å˜ä½)。*(,)* diff --git a/plugins/cordova-plugin-device-motion/doc/ko/README.md b/plugins/cordova-plugin-device-motion/doc/ko/README.md new file mode 100644 index 0000000..a62c7ea --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/ko/README.md @@ -0,0 +1,163 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +ì´ í”ŒëŸ¬ê·¸ì¸ ìž¥ì¹˜ì˜ ì†ë„ê³„ì— ëŒ€ 한 액세스를 제공합니다. ê°€ ì†ë„계 3 ì°¨ì› *x*, *y*ë° *z* ì¶• ë”°ë¼ í˜„ìž¬ 장치 ë°©í–¥ 기준으로 ì´ë™ (*ë¸íƒ€*) 변경 ë‚´ìš©ì„ ê°ì§€ 하는 모션 센서입니다. + +글로벌 `navigator.accelerometer` 개체를 통해 액세스가입니다. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## 설치 + + cordova plugin add cordova-plugin-device-motion + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * 브ë¼ìš°ì € + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Tizen + * Windows Phone 8 + * 윈ë„ìš° + +## 메서드 + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## 개체 + + * ê°€ì† + +## navigator.accelerometer.getCurrentAcceleration + +*X*, *y* ë° *z* ì¶• ë”°ë¼ í˜„ìž¬ ê°€ì†ë„ ì–»ì„. + +ì´ ê°€ì†ë„ ê°’ì´ `accelerometerSuccess` 콜백 í•¨ìˆ˜ì— ë°˜í™˜ ë©ë‹ˆë‹¤. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### 예를 들어 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### 브ë¼ìš°ì € 만지면 + +X, Y ê°’ Z 모션ì€ê°€ ì†ë„계 시뮬레ì´ì…˜ ëª¨ë“ ì— ìž„ì˜ë¡œ ìƒì„± ëœ ìˆœì„œìž…ë‹ˆë‹¤. + +### iOS ë‹¨ì  + + * iOS는 ì–´ë–¤ ì£¼ì–´ì§„ëœ ì‹œì ì—서 현재 ê°€ì†ë„ì˜ ê°œë…ì„ ì¸ì‹ 하지 못합니다. + + * ê°€ì†ì„ ê°ì‹œ 하며 ë°ì´í„° ìº¡ì²˜ì— ì£¼ì–´ì§„ 시간 간격. + + * ë”°ë¼ì„œ,는 `getCurrentAcceleration` ì—서 ë³´ê³  ëœ ë§ˆì§€ë§‰ ê°’ì„ ìƒì„± 하는 함수는 `watchAccelerometer` ì „í™”. + +## navigator.accelerometer.watchAcceleration + +때마다 `accelerometerSuccess` 콜백 함수를 실행 ì •ê¸°ì  ì†Œìžì˜ 현재 `Acceleration`ì„ ê²€ìƒ‰ 합니다. `acceleratorOptions` ê°œì²´ì˜ `frequency` 매개 변수를 통해 밀리초 단위로 ê°„ê²©ì„ ì§€ì • 합니다. + +반환 ëœ ì‹œê³„ IDê°€ ì†ë„ê³„ì˜ ì‹œê³„ ê°„ê²©ì„ ì°¸ì¡° 하 ê³ ê°€ ì†ë„계를 보는 중지 하 `navigator.accelerometer.clearWatch`와 함께 사용할 수 있습니다. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + + * **accelerometerOptions**: ë‹¤ìŒ ì„ íƒì  키 개체: + * **기간**: 밀리초ì—서 ê°€ì† ë°ì´í„°ì™€ accelerometerSuccessì— ëŒ€ 한 í˜¸ì¶œì˜ ìš”ì²­ëœ ê¸°ê°„. *(수)* (기본: 10000) + +### 예를 들어 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS ë‹¨ì  + +API 요청 간격 성공 콜백 함수를 호출 하지만 40ms ì‚¬ì´ ìž¥ì¹˜ì— ìš”ì²­ì˜ ë²”ìœ„ë¥¼ 제한 하 ê³  1000ms. 예를 들어 (3000ms) 3 ì´ˆì˜ ê°„ê²©ì„ ìš”ì²­ 하는 경우 API 마다 1 ì´ˆ 장치ì—서 ë°ì´í„°ë¥¼ 요청 하지만 성공 ì½œë°±ì„ 3 ì´ˆ 마다 실행 ë©ë‹ˆë‹¤. + +## navigator.accelerometer.clearWatch + +`watchID` 매개 변수ì—서 참조 `ê°€ì†ë„` ë³´ê³  중지 합니다. + + navigator.accelerometer.clearWatch(watchID); + + + * **watchID**: ID 반환`navigator.accelerometer.watchAcceleration`. + +### 예를 들어 + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## ê°€ì† + +특정 시ì ì— ìº¡ì²˜ëœ `Accelerometer` ë°ì´í„°ë¥¼ í¬í•¨ 합니다. ê°€ì†ë„ ê°’ í¬í•¨ ì¤‘ë ¥ì˜ íš¨ê³¼ (9.81 m/s ^2) 때 장치 ê±°ì§“ë§ í‰í‰ 하 ê³  *x*, *y*, 최대 ì§ë©´ 하 ê³  반환 하는 *z* ê°’ ``, `` ë° `9.81` ì´ì–´ì•¼ 한다,. + +### ì†ì„± + + * **x**: x ì¶•ì— ê°€ì†ì˜ 금액. (m/sì—서 ^2) *(수)* + * **y**: y ì¶•ì— ê°€ì†ì˜ 금액. (m/sì—서 ^2) *(수)* + * **z**: z ì¶•ì˜ ê°€ì†ë„ì˜ ê¸ˆì•¡. (m/sì—서 ^2) *(수)* + * **타임 스탬프**: ìƒì„± 타임 스탬프 (밀리초)입니다. *(DOMTimeStamp)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/ko/index.md b/plugins/cordova-plugin-device-motion/doc/ko/index.md new file mode 100644 index 0000000..3ada49d --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/ko/index.md @@ -0,0 +1,161 @@ + + +# cordova-plugin-device-motion + +ì´ í”ŒëŸ¬ê·¸ì¸ ìž¥ì¹˜ì˜ ì†ë„ê³„ì— ëŒ€ 한 액세스를 제공합니다. ê°€ ì†ë„계 3 ì°¨ì› *x*, *y*ë° *z* ì¶• ë”°ë¼ í˜„ìž¬ 장치 ë°©í–¥ 기준으로 ì´ë™ (*ë¸íƒ€*) 변경 ë‚´ìš©ì„ ê°ì§€ 하는 모션 센서입니다. + +글로벌 `navigator.accelerometer` 개체를 통해 액세스가입니다. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## 설치 + + cordova plugin add cordova-plugin-device-motion + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* 브ë¼ìš°ì € +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Tizen +* Windows Phone 8 +* 윈ë„ìš° + +## 메서드 + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## 개체 + +* ê°€ì† + +## navigator.accelerometer.getCurrentAcceleration + +*X*, *y* ë° *z* ì¶• ë”°ë¼ í˜„ìž¬ ê°€ì†ë„ ì–»ì„. + +ì´ ê°€ì†ë„ ê°’ì´ `accelerometerSuccess` 콜백 í•¨ìˆ˜ì— ë°˜í™˜ ë©ë‹ˆë‹¤. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### 예를 들어 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### 브ë¼ìš°ì € 만지면 + +X, Y ê°’ Z 모션ì€ê°€ ì†ë„계 시뮬레ì´ì…˜ ëª¨ë“ ì— ìž„ì˜ë¡œ ìƒì„± ëœ ìˆœì„œìž…ë‹ˆë‹¤. + +### iOS ë‹¨ì  + +* iOS는 ì–´ë–¤ ì£¼ì–´ì§„ëœ ì‹œì ì—서 현재 ê°€ì†ë„ì˜ ê°œë…ì„ ì¸ì‹ 하지 못합니다. + +* ê°€ì†ì„ ê°ì‹œ 하며 ë°ì´í„° ìº¡ì²˜ì— ì£¼ì–´ì§„ 시간 간격. + +* ë”°ë¼ì„œ,는 `getCurrentAcceleration` ì—서 ë³´ê³  ëœ ë§ˆì§€ë§‰ ê°’ì„ ìƒì„± 하는 함수는 `watchAccelerometer` ì „í™”. + +## navigator.accelerometer.watchAcceleration + +때마다 `accelerometerSuccess` 콜백 함수를 실행 ì •ê¸°ì  ì†Œìžì˜ 현재 `Acceleration`ì„ ê²€ìƒ‰ 합니다. `acceleratorOptions` ê°œì²´ì˜ `frequency` 매개 변수를 통해 밀리초 단위로 ê°„ê²©ì„ ì§€ì • 합니다. + +반환 ëœ ì‹œê³„ IDê°€ ì†ë„ê³„ì˜ ì‹œê³„ ê°„ê²©ì„ ì°¸ì¡° 하 ê³ ê°€ ì†ë„계를 보는 중지 하 `navigator.accelerometer.clearWatch`와 함께 사용할 수 있습니다. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: ë‹¤ìŒ ì„ íƒì  키 개체: + * **기간**: 밀리초ì—서 ê°€ì† ë°ì´í„°ì™€ accelerometerSuccessì— ëŒ€ 한 í˜¸ì¶œì˜ ìš”ì²­ëœ ê¸°ê°„. *(수)* (기본: 10000) + +### 예를 들어 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS ë‹¨ì  + +API 요청 간격 성공 콜백 함수를 호출 하지만 40ms ì‚¬ì´ ìž¥ì¹˜ì— ìš”ì²­ì˜ ë²”ìœ„ë¥¼ 제한 하 ê³  1000ms. 예를 들어 (3000ms) 3 ì´ˆì˜ ê°„ê²©ì„ ìš”ì²­ 하는 경우 API 마다 1 ì´ˆ 장치ì—서 ë°ì´í„°ë¥¼ 요청 하지만 성공 ì½œë°±ì„ 3 ì´ˆ 마다 실행 ë©ë‹ˆë‹¤. + +## navigator.accelerometer.clearWatch + +`watchID` 매개 변수ì—서 참조 `ê°€ì†ë„` ë³´ê³  중지 합니다. + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: ID 반환`navigator.accelerometer.watchAcceleration`. + +### 예를 들어 + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## ê°€ì† + +특정 시ì ì— ìº¡ì²˜ëœ `Accelerometer` ë°ì´í„°ë¥¼ í¬í•¨ 합니다. ê°€ì†ë„ ê°’ í¬í•¨ ì¤‘ë ¥ì˜ íš¨ê³¼ (9.81 m/s ^2) 때 장치 ê±°ì§“ë§ í‰í‰ 하 ê³  *x*, *y*, 최대 ì§ë©´ 하 ê³  반환 하는 *z* ê°’ ``, `` ë° `9.81` ì´ì–´ì•¼ 한다,. + +### ì†ì„± + +* **x**: x ì¶•ì— ê°€ì†ì˜ 금액. (m/sì—서 ^2) *(수)* +* **y**: y ì¶•ì— ê°€ì†ì˜ 금액. (m/sì—서 ^2) *(수)* +* **z**: z ì¶•ì˜ ê°€ì†ë„ì˜ ê¸ˆì•¡. (m/sì—서 ^2) *(수)* +* **타임 스탬프**: ìƒì„± 타임 스탬프 (밀리초)입니다. *(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/pl/README.md b/plugins/cordova-plugin-device-motion/doc/pl/README.md new file mode 100644 index 0000000..d2c6758 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/pl/README.md @@ -0,0 +1,163 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +Ten plugin umożliwia dostÄ™p do akcelerometru. Akcelerometr jest czujnikiem ruchu, który wykrywa zmiany (*delta*) w ruchu wzglÄ™dem bieżącej orientacji urzÄ…dzenia, w trzech wymiarach na osi *x*, *y*i *z* . + +DostÄ™p odbywa siÄ™ za pomocÄ… obiektu globalnego `navigator.accelerometer`. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## Instalacja + + cordova plugin add cordova-plugin-device-motion + + +## ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * PrzeglÄ…darka + * Firefox OS + * iOS + * Tizen + * Windows Phone 8 + * Windows + +## Metody + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## Obiekty + + * Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Uzyskać aktualne przyspieszenie wzdÅ‚uż osi *x*, *y* i *z*. + +Te wartoÅ›ci przyspieszenia sÄ… zwracane do funkcji wywoÅ‚ania zwrotnego `accelerometerSuccess`. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### PrzykÅ‚ad + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Quirks przeglÄ…darki + +WartoÅ›ci dla osi X, Y, Z ruchu sÄ… losowo generowane w celu symulacji akcelerometr. + +### Dziwactwa iOS + + * W iOS nie wprowadzono możliwoÅ›ci zmierzenia aktualnego przyspieszenia w dowolnym punkcie. + + * Musisz obserwować przyspieszenie i odbierać wyniki w okreÅ›lonych odstÄ™pach czasu. + + * PodsumowujÄ…c, funkcja `getCurrentAcceleration` zwraca ostatniÄ… wartość zgÅ‚oszonÄ… przez wywoÅ‚anie `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Pobiera bieżącego urzÄ…dzenia `Acceleration` w regularnych odstÄ™pach czasu, wykonywanie funkcji wywoÅ‚ania zwrotnego `accelerometerSuccess` każdorazowe. OkreÅ›l interwaÅ‚ w milisekundach przez parametr obiektu `acceleratorOptions` `frequency`. + +Identyfikator zwrócony zegarek odwoÅ‚uje akcelerometr zegarek interwaÅ‚ i może być używany z `navigator.accelerometer.clearWatch`, aby zatrzymać obejrzeniu akcelerometru. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + + * **accelerometerOptions**: Obiekt z nastÄ™pujÄ…cymi opcjonalnymi kluczami: + * **okres**: żądany okres wzywa do accelerometerSuccess z danych przyspieszenia w milisekundach. *(Liczba)* (DomyÅ›lnie: 10000) + +### PrzykÅ‚ad + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### Dziwactwa iOS + +Interfejs API wymaga sukcesu funkcji wywoÅ‚ania zwrotnego w interwaÅ‚ żądana, ale ogranicza zakres żądania do urzÄ…dzenia miÄ™dzy 40ms i 1000ms. Na przykÅ‚ad jeÅ›li poprosisz o odstÄ™pie 3 sekundy (3000ms), interfejs API żądania danych z urzÄ…dzenia co 1 sekundÄ™, ale tylko wykonuje wywoÅ‚anie zwrotne sukces co 3 sekundy. + +## navigator.accelerometer.clearWatch + +Przestać oglÄ…dać `Acceleration` okreÅ›lany przez parametr `watchID`. + + navigator.accelerometer.clearWatch(watchID); + + + * **watchID**: Identyfikator zwrócony przez `navigator.accelerometer.watchAcceleration`. + +### PrzykÅ‚ad + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Acceleration + +Zawiera `Accelerometer` dane przechwycone w okreÅ›lonym czasie. WartoÅ›ci przyÅ›pieszenia to efekt grawitacji (9.81 m/s ^ 2), tak, że kiedy urzÄ…dzenie znajduje siÄ™ pÅ‚aska i górÄ™, *x*, *y*, i *z* wartoÅ›ci zwracane powinno być ``, `` i `9.81`. + +### WÅ‚aÅ›ciwoÅ›ci + + * **x**: Wartość przyÅ›pieszenia na osi x. (w m/s^2) *(Liczba)* + * **y**: Wartość przyÅ›pieszenia na osi y. (w m/s^2) *(Liczba)* + * **z**: Wartość przyÅ›pieszenia na osi z. (w m/s^2) *(Liczba)* + * **timestamp**: Znacznik czasu w milisekundach. *(DOMTimeStamp)* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/pl/index.md b/plugins/cordova-plugin-device-motion/doc/pl/index.md new file mode 100644 index 0000000..2d204a5 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/pl/index.md @@ -0,0 +1,161 @@ + + +# cordova-plugin-device-motion + +Ten plugin umożliwia dostÄ™p do akcelerometru. Akcelerometr jest czujnikiem ruchu, który wykrywa zmiany (*delta*) w ruchu wzglÄ™dem bieżącej orientacji urzÄ…dzenia, w trzech wymiarach na osi *x*, *y*i *z* . + +DostÄ™p odbywa siÄ™ za pomocÄ… obiektu globalnego `navigator.accelerometer`. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## Instalacja + + cordova plugin add cordova-plugin-device-motion + + +## ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* PrzeglÄ…darka +* Firefox OS +* iOS +* Tizen +* Windows Phone 8 +* Windows + +## Metody + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## Obiekty + +* Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Uzyskać aktualne przyspieszenie wzdÅ‚uż osi *x*, *y* i *z*. + +Te wartoÅ›ci przyspieszenia sÄ… zwracane do funkcji wywoÅ‚ania zwrotnego `accelerometerSuccess`. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### PrzykÅ‚ad + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Quirks przeglÄ…darki + +WartoÅ›ci dla osi X, Y, Z ruchu sÄ… losowo generowane w celu symulacji akcelerometr. + +### Dziwactwa iOS + +* W iOS nie wprowadzono możliwoÅ›ci zmierzenia aktualnego przyspieszenia w dowolnym punkcie. + +* Musisz obserwować przyspieszenie i odbierać wyniki w okreÅ›lonych odstÄ™pach czasu. + +* PodsumowujÄ…c, funkcja `getCurrentAcceleration` zwraca ostatniÄ… wartość zgÅ‚oszonÄ… przez wywoÅ‚anie `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Pobiera bieżącego urzÄ…dzenia `Acceleration` w regularnych odstÄ™pach czasu, wykonywanie funkcji wywoÅ‚ania zwrotnego `accelerometerSuccess` każdorazowe. OkreÅ›l interwaÅ‚ w milisekundach przez parametr obiektu `acceleratorOptions` `frequency`. + +Identyfikator zwrócony zegarek odwoÅ‚uje akcelerometr zegarek interwaÅ‚ i może być używany z `navigator.accelerometer.clearWatch`, aby zatrzymać obejrzeniu akcelerometru. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: Obiekt z nastÄ™pujÄ…cymi opcjonalnymi kluczami: + * **okres**: żądany okres wzywa do accelerometerSuccess z danych przyspieszenia w milisekundach. *(Liczba)* (DomyÅ›lnie: 10000) + +### PrzykÅ‚ad + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### Dziwactwa iOS + +Interfejs API wymaga sukcesu funkcji wywoÅ‚ania zwrotnego w interwaÅ‚ żądana, ale ogranicza zakres żądania do urzÄ…dzenia miÄ™dzy 40ms i 1000ms. Na przykÅ‚ad jeÅ›li poprosisz o odstÄ™pie 3 sekundy (3000ms), interfejs API żądania danych z urzÄ…dzenia co 1 sekundÄ™, ale tylko wykonuje wywoÅ‚anie zwrotne sukces co 3 sekundy. + +## navigator.accelerometer.clearWatch + +Przestać oglÄ…dać `Acceleration` okreÅ›lany przez parametr `watchID`. + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: Identyfikator zwrócony przez `navigator.accelerometer.watchAcceleration`. + +### PrzykÅ‚ad + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Acceleration + +Zawiera `Accelerometer` dane przechwycone w okreÅ›lonym czasie. WartoÅ›ci przyÅ›pieszenia to efekt grawitacji (9.81 m/s ^ 2), tak, że kiedy urzÄ…dzenie znajduje siÄ™ pÅ‚aska i górÄ™, *x*, *y*, i *z* wartoÅ›ci zwracane powinno być ``, `` i `9.81`. + +### WÅ‚aÅ›ciwoÅ›ci + +* **x**: Wartość przyÅ›pieszenia na osi x. (w m/s^2) *(Liczba)* +* **y**: Wartość przyÅ›pieszenia na osi y. (w m/s^2) *(Liczba)* +* **z**: Wartość przyÅ›pieszenia na osi z. (w m/s^2) *(Liczba)* +* **timestamp**: Znacznik czasu w milisekundach. *(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/ru/index.md b/plugins/cordova-plugin-device-motion/doc/ru/index.md new file mode 100644 index 0000000..baeb7fb --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/ru/index.md @@ -0,0 +1,151 @@ + + +# cordova-plugin-device-motion + +Этот плагин обеÑпечивает доÑтуп к акÑелерометру уÑтройÑтва. ÐкÑелерометр ÑвлÑетÑÑ Ð´Ð°Ñ‚Ñ‡Ð¸ÐºÐ¾Ð¼ движениÑ, который обнаруживает изменение (*дельта*) в движении отноÑительно текущей ориентации уÑтройÑтва, в трех измерениÑÑ… вдоль оÑей *x*, *y* и *z* . + +## УÑтановка + + cordova plugin add cordova-plugin-device-motion + + +## Поддерживаемые платформы + +* Amazon Fire ОС +* Android +* BlackBerry 10 +* Обозреватель +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +## Методы + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## Объекты + +* Acceleration + +## navigator.accelerometer.getCurrentAcceleration + +Возвращает текущее уÑкорение вдоль оÑей *x*, *y* и *z*. + +Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑƒÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´Ð°ÑŽÑ‚ÑÑ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ обратного вызова `accelerometerSuccess`. + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### Пример + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### Браузер причуды + +Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ X, Y, Z Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ ÑвлÑÑŽÑ‚ÑÑ Ð²Ñе Ñлучайным в целÑÑ… Ð¼Ð¾Ð´ÐµÐ»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð°ÐºÑелерометра. + +### ОÑобенноÑти iOS + +* iOS не поддерживает автоматичеÑкое обновление значений Ð´Ð»Ñ ÑƒÑкорениÑ. + +* Ð’Ñ‹ должны ÑамоÑтоÑтельно отÑлеживать изменение уÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð¸ Ñчитывать данные через определенные интервалы времени. + +* Таким образом Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ `getCurrentAcceleration` возвращает поÑледнее значение, полученное при вызове `watchAccelerometer`. + +## navigator.accelerometer.watchAcceleration + +Извлекает Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ ÑƒÑтройÑтво `Acceleration` Ñ Ð¿Ð¾ÑтоÑнным интервалом, выполнение `accelerometerSuccess` Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова каждый раз. Задайте интервал в миллиÑекундах, через `acceleratorOptions` объекта `frequency` параметр. + +Возвращаемый Ñмотреть ÑÑылки ID акÑелерометр чаÑÑ‹ интервал и может быть иÑпользован Ñ `navigator.accelerometer.clearWatch` чтобы оÑтановить проÑмотр акÑелерометр. + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: Объект Ñ Ñледующие необÑзательные ÑвойÑтвами: + * **период**: запрошенный период звонков на accelerometerSuccess Ñ ÑƒÑкорение данных в миллиÑекундах. *(ЧиÑло)* (По умолчанию: 10000) + +### Пример + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### ОÑобенноÑти iOS + +API вызывает функцию обратного вызова Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ интервалом, но имеет ограничение по чаÑтоте запроÑов к уÑтройÑтву от 40 Ð¼Ñ Ð¸ до 1000 мÑ. Ðапример еÑли вы запрашиваете интервал 3 Ñекунды, (3000 мÑ), API запрашивает данные от уÑтройÑтва каждую Ñекунду, но Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова будет Ñрабатывать только каждые 3 Ñекунды. + +## navigator.accelerometer.clearWatch + +ОÑтанавливает отÑлеживание изменений объекта `Acceleration`, на который ÑÑылаетÑÑ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€ `watchID`. + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: идентификатор, возвращенный`navigator.accelerometer.watchAcceleration`. + +### Пример + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## Acceleration + +Содержит данные полученные от акÑелерометра на определенный момент времени. УÑкорение Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ Ñффект гравитации (9,81 м/Ñ ^ 2), так что когда уÑтройÑтво лежит плоÑÐºÐ°Ñ Ð¸ вверх, *x*, *y*, и *z* значениÑ, возвращаемые должны быть `` , `` , и`9.81`. + +### Параметры + +* **x**: величина уÑкорение по оÑи x. (в м/Ñ ^ 2) *(ЧиÑло)* +* **y**: величина уÑкорение по оÑи y. (в м/Ñ ^ 2) *(ЧиÑло)* +* **z**: величина уÑкорение по оÑи z. (в м/Ñ ^ 2) *(ЧиÑло)* +* **timestamp**: Ð²Ñ€ÐµÐ¼ÐµÐ½Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° в миллиÑекундах. *(DOMTimeStamp)* diff --git a/plugins/cordova-plugin-device-motion/doc/zh/README.md b/plugins/cordova-plugin-device-motion/doc/zh/README.md new file mode 100644 index 0000000..766e4de --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/zh/README.md @@ -0,0 +1,163 @@ + + +# cordova-plugin-device-motion + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-motion.svg)](https://travis-ci.org/apache/cordova-plugin-device-motion) + +é€™å€‹å¤–æŽ›ç¨‹å¼æä¾›äº†å°è¨­å‚™çš„加速度計的訪å•。 加速度計是動作感應器檢測到的更改 (*三角洲*) åœ¨ç›¸å°æ–¼ç•¶å‰çš„設備方å‘,在三個維度沿*x*〠*y*å’Œ*z*軸é‹å‹•。 + +è¨ªå•æ˜¯é€šéŽä¸€å€‹å…¨çƒ `navigator.accelerometer` 物件。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-device-motion + + +## 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç€è¦½å™¨ + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Tizen + * Windows Phone 8 + * Windows + +## 方法 + + * navigator.accelerometer.getCurrentAcceleration + * navigator.accelerometer.watchAcceleration + * navigator.accelerometer.clearWatch + +## 物件 + + * 加速度 + +## navigator.accelerometer.getCurrentAcceleration + +得到當å‰åŠ é€Ÿåº¦æ²¿ *x*〠*y* å’Œ *z* 軸。 + +這些加速度值將返回到 `accelerometerSuccess` 回呼函數。 + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### 示例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### ç€è¦½å™¨çš„æ€ªç™– + +值 X〠Y〠Z 議案是所有中隨機生æˆçš„訂單來類比加速度感應器。 + +### iOS 的怪癖 + + * iOS 䏿œƒèªè­˜åˆ°åœ¨ä»»ä½•給定的點ç²å–ç•¶å‰åŠ é€Ÿåº¦çš„æ¦‚å¿µã€‚ + + * 你必須看加速和æ•ç²çš„資料在特定的時間間隔。 + + * 因此, `getCurrentAcceleration` 收益率從報告的最後一個值的函數 `watchAccelerometer` 調用。 + +## navigator.accelerometer.watchAcceleration + +在週期性時間間隔,執行 `accelerometerSuccess` å›žå‘¼å‡½æ•¸æ¯æ¬¡æª¢ç´¢è¨­å‚™çš„ç•¶å‰ `Accelerometer`。 指定的間隔,以毫秒為單ä½é€šéŽ `acceleratorOptions` 物件的 `frequency` åƒæ•¸ã€‚ + +返回的表 ID 引用加速度計的手錶時間間隔,並且å¯ä»¥èˆ‡ `navigator.accelerometer.clearWatch` ç”¨ä¾†åœæ­¢è§€çœ‹äº†åŠ é€Ÿåº¦è¨ˆã€‚ + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + + * **accelerometerOptions**: 具有以下å¯é¸çš„éµçš„物件: + * **期間**: 請求的期間的調用的 accelerometerSuccess 與加速度資料以毫秒為單ä½ã€‚*(人數)*(é è¨­å€¼ï¼š 10000) + +### 示例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS 的怪癖 + +API 呼嫿ˆåŠŸçš„å›žå‘¼å‡½æ•¸åœ¨æ™‚é–“é–“éš”çš„è¦æ±‚,但將請求的範åœé™åˆ¶ç‚º 40ms年之間è£ç½®å’Œ 1000ms。 例如,如果您請求的時間間隔為 3 秒,(3000ms),API 請求資料從設備æ¯éš” 1 ç§’ï¼Œä½†åªæ˜¯åŸ·è¡ŒæˆåŠŸå›žæª”æ¯ 3 秒。 + +## navigator.accelerometer.clearWatch + +別看 `watchID` åƒæ•¸æ‰€å¼•用的 `Accelerometer`。 + + navigator.accelerometer.clearWatch(watchID); + + + * **watchID**: 由返回的 ID`navigator.accelerometer.watchAcceleration`. + +### 示例 + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## 加速度 + +包å«åœ¨æ™‚間中æ•ç²çš„特定點的 `Accekerometer` 資料。 加速度值包括é‡åŠ›çš„ä½œç”¨ (9.81 m/s ^2),這樣當設備在於æ‰å’Œæœä¸Šï¼Œ*x*,*y*,*z* 返回的值應該是 ``〠`` 度和 `9.81`. + +### 屬性 + + * **x**: 在 X 軸上的加速度é‡ã€‚(在 m/s ^2)*(人數)* + * **y**: 在 y 軸上的加速度é‡ã€‚(在 m/s ^2)*(人數)* + * **z**: 在 Z 軸上的加速度é‡ã€‚(在 m/s ^2)*(人數)* + * **timestamp**: 創建時間戳記以毫秒為單ä½ã€‚*() DOMTimeStamp* \ No newline at end of file diff --git a/plugins/cordova-plugin-device-motion/doc/zh/index.md b/plugins/cordova-plugin-device-motion/doc/zh/index.md new file mode 100644 index 0000000..f9ad0d5 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/doc/zh/index.md @@ -0,0 +1,161 @@ + + +# cordova-plugin-device-motion + +é€™å€‹å¤–æŽ›ç¨‹å¼æä¾›äº†å°è¨­å‚™çš„加速度計的訪å•。 加速度計是動作感應器檢測到的更改 (*三角洲*) åœ¨ç›¸å°æ–¼ç•¶å‰çš„設備方å‘,在三個維度沿*x*〠*y*å’Œ*z*軸é‹å‹•。 + +è¨ªå•æ˜¯é€šéŽä¸€å€‹å…¨çƒ `navigator.accelerometer` 物件。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.accelerometer); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-device-motion + + +## 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç€è¦½å™¨ +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* æ³° +* Windows Phone 8 +* Windows + +## 方法 + +* navigator.accelerometer.getCurrentAcceleration +* navigator.accelerometer.watchAcceleration +* navigator.accelerometer.clearWatch + +## 物件 + +* 加速度 + +## navigator.accelerometer.getCurrentAcceleration + +得到當å‰åŠ é€Ÿåº¦æ²¿ *x*〠*y* å’Œ *z* 軸。 + +這些加速度值將返回到 `accelerometerSuccess` 回呼函數。 + + navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError); + + +### 示例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + navigator.accelerometer.getCurrentAcceleration(onSuccess, onError); + + +### ç€è¦½å™¨çš„æ€ªç™– + +值 X〠Y〠Z 議案是所有中隨機生æˆçš„訂單來類比加速度感應器。 + +### iOS 的怪癖 + +* iOS 䏿œƒèªè­˜åˆ°åœ¨ä»»ä½•給定的點ç²å–ç•¶å‰åŠ é€Ÿåº¦çš„æ¦‚å¿µã€‚ + +* 你必須看加速和æ•ç²çš„資料在特定的時間間隔。 + +* 因此, `getCurrentAcceleration` 收益率從報告的最後一個值的函數 `watchAccelerometer` 調用。 + +## navigator.accelerometer.watchAcceleration + +在週期性時間間隔,執行 `accelerometerSuccess` å›žå‘¼å‡½æ•¸æ¯æ¬¡æª¢ç´¢è¨­å‚™çš„ç•¶å‰ `Accelerometer`。 指定的間隔,以毫秒為單ä½é€šéŽ `acceleratorOptions` 物件的 `frequency` åƒæ•¸ã€‚ + +返回的表 ID 引用加速度計的手錶時間間隔,並且å¯ä»¥èˆ‡ `navigator.accelerometer.clearWatch` ç”¨ä¾†åœæ­¢è§€çœ‹äº†åŠ é€Ÿåº¦è¨ˆã€‚ + + var watchID = navigator.accelerometer.watchAcceleration(accelerometerSuccess, + accelerometerError, + accelerometerOptions); + + +* **accelerometerOptions**: 具有以下å¯é¸çš„éµçš„物件: + * **期間**: 請求的期間的調用的 accelerometerSuccess 與加速度資料以毫秒為單ä½ã€‚*(人數)*(é è¨­å€¼ï¼š 10000) + +### 示例 + + function onSuccess(acceleration) { + alert('Acceleration X: ' + acceleration.x + '\n' + + 'Acceleration Y: ' + acceleration.y + '\n' + + 'Acceleration Z: ' + acceleration.z + '\n' + + 'Timestamp: ' + acceleration.timestamp + '\n'); + }; + + function onError() { + alert('onError!'); + }; + + var options = { frequency: 3000 }; // Update every 3 seconds + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + +### iOS 的怪癖 + +API 呼嫿ˆåŠŸçš„å›žå‘¼å‡½æ•¸åœ¨æ™‚é–“é–“éš”çš„è¦æ±‚,但將請求的範åœé™åˆ¶ç‚º 40ms年之間è£ç½®å’Œ 1000ms。 例如,如果您請求的時間間隔為 3 秒,(3000ms),API 請求資料從設備æ¯éš” 1 ç§’ï¼Œä½†åªæ˜¯åŸ·è¡ŒæˆåŠŸå›žæª”æ¯ 3 秒。 + +## navigator.accelerometer.clearWatch + +別看 `watchID` åƒæ•¸æ‰€å¼•用的 `Accelerometer`。 + + navigator.accelerometer.clearWatch(watchID); + + +* **watchID**: 由返回的 ID`navigator.accelerometer.watchAcceleration`. + +### 示例 + + var watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options); + + // ... later on ... + + navigator.accelerometer.clearWatch(watchID); + + +## 加速度 + +包å«åœ¨æ™‚間中æ•ç²çš„特定點的 `Accekerometer` 資料。 加速度值包括é‡åŠ›çš„ä½œç”¨ (9.81 m/s ^2),這樣當設備在於æ‰å’Œæœä¸Šï¼Œ*x*,*y*,*z* 返回的值應該是 ``〠`` 度和 `9.81`. + +### 屬性 + +* **x**: 在 X 軸上的加速度é‡ã€‚(在 m/s ^2)*(人數)* +* **y**: 在 y 軸上的加速度é‡ã€‚(在 m/s ^2)*(人數)* +* **z**: 在 Z 軸上的加速度é‡ã€‚(在 m/s ^2)*(人數)* +* **timestamp**: 創建時間戳記以毫秒為單ä½ã€‚*() DOMTimeStamp* diff --git a/plugins/cordova-plugin-device-motion/package.json b/plugins/cordova-plugin-device-motion/package.json new file mode 100644 index 0000000..851f2e0 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/package.json @@ -0,0 +1,60 @@ +{ + "name": "cordova-plugin-device-motion", + "version": "1.2.3", + "description": "Cordova Device Motion Plugin", + "cordova": { + "id": "cordova-plugin-device-motion", + "platforms": [ + "firefoxos", + "android", + "amazon-fireos", + "ubuntu", + "ios", + "blackberry10", + "wp7", + "wp8", + "windows8", + "windows", + "tizen", + "browser" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-device-motion" + }, + "keywords": [ + "cordova", + "device", + "motion", + "ecosystem:cordova", + "cordova-firefoxos", + "cordova-android", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-ios", + "cordova-blackberry10", + "cordova-wp7", + "cordova-wp8", + "cordova-windows8", + "cordova-windows", + "cordova-tizen", + "cordova-browser" + ], + "scripts": { + "test": "npm run jshint", + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "engines": { + "cordovaDependencies": { + "2.0.0": { + "cordova": ">100" + } + } + }, + "devDependencies": { + "jshint": "^2.6.0" + } +} diff --git a/plugins/cordova-plugin-device-motion/plugin.xml b/plugins/cordova-plugin-device-motion/plugin.xml new file mode 100644 index 0000000..2378d23 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/plugin.xml @@ -0,0 +1,165 @@ + + + + + + Device Motion + Cordova Device Motion Plugin + Apache 2.0 + cordova,device,motion + https://git-wip-us.apache.org/repos/asf/cordova-plugin-device-motion.git + https://issues.apache.org/jira/browse/CB/component/12320636 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-device-motion/src/android/AccelListener.java b/plugins/cordova-plugin-device-motion/src/android/AccelListener.java new file mode 100755 index 0000000..61856fb --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/android/AccelListener.java @@ -0,0 +1,311 @@ +/* + 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.devicemotion; + +import java.util.List; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +import android.os.Handler; +import android.os.Looper; + +/** + * This class listens to the accelerometer sensor and stores the latest + * acceleration values x,y,z. + */ +public class AccelListener extends CordovaPlugin implements SensorEventListener { + + public static int STOPPED = 0; + public static int STARTING = 1; + public static int RUNNING = 2; + public static int ERROR_FAILED_TO_START = 3; + + private float x,y,z; // most recent acceleration values + private long timestamp; // time of most recent value + private int status; // status of listener + private int accuracy = SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM; + + private SensorManager sensorManager; // Sensor manager + private Sensor mSensor; // Acceleration sensor returned by sensor manager + + private CallbackContext callbackContext; // Keeps track of the JS callback context. + + private Handler mainHandler=null; + private Runnable mainRunnable =new Runnable() { + public void run() { + AccelListener.this.timeout(); + } + }; + + /** + * Create an accelerometer listener. + */ + public AccelListener() { + this.x = 0; + this.y = 0; + this.z = 0; + this.timestamp = 0; + this.setStatus(AccelListener.STOPPED); + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The associated CordovaWebView. + */ + @Override + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE); + } + + /** + * Executes the request. + * + * @param action The action to execute. + * @param args The exec() arguments. + * @param callbackId The callback id used when calling back into JavaScript. + * @return Whether the action was valid. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) { + if (action.equals("start")) { + this.callbackContext = callbackContext; + if (this.status != AccelListener.RUNNING) { + // If not running, then this is an async call, so don't worry about waiting + // We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road + this.start(); + } + } + else if (action.equals("stop")) { + if (this.status == AccelListener.RUNNING) { + this.stop(); + } + } else { + // Unsupported action + return false; + } + + PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT, ""); + result.setKeepCallback(true); + callbackContext.sendPluginResult(result); + return true; + } + + /** + * Called by AccelBroker when listener is to be shut down. + * Stop listener. + */ + public void onDestroy() { + this.stop(); + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + // + /** + * Start listening for acceleration sensor. + * + * @return status of listener + */ + private int start() { + // If already starting or running, then restart timeout and return + if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) { + startTimeout(); + return this.status; + } + + this.setStatus(AccelListener.STARTING); + + // Get accelerometer from sensor manager + List list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER); + + // If found, then register as listener + if ((list != null) && (list.size() > 0)) { + this.mSensor = list.get(0); + if (this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI)) { + this.setStatus(AccelListener.STARTING); + // CB-11531: Mark accuracy as 'reliable' - this is complementary to + // setting it to 'unreliable' 'stop' method + this.accuracy = SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM; + } else { + this.setStatus(AccelListener.ERROR_FAILED_TO_START); + this.fail(AccelListener.ERROR_FAILED_TO_START, "Device sensor returned an error."); + return this.status; + }; + + } else { + this.setStatus(AccelListener.ERROR_FAILED_TO_START); + this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to."); + return this.status; + } + + startTimeout(); + + return this.status; + } + private void startTimeout() { + // Set a timeout callback on the main thread. + stopTimeout(); + mainHandler = new Handler(Looper.getMainLooper()); + mainHandler.postDelayed(mainRunnable, 2000); + } + private void stopTimeout() { + if(mainHandler!=null){ + mainHandler.removeCallbacks(mainRunnable); + } + } + /** + * Stop listening to acceleration sensor. + */ + private void stop() { + stopTimeout(); + if (this.status != AccelListener.STOPPED) { + this.sensorManager.unregisterListener(this); + } + this.setStatus(AccelListener.STOPPED); + this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE; + } + + /** + * Returns latest cached position if the sensor hasn't returned newer value. + * + * Called two seconds after starting the listener. + */ + private void timeout() { + if (this.status == AccelListener.STARTING && + this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) { + // call win with latest cached position + // but first check if cached position is reliable + this.timestamp = System.currentTimeMillis(); + this.win(); + } + } + + /** + * Called when the accuracy of the sensor has changed. + * + * @param sensor + * @param accuracy + */ + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Only look at accelerometer events + if (sensor.getType() != Sensor.TYPE_ACCELEROMETER) { + return; + } + + // If not running, then just return + if (this.status == AccelListener.STOPPED) { + return; + } + this.accuracy = accuracy; + } + + /** + * Sensor listener event. + * + * @param SensorEvent event + */ + public void onSensorChanged(SensorEvent event) { + // Only look at accelerometer events + if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) { + return; + } + + // If not running, then just return + if (this.status == AccelListener.STOPPED) { + return; + } + this.setStatus(AccelListener.RUNNING); + + if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) { + + // Save time that event was received + this.timestamp = System.currentTimeMillis(); + this.x = event.values[0]; + this.y = event.values[1]; + this.z = event.values[2]; + + this.win(); + } + } + + /** + * Called when the view navigates. + */ + @Override + public void onReset() { + if (this.status == AccelListener.RUNNING) { + this.stop(); + } + } + + // Sends an error back to JS + private void fail(int code, String message) { + // Error object + JSONObject errorObj = new JSONObject(); + try { + errorObj.put("code", code); + errorObj.put("message", message); + } catch (JSONException e) { + e.printStackTrace(); + } + PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj); + err.setKeepCallback(true); + callbackContext.sendPluginResult(err); + } + + private void win() { + // Success return object + PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON()); + result.setKeepCallback(true); + callbackContext.sendPluginResult(result); + } + + private void setStatus(int status) { + this.status = status; + } + private JSONObject getAccelerationJSON() { + JSONObject r = new JSONObject(); + try { + r.put("x", this.x); + r.put("y", this.y); + r.put("z", this.z); + r.put("timestamp", this.timestamp); + } catch (JSONException e) { + e.printStackTrace(); + } + return r; + } +} diff --git a/plugins/cordova-plugin-device-motion/src/blackberry10/index.js b/plugins/cordova-plugin-device-motion/src/blackberry10/index.js new file mode 100644 index 0000000..98bd8ce --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/blackberry10/index.js @@ -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. + * +*/ + +/* global PluginResult */ + +var callback; + +module.exports = { + start: function (success, fail, args, env) { + var result = new PluginResult(args, env); + window.removeEventListener("devicemotion", callback); + callback = function (motion) { + var info = { + x: motion.accelerationIncludingGravity.x, + y: motion.accelerationIncludingGravity.y, + z: motion.accelerationIncludingGravity.z, + timestamp: motion.timestamp + }; + result.callbackOk(info, true); + }; + window.addEventListener("devicemotion", callback); + result.noResult(true); + }, + stop: function (success, fail, args, env) { + var result = new PluginResult(args, env); + window.removeEventListener("devicemotion", callback); + result.ok("removed"); + } +}; diff --git a/plugins/cordova-plugin-device-motion/src/browser/AccelerometerProxy.js b/plugins/cordova-plugin-device-motion/src/browser/AccelerometerProxy.js new file mode 100644 index 0000000..838ae54 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/browser/AccelerometerProxy.js @@ -0,0 +1,45 @@ +/* + * + * 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 listener(success) { + var accel = {}; + + accel.x = (Math.round(((Math.random() * 2) - 1) * 100) / 100); + accel.y = (Math.round(((Math.random() * 2) - 1) * 100) / 100); + accel.z = (Math.round(((Math.random() * 2) - 1) * 100) / 100); + accel.timestamp = new Date().getTime(); + + success(accel); + + window.removeEventListener('devicemotion', listener, false); +} + +var Accelerometer = { + start: function start(success, error) { + return window.addEventListener('devicemotion', function(){ + listener(success); + }, false); + } +}; + +module.exports = Accelerometer; +require('cordova/exec/proxy').add('Accelerometer', Accelerometer); diff --git a/plugins/cordova-plugin-device-motion/src/firefoxos/accelerometer.js b/plugins/cordova-plugin-device-motion/src/firefoxos/accelerometer.js new file mode 100644 index 0000000..5a2f986 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/firefoxos/accelerometer.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. + * +*/ + +function listener(success, ev) { + var acc = ev.accelerationIncludingGravity; + acc.timestamp = new Date().getTime(); + success(acc); +} + +var Accelerometer = { + start: function start(success, error) { + return window.addEventListener('devicemotion', function(ev) { + listener(success, ev); + }, false); + }, + + stop: function stop() { + window.removeEventListener('devicemotion', listener, false); + } +}; + +module.exports = Accelerometer; +require('cordova/exec/proxy').add('Accelerometer', Accelerometer); diff --git a/plugins/cordova-plugin-device-motion/src/ios/CDVAccelerometer.h b/plugins/cordova-plugin-device-motion/src/ios/CDVAccelerometer.h new file mode 100755 index 0000000..ee1664f --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/ios/CDVAccelerometer.h @@ -0,0 +1,39 @@ +/* + 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 +#import + +@interface CDVAccelerometer : CDVPlugin +{ + double x; + double y; + double z; + NSTimeInterval timestamp; +} + +@property (readonly, assign) BOOL isRunning; +@property (nonatomic, strong) NSString* callbackId; + +- (CDVAccelerometer*)init; + +- (void)start:(CDVInvokedUrlCommand*)command; +- (void)stop:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/cordova-plugin-device-motion/src/ios/CDVAccelerometer.m b/plugins/cordova-plugin-device-motion/src/ios/CDVAccelerometer.m new file mode 100755 index 0000000..476de04 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/ios/CDVAccelerometer.m @@ -0,0 +1,149 @@ +/* + 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 +#import "CDVAccelerometer.h" + +@interface CDVAccelerometer () {} +@property (readwrite, assign) BOOL isRunning; +@property (readwrite, assign) BOOL haveReturnedResult; +@property (readwrite, strong) CMMotionManager* motionManager; +@property (readwrite, assign) double x; +@property (readwrite, assign) double y; +@property (readwrite, assign) double z; +@property (readwrite, assign) NSTimeInterval timestamp; +@end + +@implementation CDVAccelerometer + +@synthesize callbackId, isRunning,x,y,z,timestamp; + +// defaults to 10 msec +#define kAccelerometerInterval 10 +// g constant: -9.81 m/s^2 +#define kGravitationalConstant -9.81 + +- (CDVAccelerometer*)init +{ + self = [super init]; + if (self) { + self.x = 0; + self.y = 0; + self.z = 0; + self.timestamp = 0; + self.callbackId = nil; + self.isRunning = NO; + self.haveReturnedResult = YES; + self.motionManager = nil; + } + return self; +} + +- (void)dealloc +{ + [self stop:nil]; +} + +- (void)start:(CDVInvokedUrlCommand*)command +{ + self.haveReturnedResult = NO; + self.callbackId = command.callbackId; + + if (!self.motionManager) + { + self.motionManager = [[CMMotionManager alloc] init]; + } + + if ([self.motionManager isAccelerometerAvailable] == YES) { + // Assign the update interval to the motion manager and start updates + [self.motionManager setAccelerometerUpdateInterval:kAccelerometerInterval/1000]; // expected in seconds + __weak CDVAccelerometer* weakSelf = self; + [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { + weakSelf.x = accelerometerData.acceleration.x; + weakSelf.y = accelerometerData.acceleration.y; + weakSelf.z = accelerometerData.acceleration.z; + weakSelf.timestamp = ([[NSDate date] timeIntervalSince1970] * 1000); + [weakSelf returnAccelInfo]; + }]; + + if (!self.isRunning) { + self.isRunning = YES; + } + } + else { + + NSLog(@"Running in Simulator? All gyro tests will fail."); + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_INVALID_ACTION messageAsString:@"Error. Accelerometer Not Available."]; + + [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; + } + +} + +- (void)onReset +{ + [self stop:nil]; +} + +- (void)stop:(CDVInvokedUrlCommand*)command +{ + if ([self.motionManager isAccelerometerAvailable] == YES) { + if (self.haveReturnedResult == NO){ + // block has not fired before stop was called, return whatever result we currently have + [self returnAccelInfo]; + } + [self.motionManager stopAccelerometerUpdates]; + } + self.isRunning = NO; +} + +- (void)returnAccelInfo +{ + // Create an acceleration object + NSMutableDictionary* accelProps = [NSMutableDictionary dictionaryWithCapacity:4]; + + [accelProps setValue:[NSNumber numberWithDouble:self.x * kGravitationalConstant] forKey:@"x"]; + [accelProps setValue:[NSNumber numberWithDouble:self.y * kGravitationalConstant] forKey:@"y"]; + [accelProps setValue:[NSNumber numberWithDouble:self.z * kGravitationalConstant] forKey:@"z"]; + [accelProps setValue:[NSNumber numberWithDouble:self.timestamp] forKey:@"timestamp"]; + + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:accelProps]; + [result setKeepCallback:[NSNumber numberWithBool:YES]]; + [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; + self.haveReturnedResult = YES; +} + +// TODO: Consider using filtering to isolate instantaneous data vs. gravity data -jm + +/* + #define kFilteringFactor 0.1 + + // Use a basic low-pass filter to keep only the gravity component of each axis. + grav_accelX = (acceleration.x * kFilteringFactor) + ( grav_accelX * (1.0 - kFilteringFactor)); + grav_accelY = (acceleration.y * kFilteringFactor) + ( grav_accelY * (1.0 - kFilteringFactor)); + grav_accelZ = (acceleration.z * kFilteringFactor) + ( grav_accelZ * (1.0 - kFilteringFactor)); + + // Subtract the low-pass value from the current value to get a simplified high-pass filter + instant_accelX = acceleration.x - ( (acceleration.x * kFilteringFactor) + (instant_accelX * (1.0 - kFilteringFactor)) ); + instant_accelY = acceleration.y - ( (acceleration.y * kFilteringFactor) + (instant_accelY * (1.0 - kFilteringFactor)) ); + instant_accelZ = acceleration.z - ( (acceleration.z * kFilteringFactor) + (instant_accelZ * (1.0 - kFilteringFactor)) ); + + + */ +@end diff --git a/plugins/cordova-plugin-device-motion/src/tizen/AccelerometerProxy.js b/plugins/cordova-plugin-device-motion/src/tizen/AccelerometerProxy.js new file mode 100644 index 0000000..8352ba9 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/tizen/AccelerometerProxy.js @@ -0,0 +1,49 @@ +/* + * + * 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(win) { + var accelerometerCallback = null; + + module.exports = { + start: function (successCallback, errorCallback) { + if (accelerometerCallback) { + win.removeEventListener("devicemotion", accelerometerCallback, true); + } + + accelerometerCallback = function (motion) { + successCallback({ + x: motion.accelerationIncludingGravity.x, + y: motion.accelerationIncludingGravity.y, + z: motion.accelerationIncludingGravity.z, + timestamp: new Date().getTime() + }); + }; + win.addEventListener("devicemotion", accelerometerCallback, true); + }, + + stop: function (successCallback, errorCallback) { + win.removeEventListener("devicemotion", accelerometerCallback, true); + accelerometerCallback = null; + } + }; + + require("cordova/tizen/commandProxy").add("Accelerometer", module.exports); +}(window)); diff --git a/plugins/cordova-plugin-device-motion/src/ubuntu/accelerometer.cpp b/plugins/cordova-plugin-device-motion/src/ubuntu/accelerometer.cpp new file mode 100644 index 0000000..74e3ead --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/ubuntu/accelerometer.cpp @@ -0,0 +1,58 @@ +/* + * + * 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. + * +*/ + +#include +#include "accelerometer.h" + +DeviceMotion::DeviceMotion(Cordova *cordova): CPlugin(cordova), _scId(0), _ecId(0) { + _accelerometerSource = QSharedPointer(new QAccelerometer()); + _sensorAvaliable = _accelerometerSource->start(); + connect(_accelerometerSource.data(), SIGNAL(readingChanged()), SLOT(updateSensor())); +} + +void DeviceMotion::start(int scId, int ecId) { + assert(_ecId == 0); + assert(_scId == 0); + + _ecId = ecId; + _scId = scId; + + if (!_sensorAvaliable) { + this->cb(ecId); + return; + } +} + +void DeviceMotion::stop(int, int) { + _scId = 0; + _ecId = 0; +} + +void DeviceMotion::updateSensor() { + QAccelerometerReading *accelerometer = _accelerometerSource->reading(); + + QVariantMap obj; + obj.insert("x", accelerometer->x()); + obj.insert("y", accelerometer->y()); + obj.insert("z", accelerometer->z()); + // accelerometer->timestamp() is not sutiable. + // Timestamps values are microseconds since _a_ fixed point(depend on backend). + obj.insert("timestamp", QDateTime::currentDateTime().toMSecsSinceEpoch()); + + if (_scId) + this->callbackWithoutRemove(_scId, CordovaInternal::format(obj)); +} diff --git a/plugins/cordova-plugin-device-motion/src/ubuntu/accelerometer.h b/plugins/cordova-plugin-device-motion/src/ubuntu/accelerometer.h new file mode 100644 index 0000000..3f36a16 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/ubuntu/accelerometer.h @@ -0,0 +1,55 @@ +/* + * + * 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. + * +*/ + +#ifndef ACCELEROMETER_H +#define ACCELEROMETER_H + +#include +#include +#include + +class DeviceMotion: public CPlugin { + Q_OBJECT +public: + explicit DeviceMotion(Cordova *cordova); + + virtual const QString fullName() override { + return DeviceMotion::fullID(); + } + + virtual const QString shortName() override { + return "Accelerometer"; + } + + static const QString fullID() { + return "Accelerometer"; + } + +public slots: + void start(int scId, int ecId); + void stop(int scId, int ecId); + +protected slots: + void updateSensor(); + +private: + int _scId, _ecId; + bool _sensorAvaliable; + QSharedPointer _accelerometerSource; +}; + +#endif diff --git a/plugins/cordova-plugin-device-motion/src/windows/AccelerometerProxy.js b/plugins/cordova-plugin-device-motion/src/windows/AccelerometerProxy.js new file mode 100644 index 0000000..aa07780 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/windows/AccelerometerProxy.js @@ -0,0 +1,73 @@ +/* + * + * 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:true */ + +var Acceleration = require('cordova-plugin-device-motion.Acceleration'); + +/* This is the actual implementation part that returns the result on Windows 8 +*/ +var gConstant = -9.81; + +module.exports = { + onDataChanged:null, + start:function(win,lose){ + + var accel = Windows.Devices.Sensors.Accelerometer.getDefault(); + if(!accel) { + if (lose) { + lose("No accelerometer found"); + } + } + else { + accel.reportInterval = Math.max(16,accel.minimumReportInterval); + + // store our bound function + this.onDataChanged = function(e) { + var a = e.reading; + win(new Acceleration(a.accelerationX * gConstant, a.accelerationY * gConstant, a.accelerationZ * gConstant), {keepCallback: true}); + }; + accel.addEventListener("readingchanged",this.onDataChanged); + + setTimeout(function(){ + var a = accel.getCurrentReading(); + win(new Acceleration(a.accelerationX * gConstant, a.accelerationY * gConstant, a.accelerationZ * gConstant), {keepCallback: true}); + },0); // async do later + } + }, + stop:function(win,lose){ + win = win || function(){}; + var accel = Windows.Devices.Sensors.Accelerometer.getDefault(); + if(!accel) { + if (lose) { + lose("No accelerometer found"); + } + } + else { + accel.removeEventListener("readingchanged",this.onDataChanged); + this.onDataChanged = null; + accel.reportInterval = 0; // back to the default + win(); + } + } +}; + +require("cordova/exec/proxy").add("Accelerometer",module.exports); diff --git a/plugins/cordova-plugin-device-motion/src/wp/Accelerometer.cs b/plugins/cordova-plugin-device-motion/src/wp/Accelerometer.cs new file mode 100644 index 0000000..33c5434 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/src/wp/Accelerometer.cs @@ -0,0 +1,179 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Threading; +using Microsoft.Devices.Sensors; +using System.Globalization; +using System.Diagnostics; +using System.Windows.Threading; + +namespace WPCordovaClassLib.Cordova.Commands +{ + /// + /// Captures device motion in the x, y, and z direction. + /// + public class Accelerometer : BaseCommand + { + #region Status codes and Constants + + public const int Stopped = 0; + public const int Starting = 1; + public const int Running = 2; + public const int ErrorFailedToStart = 3; + + public const double gConstant = -9.81; + + #endregion + + #region Static members + + /// + /// Status of listener + /// + private static int currentStatus; + + /// + /// Accelerometer + /// + private static readonly Windows.Devices.Sensors.Accelerometer accelerometer = Windows.Devices.Sensors.Accelerometer.GetDefault(); + + /// + /// Timer which is used to update + /// + private static Timer updateTimer; + + /// + /// Callback Id to report acceleration result in watch mode + /// + private static string watchCallbackId; + + #endregion + + /// + /// Starts listening for acceleration sensor + /// + /// status of listener + public void start(string options) + { + watchCallbackId = GetCallbackIdFromOptions(options); + + if (currentStatus == Running) + { + return; + } + + try + { + // we use 20ms as a minimum allowed update interval + int minReportInterval = Math.Max((int)accelerometer.MinimumReportInterval, 20); + + updateTimer = new Timer(ReportAccelerationValue, null, 0, minReportInterval); + this.SetStatus(Running); + + PluginResult result = new PluginResult(PluginResult.Status.OK, GetCurrentAccelerationFormatted()); + result.KeepCallback = true; + DispatchCommandResult(result, watchCallbackId); + } + catch (Exception ex) + { + this.SetStatus(ErrorFailedToStart); + DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart), watchCallbackId); + } + } + + public void stop(string options) + { + string callbackId = GetCallbackIdFromOptions(options); + + if (currentStatus == Running) + { + watchCallbackId = null; + updateTimer.Dispose(); + this.SetStatus(Stopped); + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId); + } + + public void getCurrentAcceleration(string options) + { + string callbackId = GetCallbackIdFromOptions(options); + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, GetCurrentAccelerationFormatted()), callbackId); + } + + private void ReportAccelerationValue(object stateInfo) + { + if (String.IsNullOrEmpty(watchCallbackId)) { + // soemthing goes wrong, callback has been called after stop.. + return; + } + string currentAccelerationFormatted = GetCurrentAccelerationFormatted(); + var result = currentAccelerationFormatted == null ? new PluginResult(PluginResult.Status.NO_RESULT) + : new PluginResult(PluginResult.Status.OK, currentAccelerationFormatted); + result.KeepCallback = true; + DispatchCommandResult(result, watchCallbackId); + } + + /// + /// Formats current coordinates into JSON format + /// + /// Coordinates in JSON format + private string GetCurrentAccelerationFormatted() + { + try + { + var currentReading = accelerometer.GetCurrentReading(); + var currentCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2}", + (currentReading.AccelerationX * gConstant).ToString("0.00000", CultureInfo.InvariantCulture), + (currentReading.AccelerationY * gConstant).ToString("0.00000", CultureInfo.InvariantCulture), + (currentReading.AccelerationZ * gConstant).ToString("0.00000", CultureInfo.InvariantCulture)); + + return "{" + currentCoordinates + "}"; + } + catch + { + return null; + } + } + + /// + /// Sets current status + /// + /// current status + private void SetStatus(int status) + { + currentStatus = status; + } + + private string GetCallbackIdFromOptions(string options) + { + try + { + string[] optionsString = JSON.JsonHelper.Deserialize(options); + + return optionsString[0]; + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), this.CurrentCommandCallbackId); + return null; + } + } + } +} diff --git a/plugins/cordova-plugin-device-motion/tests/plugin.xml b/plugins/cordova-plugin-device-motion/tests/plugin.xml new file mode 100644 index 0000000..54efe2a --- /dev/null +++ b/plugins/cordova-plugin-device-motion/tests/plugin.xml @@ -0,0 +1,29 @@ + + + + + Cordova Device Motion Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-device-motion/tests/tests.js b/plugins/cordova-plugin-device-motion/tests/tests.js new file mode 100644 index 0000000..5deec41 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/tests/tests.js @@ -0,0 +1,372 @@ +/* + * + * 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. + * +*/ + +/* jshint jasmine: true */ +/* global Windows */ + +exports.defineAutoTests = function () { + var isWindows = (cordova.platformId === "windows") || (cordova.platformId === "windows8"), + // Checking existence of accelerometer for windows platform + // Assumed that accelerometer always exists on other platforms. Extend + // condition to support accelerometer check on other platforms + isAccelExist = isWindows ? Windows.Devices.Sensors.Accelerometer.getDefault() !== null : true; + + describe('Accelerometer (navigator.accelerometer)', function () { + var fail = function(done) { + expect(true).toBe(false); + done(); + }; + + // This timeout is here to lessen the load on native accelerometer + // intensive use of which can lead to occasional test failures + afterEach(function(done) { + setTimeout(function() { + done(); + }, 100); + }); + + it("accelerometer.spec.1 should exist", function () { + expect(navigator.accelerometer).toBeDefined(); + }); + + describe("getCurrentAcceleration", function() { + it("accelerometer.spec.2 should exist", function() { + expect(typeof navigator.accelerometer.getCurrentAcceleration).toBeDefined(); + expect(typeof navigator.accelerometer.getCurrentAcceleration == 'function').toBe(true); + }); + + it("accelerometer.spec.3 success callback should be called with an Acceleration object", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var win = function(a) { + expect(a).toBeDefined(); + expect(a.x).toBeDefined(); + expect(typeof a.x == 'number').toBe(true); + expect(a.y).toBeDefined(); + expect(typeof a.y == 'number').toBe(true); + expect(a.z).toBeDefined(); + expect(typeof a.z == 'number').toBe(true); + expect(a.timestamp).toBeDefined(); + expect(typeof a.timestamp).toBe('number'); + done(); + }; + + var onError = function(err){ + console.log(err); + console.log("Skipping gyroscope tests, marking all as pending."); + isAccelExist = false; + expect(true).toBe(true); + done(); + }; + + navigator.accelerometer.getCurrentAcceleration(win, onError); + }); + + it("accelerometer.spec.4 success callback Acceleration object should have (reasonable) values for x, y and z expressed in m/s^2", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var reasonableThreshold = 15; + var win = function(a) { + expect(a.x).toBeLessThan(reasonableThreshold); + expect(a.x).toBeGreaterThan(reasonableThreshold * -1); + expect(a.y).toBeLessThan(reasonableThreshold); + expect(a.y).toBeGreaterThan(reasonableThreshold * -1); + expect(a.z).toBeLessThan(reasonableThreshold); + expect(a.z).toBeGreaterThan(reasonableThreshold * -1); + done(); + }; + + navigator.accelerometer.getCurrentAcceleration(win, fail.bind(null,done)); + }); + + it("accelerometer.spec.5 success callback Acceleration object should return a recent timestamp", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var veryRecently = (new Date()).getTime(); + // Need to check that dates returned are not vastly greater than a recent time stamp. + // In case the timestamps returned are ridiculously high + var reasonableTimeLimit = veryRecently + 5000; // 5 seconds from now + var win = function(a) { + expect(a.timestamp).toBeGreaterThan(veryRecently); + expect(a.timestamp).toBeLessThan(reasonableTimeLimit); + done(); + }; + + navigator.accelerometer.getCurrentAcceleration(win, fail.bind(null,done)); + }); + }); + + describe("watchAcceleration", function() { + var id; + + afterEach(function(done) { + if (id) { + navigator.accelerometer.clearWatch(id); + } + // clearWatch implementation is async but doesn't accept a cllback + // so let's give it some time before starting next spec + setTimeout(done, 100); + }); + + it("accelerometer.spec.6 should exist", function() { + expect(navigator.accelerometer.watchAcceleration).toBeDefined(); + expect(typeof navigator.accelerometer.watchAcceleration == 'function').toBe(true); + }); + + it("accelerometer.spec.7 success callback should be called with an Acceleration object", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var win = function(a) { + expect(a).toBeDefined(); + expect(a.x).toBeDefined(); + expect(typeof a.x == 'number').toBe(true); + expect(a.y).toBeDefined(); + expect(typeof a.y == 'number').toBe(true); + expect(a.z).toBeDefined(); + expect(typeof a.z == 'number').toBe(true); + expect(a.timestamp).toBeDefined(); + expect(typeof a.timestamp).toBe('number'); + done(); + }; + + id = navigator.accelerometer.watchAcceleration(win, fail.bind(null,done), {frequency:100}); + }); + + it("accelerometer.spec.8 success callback Acceleration object should have (reasonable) values for x, y and z expressed in m/s^2", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var reasonableThreshold = 15; + var win = function(a) { + expect(a.x).toBeLessThan(reasonableThreshold); + expect(a.x).toBeGreaterThan(reasonableThreshold * -1); + expect(a.y).toBeLessThan(reasonableThreshold); + expect(a.y).toBeGreaterThan(reasonableThreshold * -1); + expect(a.z).toBeLessThan(reasonableThreshold); + expect(a.z).toBeGreaterThan(reasonableThreshold * -1); + done(); + }; + + id = navigator.accelerometer.watchAcceleration(win, fail.bind(null,done), {frequency:100}); + }); + + it("accelerometer.spec.9 success callback Acceleration object should return a recent timestamp", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var veryRecently = (new Date()).getTime(); + // Need to check that dates returned are not vastly greater than a recent time stamp. + // In case the timestamps returned are ridiculously high + var reasonableTimeLimit = veryRecently + 5000; // 5 seconds from now + var win = function(a) { + expect(a.timestamp).toBeGreaterThan(veryRecently); + expect(a.timestamp).toBeLessThan(reasonableTimeLimit); + done(); + }; + + id = navigator.accelerometer.watchAcceleration(win, fail.bind(null,done), {frequency:100}); + }); + + it("accelerometer.spec.12 success callback should be preserved and called several times", function (done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var callbacksCallCount = 0, + callbacksCallTestCount = 3; + + var win = function (a) { + if (callbacksCallCount++ < callbacksCallTestCount) return; + expect(typeof a).toBe('object'); + done(); + }; + + id = navigator.accelerometer.watchAcceleration(win, fail.bind(null, done), { frequency: 100 }); + }); + }); + + describe("clearWatch", function() { + it("accelerometer.spec.10 should exist", function() { + expect(navigator.accelerometer.clearWatch).toBeDefined(); + expect(typeof navigator.accelerometer.clearWatch == 'function').toBe(true); + }); + + it("accelerometer.spec.11 should clear an existing watch", function(done) { + // skip the test if Accelerometer doesn't exist on this device + if (!isAccelExist) { + pending(); + } + var id; + + // expect win to get called exactly once + var win = function(a) { + // clear watch on first call + navigator.accelerometer.clearWatch(id); + // if win isn't called again in 201 ms we assume success + var tid = setTimeout(function() { + expect(true).toBe(true); + done(); + }, 101); + // if win is called again, clear the timeout and fail the test + win = function() { + clearTimeout(tid); + fail(done); + }; + }; + + // wrap the success call in a closure since the value of win changes between calls + id = navigator.accelerometer.watchAcceleration(function() { win(); }, fail.bind(null, done), {frequency:100}); + }); + }); + }); +}; + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +exports.defineManualTests = function (contentEl, createActionButton) { + function roundNumber(num) { + var dec = 3; + var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + return result; + } + + var watchAccelId = null; + + /** + * Set accelerometer status + */ + function setAccelStatus(status) { + document.getElementById('accel_status').innerHTML = status; + } + + /** + * Stop watching the acceleration + */ + function stopAccel() { + console.log("stopAccel()"); + setAccelStatus("Stopped"); + if (watchAccelId) { + navigator.accelerometer.clearWatch(watchAccelId); + watchAccelId = null; + } + } + + /** + * Start watching acceleration + */ + var watchAccel = function () { + console.log("watchAccel()"); + + // Success callback + var success = function (a) { + document.getElementById('x').innerHTML = roundNumber(a.x); + document.getElementById('y').innerHTML = roundNumber(a.y); + document.getElementById('z').innerHTML = roundNumber(a.z); + document.getElementById('t').innerHTML = a.timestamp; + }; + + // Fail callback + var fail = function (e) { + console.log("watchAccel fail callback with error code " + e); + stopAccel(); + setAccelStatus(e); + }; + + // Update acceleration every 1 sec + var opt = {}; + opt.frequency = 1000; + watchAccelId = navigator.accelerometer.watchAcceleration(success, fail, opt); + + setAccelStatus("Running"); + }; + + /** + * Get current acceleration + */ + var getAccel = function () { + console.log("getAccel()"); + + // Stop accel if running + stopAccel(); + + // Success callback + var success = function (a) { + document.getElementById('x').innerHTML = roundNumber(a.x); + document.getElementById('y').innerHTML = roundNumber(a.y); + document.getElementById('z').innerHTML = roundNumber(a.z); + document.getElementById('t').innerHTML = a.timestamp; + console.log("getAccel success callback"); + }; + + // Fail callback + var fail = function (e) { + console.log("getAccel fail callback with error code " + e); + setAccelStatus(e); + }; + + // Make call + var opt = {}; + navigator.accelerometer.getCurrentAcceleration(success, fail, opt); + }; + + /******************************************************************************/ + + var accelerometer_tests = '
' + + 'Expected result: Will update the status box with X, Y, and Z values when pressed. Status will read "Stopped"' + + '

' + + 'Expected result: When pressed, will start a watch on the accelerometer and update X,Y,Z values when movement is sensed. Status will read "Running"' + + '

' + + 'Expected result: Will clear the accelerometer watch, so X,Y,Z values will no longer be updated. Status will read "Stopped"'; + + contentEl.innerHTML = '
' + + 'Status: Stopped' + + '' + + '' + + '' + + '' + + '' + + '
X:
Y:
Z:
Timestamp:
' + + accelerometer_tests; + + createActionButton('Get Acceleration', function () { + getAccel(); + }, 'getAcceleration'); + + createActionButton('Start Watch', function () { + watchAccel(); + }, 'watchAcceleration'); + + createActionButton('Clear Watch', function () { + stopAccel(); + }, 'clearAcceleration'); +}; diff --git a/plugins/cordova-plugin-device-motion/www/Acceleration.js b/plugins/cordova-plugin-device-motion/www/Acceleration.js new file mode 100644 index 0000000..d1669b5 --- /dev/null +++ b/plugins/cordova-plugin-device-motion/www/Acceleration.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. + * +*/ + +var Acceleration = function(x, y, z, timestamp) { + this.x = x; + this.y = y; + this.z = z; + this.timestamp = timestamp || (new Date()).getTime(); +}; + +module.exports = Acceleration; diff --git a/plugins/cordova-plugin-device-motion/www/accelerometer.js b/plugins/cordova-plugin-device-motion/www/accelerometer.js new file mode 100644 index 0000000..21abe8b --- /dev/null +++ b/plugins/cordova-plugin-device-motion/www/accelerometer.js @@ -0,0 +1,202 @@ +/* + * + * 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. + * +*/ + +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var argscheck = require('cordova/argscheck'), + utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('./Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Timer used when faking up devicemotion events +var eventTimerId = null; + +// Tells native to start. +function start() { + exec(function (a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function (e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + accel = null; + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return { win: win, fail: fail }; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously acquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function (successCallback, errorCallback, options) { + argscheck.checkArgs('fFO', 'accelerometer.getCurrentAcceleration', arguments); + + if (cordova.platformId === "windowsphone") { + exec(function (a) { + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + successCallback(accel); + }, function (e) { + errorCallback(e); + }, "Accelerometer", "getCurrentAcceleration", []); + + return; + } + + var p; + var win = function (a) { + removeListeners(p); + successCallback(a); + }; + var fail = function (e) { + removeListeners(p); + if (errorCallback) { + errorCallback(e); + } + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously acquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function (successCallback, errorCallback, options) { + argscheck.checkArgs('fFO', 'accelerometer.watchAcceleration', arguments); + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function () { }, function (e) { + removeListeners(p); + if (errorCallback) { + errorCallback(e); + } + }); + listeners.push(p); + + timers[id] = { + timer: window.setInterval(function () { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners: p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retrieved a value, sample code does not check for null ... + if (accel) { + successCallback(accel); + } + } else { + start(); + } + + if (cordova.platformId === "browser" && !eventTimerId) { + // Start firing devicemotion events if we haven't already + var devicemotionEvent = new Event('devicemotion'); + eventTimerId = window.setInterval(function() { + window.dispatchEvent(devicemotionEvent); + }, 200); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function (id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + + if (eventTimerId && Object.keys(timers).length === 0) { + // No more watchers, so stop firing 'devicemotion' events + window.clearInterval(eventTimerId); + eventTimerId = null; + } + } + } +}; +module.exports = accelerometer; diff --git a/plugins/cordova-plugin-device-orientation/CONTRIBUTING.md b/plugins/cordova-plugin-device-orientation/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-device-orientation/LICENSE b/plugins/cordova-plugin-device-orientation/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/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/plugins/cordova-plugin-device-orientation/NOTICE b/plugins/cordova-plugin-device-orientation/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/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/plugins/cordova-plugin-device-orientation/README.md b/plugins/cordova-plugin-device-orientation/README.md new file mode 100644 index 0000000..226f376 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/README.md @@ -0,0 +1,217 @@ +--- +title: Device Orientation +description: Access compass data. +--- + + +|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI | +|:-:|:-:|:-:|:-:|:-:|:-:| +|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-device-orientation)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-device-orientation/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-device-orientation)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-device-orientation/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-device-orientation)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-device-orientation/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-device-orientation)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-device-orientation/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-device-orientation)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-device-orientation/)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device-orientation)| + +# cordova-plugin-device-orientation + + +This plugin provides access to the device's compass. The compass is a sensor +that detects the direction or heading that the device is pointed, typically +from the top of the device. It measures the heading in degrees from 0 to +359.99, where 0 is north. + +Access is via a global `navigator.compass` object. + +Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + +Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Device%20Orientation%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + + cordova plugin add cordova-plugin-device-orientation + +## Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Browser +- Firefox OS +- iOS +- Tizen +- Windows Phone 7 and 8 (if available in hardware) +- Windows 8 + +## Methods + +- navigator.compass.getCurrentHeading +- navigator.compass.watchHeading +- navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Get the current compass heading. The compass heading is returned via a `CompassHeading` +object using the `compassSuccess` callback function. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + +### Example + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + +## navigator.compass.watchHeading + +Gets the device's current heading at a regular interval. Each time the heading +is retrieved, the `headingSuccess` callback function is executed. + +The returned watch ID references the compass watch interval. The watch +ID can be used with `navigator.compass.clearWatch` to stop watching the navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + +`compassOptions` may contain the following keys: + +- __frequency__: How often to retrieve the compass heading in milliseconds. _(Number)_ (Default: 100) +- __filter__: The change in degrees required to initiate a watchHeading success callback. When this value is set, __frequency__ is ignored. _(Number)_ + +### Example + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Browser Quirks + +Values for current heading are randomly generated in order to simulate the compass. + +### iOS Quirks + +Only one `watchHeading` can be in effect at one time in iOS. If a +`watchHeading` uses a filter, calling `getCurrentHeading` or +`watchHeading` uses the existing filter value to specify heading +changes. Watching heading changes with a filter is more efficient than +with time intervals. + +### Amazon Fire OS Quirks + +- `filter` is not supported. + +### Android Quirks + +- No support for `filter`. + +### Firefox OS Quirks + +- No support for `filter`. + +### Tizen Quirks + +- No support for `filter`. + +### Windows Phone 7 and 8 Quirks + +- No support for `filter`. + +## navigator.compass.clearWatch + +Stop watching the compass referenced by the watch ID parameter. + + navigator.compass.clearWatch(watchID); + +- __watchID__: The ID returned by `navigator.compass.watchHeading`. + +### Example + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + +## CompassHeading + +A `CompassHeading` object is returned to the `compassSuccess` callback function. + +### Properties + +- __magneticHeading__: The heading in degrees from 0-359.99 at a single moment in time. _(Number)_ + +- __trueHeading__: The heading relative to the geographic North Pole in degrees 0-359.99 at a single moment in time. A negative value indicates that the true heading can't be determined. _(Number)_ + +- __headingAccuracy__: The deviation in degrees between the reported heading and the true heading. _(Number)_ + +- __timestamp__: The time at which this heading was determined. _(DOMTimeStamp)_ + + +### Amazon Fire OS Quirks + +- `trueHeading` is not supported, but reports the same value as `magneticHeading` + +- `headingAccuracy` is always 0 because there is no difference between the `magneticHeading` and `trueHeading` + +### Android Quirks + +- The `trueHeading` property is not supported, but reports the same value as `magneticHeading`. + +- The `headingAccuracy` property is always 0 because there is no difference between the `magneticHeading` and `trueHeading`. + +### Firefox OS Quirks + +- The `trueHeading` property is not supported, but reports the same value as `magneticHeading`. + +- The `headingAccuracy` property is always 0 because there is no difference between the `magneticHeading` and `trueHeading`. + +### iOS Quirks + +- The `trueHeading` property is only returned for location services enabled via `navigator.geolocation.watchLocation()`. + +## CompassError + +A `CompassError` object is returned to the `compassError` callback function when an error occurs. + +### Properties + +- __code__: One of the predefined error codes listed below. + +### Constants + +- `CompassError.COMPASS_INTERNAL_ERR` +- `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/RELEASENOTES.md b/plugins/cordova-plugin-device-orientation/RELEASENOTES.md new file mode 100644 index 0000000..d1b758e --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/RELEASENOTES.md @@ -0,0 +1,265 @@ + +# Release Notes + +### 1.0.5 (Dec 07, 2016) +* [CB-9179](https://issues.apache.org/jira/browse/CB-9179) (ios) Fixed trueHeading being always 0 +* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Updated version and RELEASENOTES.md for release 1.0.4 +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md +* This closes #16. Resolved in another commit. +* Removed ios quirk code documentation +* remove quirk code +* [CB-11091](https://issues.apache.org/jira/browse/CB-11091) Incremented plugin version. +* Updated version and RELEASENOTES.md for release 1.0.3 +* Remove 'warning' emoji, as it doesn't correctly display in the docs website: http://cordova.apache.org/docs/en/dev/cordova-plugin-device-orientation/index.html +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins +* chore: edit package.json license to match SPDX id +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Incremented plugin version. +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) linked issues in RELEASENOTES.md +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated version and RELEASENOTES.md for release 1.0.2 +* removed r prefix from tags +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated RELEASENOTES to be newest to oldest +* add JIRA issue tracker link +* [CB-4596](https://issues.apache.org/jira/browse/CB-4596) Fix timestamp to be DOMTimeStamp across the board +* Actually fixing the contribute link. +* Fixing contribute link. +* [CB-9426](https://issues.apache.org/jira/browse/CB-9426) Fix exception when using device orientation plugin on browser platform. +* remove travis-ci +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Incremented plugin version. +* [CB-9202](https://issues.apache.org/jira/browse/CB-9202) updated repo url to github mirror in package.json +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Updated version and RELEASENOTES.md for release 1.0.1 +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* fix npm md issue +* Remove console log message from test +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version in package.json for release 1.0.0 +* Revert "CB-8858 Incremented plugin version." +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version and RELEASENOTES.md for release 1.0.0 +* [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) updated windows and tizen specific references of old id to new id +* [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 +* [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 +* [CB-8659](https://issues.apache.org/jira/browse/CB-8659) ios: 4.0.x Compatibility: Remove use of deprecated headers +* force async callbacks +* Updated plugin to be 'windows' instead of 'windows8' +* [CB-8614](https://issues.apache.org/jira/browse/CB-8614) Fixed getCurrentHeading and watchHeading on windows platform +* [CB-8563](https://issues.apache.org/jira/browse/CB-8563) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8458](https://issues.apache.org/jira/browse/CB-8458) Fixes false failure of test, when compass hardware is not available +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Incremented plugin version. +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.3.11 +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) Use argumentForIndex rather than NSArray extension +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Incremented plugin version. +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Updated version and RELEASENOTES.md for release 0.3.10 +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention deviceready in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Incremented plugin version. +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Updated version and RELEASENOTES.md for release 0.3.9 +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* Fixed problem with watchCompass if pressed twice +* [CB-7086](https://issues.apache.org/jira/browse/CB-7086) Renamed dir, added nested plugin.xml +* added documentation for manual tests +* Fixed problem with watchCompass if pressed twice +* [CB-7086](https://issues.apache.org/jira/browse/CB-7086) Renamed dir, added nested plugin.xml +* added documentation for manual tests +* Updated docs for browser +* Add support for the browser +* CB-7249cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* [CB-6960](https://issues.apache.org/jira/browse/CB-6960) Added manual tests +* [CB-6960](https://issues.apache.org/jira/browse/CB-6960) Port compass tests to plugin-test-framework +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Incremented plugin version. +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Updated version and RELEASENOTES.md for release 0.3.8 +* update compass.js +* [CB-7187](https://issues.apache.org/jira/browse/CB-7187) ios: Add explicit dependency on CoreLocation.framework +* [CB-7187](https://issues.apache.org/jira/browse/CB-7187) Delete unused #import of CDVShared.h +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Incremented plugin version. +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Updated version and RELEASENOTES.md for release 0.3.7 +* [CB-6799](https://issues.apache.org/jira/browse/CB-6799) Add license +* windows8. makes getHeading callback spec compliant +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Incremented plugin version on dev branch. +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Updated version and RELEASENOTES.md for release 0.3.6 +* [CB-6465](https://issues.apache.org/jira/browse/CB-6465) Add license headers to Tizen code +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460) Update license headers +* [CB-6381](https://issues.apache.org/jira/browse/CB-6381) unexpected error object [WP8] +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422) [windows8] use cordova/exec/proxy +* Add NOTICE file +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Incremented plugin version on dev branch. +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 0.3.5 +* [ubuntu] request sensors permission +* [ubuntu] add missing files +* Add support for Tizen. +* FFOS info added +* Delete stale test/ directory +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Incremented plugin version on dev branch. +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Updated version and RELEASENOTES.md for release 0.3.4 +* [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 Compass plugin +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Incremented plugin version on dev branch. +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Updated version and RELEASENOTES.md for release 0.3.3 +* add ubuntu platform +* 1. Added amazon-fireos platform. 2. Change to use amazon-fireos as a platform if user agent string contains 'cordova-amazon-fireos'. +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) Updated version and RELEASENOTES.md for release 0.3.2 +* orientation plugin +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128) added repo + issue tag to plugin.xml for device orientation plugin +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Updated version and RELEASENOTES.md for release 0.3.1 +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming id +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming core in CompassProxy +* [CB-4900](https://issues.apache.org/jira/browse/CB-4900) Windows 8 Compass plugin have extra define breaks plugin loading +* [windows8] commandProxy was moved +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Updated version and changelog +* [CB-3687](https://issues.apache.org/jira/browse/CB-3687) Added compass plugin support for blackberry10 +* [CB-4595](https://issues.apache.org/jira/browse/CB-4595) updated version +* [windows8] just pass back the code to fail, so we don't get nested error codes : code.code = 20 +* [windows8] require/use CompassError +* [CB-4417](https://issues.apache.org/jira/browse/CB-4417) Move cordova-plugin-device-orientation to its own Java package. +* updated namespace +* updated nametag and readme +* [plugin.xml] adding android namespace +* [plugin.xml] standardizing license + meta +* [license] adding apache license file +* [Windows8][CB-4439] Add windows8 support +* updating plugin.xml with registry data + +### 1.0.4 (Sep 08, 2016) +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md +* Removed ios quirk code + documentation + +### 1.0.3 (Apr 15, 2016) +* Remove `warning` emoji, as it doesn't correctly display in the docs website: http://cordova.apache.org/docs/en/dev/cordova-plugin-device-orientation/index.html +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins + +### 1.0.2 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* [CB-4596](https://issues.apache.org/jira/browse/CB-4596) Fix `timestamp` to be `DOMTimeStamp` across the board +* Fixing contribute link. +* [CB-9426](https://issues.apache.org/jira/browse/CB-9426) Fix exception when using device orientation plugin on **browser** platform. + +### 1.0.1 (Jun 17, 2015) +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* fix npm md issue +* Remove console log message from test + +### 1.0.0 (Apr 15, 2015) +* [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) updated windows and tizen specific references of old id to new id +* [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 +* [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 +* [CB-8659](https://issues.apache.org/jira/browse/CB-8659): ios: 4.0.x Compatibility: Remove use of deprecated headers +* force async callbacks +* Updated plugin to be 'windows' instead of 'windows8' +* [CB-8614](https://issues.apache.org/jira/browse/CB-8614) Fixed getCurrentHeading and watchHeading on windows platform +* [CB-8563](https://issues.apache.org/jira/browse/CB-8563) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8458](https://issues.apache.org/jira/browse/CB-8458) Fixes false failure of test, when compass hardware is not available + +### 0.3.11 (Feb 04, 2015) +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension + +### 0.3.10 (Dec 02, 2014) +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin + +### 0.3.9 (Sep 17, 2014) +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-device-orientation documentation translation: cordova-plugin-device-orientation +* Fixed problem with watchCompass if pressed twice +* [CB-7086](https://issues.apache.org/jira/browse/CB-7086) Renamed dir, added nested plugin.xml +* added documentation for manual tests +* Fixed problem with watchCompass if pressed twice +* [CB-7086](https://issues.apache.org/jira/browse/CB-7086) Renamed dir, added nested plugin.xml +* added documentation for manual tests +* Updated docs for browser +* Add support for the browser +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-device-orientation documentation translation +* [CB-6960](https://issues.apache.org/jira/browse/CB-6960) Added manual tests +* [CB-6960](https://issues.apache.org/jira/browse/CB-6960) Port compass tests to plugin-test-framework + +### 0.3.8 (Aug 06, 2014) +* **FFOS** update compass.js +* [CB-7187](https://issues.apache.org/jira/browse/CB-7187) ios: Add explicit dependency on CoreLocation.framework +* [CB-7187](https://issues.apache.org/jira/browse/CB-7187) Delete unused #import of CDVShared.h + +### 0.3.7 (Jun 05, 2014) +* [CB-6799](https://issues.apache.org/jira/browse/CB-6799) Add license +* windows8. makes getHeading callback spec compliant +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md + +### 0.3.6 (Apr 17, 2014) +* [CB-6381](https://issues.apache.org/jira/browse/CB-6381): [WP8] unexpected error object +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* [CB-6465](https://issues.apache.org/jira/browse/CB-6465): Add license headers to Tizen code +* Add NOTICE file + +### 0.3.5 (Feb 05, 2014) +* [ubuntu] request sensors permission +* [ubuntu] add missing files +* Add support for Tizen. +* FFOS info added + +### 0.3.4 (Jan 02, 2014) +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Compass plugin + +### 0.3.3 (Dec 4, 2013) +* add ubuntu platform +* 1. Added amazon-fireos platform. 2. Change to use amazon-fireos as a platform if user agent string contains 'cordova-amazon-fireos'. + +### 0.3.2 (Oct 28, 2013) +* orientation plugin +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for device orientation plugin +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.3.1 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming id +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming core in CompassProxy +* [CB-4900](https://issues.apache.org/jira/browse/CB-4900) Windows 8 Compass plugin have extra define breaks plugin loading +* [windows8] commandProxy was moved +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. + +### 0.3.0 (Sept 5, 2013) +* [CB-3687](https://issues.apache.org/jira/browse/CB-3687) Added blackberry10 support diff --git a/plugins/cordova-plugin-device-orientation/doc/de/README.md b/plugins/cordova-plugin-device-orientation/doc/de/README.md new file mode 100644 index 0000000..c5be422 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/de/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +Dieses Plugin ermöglicht den Zugriff auf das Gerät Kompass. Der Kompass ist ein Sensor, der erkennt die Richtung oder Position, dass das Gerät in der Regel von der Oberseite des Geräts gezeigt wird. Er misst die Überschrift im Grad von 0 bis 359.99, 0 wo Norden ist. + +Der Zugang ist über eine globale `navigator.compass`-Objekt. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Installation + + cordova plugin add cordova-plugin-device-orientation + + +## Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 und 8 (falls verfügbar in Hardware) + * Windows 8 + +## Methoden + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Erhalten Sie aktuelle Kompassrichtung. Die Kompassrichtung wird über ein `CompassHeading`-Objekt mithilfe der `compassSuccess`-Callback-Funktion zurückgegeben. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### Beispiel + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Ruft das Gerät an den aktuellen Kurs in regelmäßigen Abständen. Jedes Mal, wenn die Überschrift abgerufen wird, wird die Callback-Funktion `headingSuccess` ausgeführt. + +Die zurückgegebenen Watch-ID verweist das Kompass-Uhr-Intervall. Die Uhr ID mit `navigator.compass.clearWatch` einsetzbar, um gerade die navigator.compass zu stoppen. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` enthalten die folgenden Schlüssel: + + * **Häufigkeit**: wie oft die Kompassrichtung in Millisekunden abrufen. *(Anzahl)* (Default: 100) + * **Filter**: die Veränderung der Grad benötigt, um einen WatchHeading Erfolg Rückruf initiiert. Wenn dieser Wert festgelegt ist, wird die **Häufigkeit** ignoriert. *(Anzahl)* + +### Beispiel + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Browser-Eigenheiten + +Werte für aktuelle Überschrift werden nach dem Zufallsprinzip generiert, um den Kompass zu simulieren. + +### iOS Macken + +Nur ein `watchHeading` kann in der Tat auf einmal in iOS sein. Wenn ein `watchHeading` einen Filter verwendet, wird durch Aufrufen von `getCurrentHeading` oder `watchHeading` den vorhandenen Filterwert Überschrift Änderungen angegeben. Überschrift Veränderungen beobachten, mit einem Filter ist effizienter als mit Zeitintervallen. + +### Amazon Fire OS Macken + + * `filter`wird nicht unterstützt. + +### Android Eigenarten + + * Keine Unterstützung für`filter`. + +### Firefox OS Macken + + * Keine Unterstützung für`filter`. + +### Tizen Macken + + * Keine Unterstützung für`filter`. + +### Windows Phone 7 und 8 Eigenarten + + * Keine Unterstützung für`filter`. + +## navigator.compass.clearWatch + +Stoppen Sie, beobachten den Kompass auf der Watch-ID-Parameter verweist. + + navigator.compass.clearWatch(watchID); + + + * **WatchID**: die ID von zurückgegeben`navigator.compass.watchHeading`. + +### Beispiel + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +Ein `CompassHeading`-Objekt wird an die `compassSuccess`-Callback-Funktion zurückgegeben. + +### Eigenschaften + + * **MagneticHeading**: die Überschrift in Grad von 0-359.99 zu einem einzigen Zeitpunkt. *(Anzahl)* + + * **TrueHeading**: die Überschrift im Verhältnis zu den geografischen Nordpol in Grad 0-359.99 zu einem einzigen Zeitpunkt. Ein negativer Wert bedeutet, dass die wahre Überschrift nicht bestimmt werden kann. *(Anzahl)* + + * **HeadingAccuracy**: die Abweichung in Grad zwischen der gemeldeten Überschrift und die wahre Richtung. *(Anzahl)* + + * **Timestamp**: die Zeit, an dem dieser Rubrik bestimmt war. *(Millisekunden)* + +### Amazon Fire OS Macken + + * `trueHeading`wird nicht unterstützt, aber meldet den gleichen Wert wie`magneticHeading` + + * `headingAccuracy`ist immer 0 da es keinen Unterschied zwischen gibt der `magneticHeading` und`trueHeading` + +### Android Eigenarten + + * Die `trueHeading` -Eigenschaft wird nicht unterstützt, jedoch meldet den gleichen Wert wie`magneticHeading`. + + * Die `headingAccuracy` -Eigenschaft ist immer 0 da es keinen Unterschied zwischen gibt der `magneticHeading` und`trueHeading`. + +### Firefox OS Macken + + * Die `trueHeading` -Eigenschaft wird nicht unterstützt, jedoch meldet den gleichen Wert wie`magneticHeading`. + + * Die `headingAccuracy` -Eigenschaft ist immer 0 da es keinen Unterschied zwischen gibt der `magneticHeading` und`trueHeading`. + +### iOS Macken + + * Die `trueHeading` -Eigenschaft nur für Ortungsdienste aktiviert über zurückgegeben`navigator.geolocation.watchLocation()`. + +## CompassError + +Ein `CompassError`-Objekt wird an die `compassError`-Callback-Funktion zurückgegeben, wenn ein Fehler auftritt. + +### Eigenschaften + + * **Code**: einer der vordefinierten Fehlercodes aufgeführt. + +### Konstanten + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/de/index.md b/plugins/cordova-plugin-device-orientation/doc/de/index.md new file mode 100644 index 0000000..c13308e --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/de/index.md @@ -0,0 +1,202 @@ + + +# cordova-plugin-device-orientation + +Dieses Plugin ermöglicht den Zugriff auf das Gerät Kompass. Der Kompass ist ein Sensor, der erkennt die Richtung oder Position, dass das Gerät in der Regel von der Oberseite des Geräts gezeigt wird. Er misst die Überschrift im Grad von 0 bis 359.99, 0 wo Norden ist. + +Der Zugang ist über eine globale `navigator.compass`-Objekt. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Installation + + cordova plugin add cordova-plugin-device-orientation + + +## Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 und 8 (falls verfügbar in Hardware) +* Windows 8 + +## Methoden + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Erhalten Sie aktuelle Kompassrichtung. Die Kompassrichtung wird über ein `CompassHeading`-Objekt mithilfe der `compassSuccess`-Callback-Funktion zurückgegeben. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### Beispiel + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Ruft das Gerät an den aktuellen Kurs in regelmäßigen Abständen. Jedes Mal, wenn die Überschrift abgerufen wird, wird die Callback-Funktion `headingSuccess` ausgeführt. + +Die zurückgegebenen Watch-ID verweist das Kompass-Uhr-Intervall. Die Uhr ID mit `navigator.compass.clearWatch` einsetzbar, um gerade die navigator.compass zu stoppen. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` enthalten die folgenden Schlüssel: + +* **Häufigkeit**: wie oft die Kompassrichtung in Millisekunden abrufen. *(Anzahl)* (Default: 100) +* **Filter**: die Veränderung der Grad benötigt, um einen WatchHeading Erfolg Rückruf initiiert. Wenn dieser Wert festgelegt ist, wird die **Häufigkeit** ignoriert. *(Anzahl)* + +### Beispiel + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Browser-Eigenheiten + +Werte für aktuelle Überschrift werden nach dem Zufallsprinzip generiert, um den Kompass zu simulieren. + +### iOS Macken + +Nur ein `watchHeading` kann in der Tat auf einmal in iOS sein. Wenn ein `watchHeading` einen Filter verwendet, wird durch Aufrufen von `getCurrentHeading` oder `watchHeading` den vorhandenen Filterwert Überschrift Änderungen angegeben. Überschrift Veränderungen beobachten, mit einem Filter ist effizienter als mit Zeitintervallen. + +### Amazon Fire OS Macken + +* `filter`wird nicht unterstützt. + +### Android Eigenarten + +* Keine Unterstützung für`filter`. + +### Firefox OS Macken + +* Keine Unterstützung für`filter`. + +### Tizen Macken + +* Keine Unterstützung für`filter`. + +### Windows Phone 7 und 8 Eigenarten + +* Keine Unterstützung für`filter`. + +## navigator.compass.clearWatch + +Stoppen Sie, beobachten den Kompass auf der Watch-ID-Parameter verweist. + + navigator.compass.clearWatch(watchID); + + +* **WatchID**: die ID von zurückgegeben`navigator.compass.watchHeading`. + +### Beispiel + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +Ein `CompassHeading`-Objekt wird an die `compassSuccess`-Callback-Funktion zurückgegeben. + +### Eigenschaften + +* **MagneticHeading**: die Überschrift in Grad von 0-359.99 zu einem einzigen Zeitpunkt. *(Anzahl)* + +* **TrueHeading**: die Überschrift im Verhältnis zu den geografischen Nordpol in Grad 0-359.99 zu einem einzigen Zeitpunkt. Ein negativer Wert bedeutet, dass die wahre Überschrift nicht bestimmt werden kann. *(Anzahl)* + +* **HeadingAccuracy**: die Abweichung in Grad zwischen der gemeldeten Überschrift und die wahre Richtung. *(Anzahl)* + +* **Timestamp**: die Zeit, an dem dieser Rubrik bestimmt war. *(Millisekunden)* + +### Amazon Fire OS Macken + +* `trueHeading`wird nicht unterstützt, aber meldet den gleichen Wert wie`magneticHeading` + +* `headingAccuracy`ist immer 0 da es keinen Unterschied zwischen gibt der `magneticHeading` und`trueHeading` + +### Android Eigenarten + +* Die `trueHeading` -Eigenschaft wird nicht unterstützt, jedoch meldet den gleichen Wert wie`magneticHeading`. + +* Die `headingAccuracy` -Eigenschaft ist immer 0 da es keinen Unterschied zwischen gibt der `magneticHeading` und`trueHeading`. + +### Firefox OS Macken + +* Die `trueHeading` -Eigenschaft wird nicht unterstützt, jedoch meldet den gleichen Wert wie`magneticHeading`. + +* Die `headingAccuracy` -Eigenschaft ist immer 0 da es keinen Unterschied zwischen gibt der `magneticHeading` und`trueHeading`. + +### iOS Macken + +* Die `trueHeading` -Eigenschaft nur für Ortungsdienste aktiviert über zurückgegeben`navigator.geolocation.watchLocation()`. + +## CompassError + +Ein `CompassError`-Objekt wird an die `compassError`-Callback-Funktion zurückgegeben, wenn ein Fehler auftritt. + +### Eigenschaften + +* **Code**: einer der vordefinierten Fehlercodes aufgeführt. + +### Konstanten + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/es/README.md b/plugins/cordova-plugin-device-orientation/doc/es/README.md new file mode 100644 index 0000000..9ef441c --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/es/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +Este plugin proporciona acceso al compás del dispositivo. La brújula es un sensor que detecta la dirección o rumbo que el dispositivo está apuntado, normalmente desde la parte superior del dispositivo. Mide el rumbo en grados de 0 a 359.99, donde 0 es el norte. + +El acceso es por un global `navigator.compass` objeto. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Instalación + + cordova plugin add cordova-plugin-device-orientation + + +## Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Explorador + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 y 8 (si está disponible en el hardware) + * Windows 8 + +## Métodos + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Conseguir el actual rumbo de la brújula. El rumbo de la brújula es devuelto vía un `CompassHeading` objeto usando la `compassSuccess` función de callback. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### Ejemplo + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Obtiene el título actual del dispositivo a intervalos regulares. Cada vez que se recupera el título, el `headingSuccess` se ejecuta la función callback. + +El identificador devuelto reloj referencias el intervalo reloj brújula. El reloj ID puede utilizarse con `navigator.compass.clearWatch` para dejar de ver la navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions`puede contener las siguientes claves: + + * **frecuencia**: frecuencia con la que recuperar el rumbo de la brújula en milisegundos. *(Número)* (Por defecto: 100) + * **filtro**: el cambio de grados necesarios para iniciar una devolución de llamada de éxito watchHeading. Cuando se establece este valor, **frecuencia** es ignorado. *(Número)* + +### Ejemplo + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Navegador rarezas + +Los valores de partida actual son generados al azar para simular la brújula. + +### iOS rarezas + +Solamente un `watchHeading` puede ser en efecto a la vez en iOS. Si un `watchHeading` utiliza un filtro, llamando a `getCurrentHeading` o `watchHeading` utiliza el valor existente del filtro para especificar los cambios de rumbo. Observando los cambios de rumbo con un filtro es más eficiente que con intervalos de tiempo. + +### Amazon fuego OS rarezas + + * `filter`No se admite. + +### Rarezas Android + + * No hay soporte para`filter`. + +### Firefox OS rarezas + + * No hay soporte para`filter`. + +### Rarezas Tizen + + * No hay soporte para`filter`. + +### Windows Phone 7 y 8 rarezas + + * No hay soporte para`filter`. + +## navigator.compass.clearWatch + +Deja de mirar la brújula al que hace referencia el parámetro ID de reloj. + + navigator.compass.clearWatch(watchID); + + + * **watchID**: el identificador devuelto por`navigator.compass.watchHeading`. + +### Ejemplo + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +A `CompassHeading` objeto es devuelto a la `compassSuccess` función de callback. + +### Propiedades + + * **magneticHeading**: el rumbo en grados de 0-359.99 en un solo momento. *(Número)* + + * **trueHeading**: el título en relación con el polo norte geográfico en grados 0-359.99 en un solo momento. Un valor negativo indica que no se puede determinar el rumbo verdadero. *(Número)* + + * **headingAccuracy**: la desviación en grados entre el rumbo divulgado y el rumbo verdadero. *(Número)* + + * **timestamp**: el momento en el cual se determinó esta partida. *(milisegundos)* + +### Amazon fuego OS rarezas + + * `trueHeading`No es compatible, pero el mismo valor que los informes`magneticHeading` + + * `headingAccuracy`es siempre 0 porque no hay ninguna diferencia entre el `magneticHeading` y`trueHeading` + +### Rarezas Android + + * El `trueHeading` propiedad no es compatible, pero el mismo valor que los informes`magneticHeading`. + + * El `headingAccuracy` propiedad es siempre 0 porque no hay ninguna diferencia entre el `magneticHeading` y`trueHeading`. + +### Firefox OS rarezas + + * El `trueHeading` propiedad no es compatible, pero el mismo valor que los informes`magneticHeading`. + + * El `headingAccuracy` propiedad es siempre 0 porque no hay ninguna diferencia entre el `magneticHeading` y`trueHeading`. + +### iOS rarezas + + * El `trueHeading` propiedad es devuelto sólo para servicios de localización habilitados mediante`navigator.geolocation.watchLocation()`. + +## CompassError + +A `CompassError` objeto es devuelto a la `compassError` función de devolución de llamada cuando se produce un error. + +### Propiedades + + * **code**: uno de los códigos de error predefinido enumerados a continuación. + +### Constantes + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/es/index.md b/plugins/cordova-plugin-device-orientation/doc/es/index.md new file mode 100644 index 0000000..5939d00 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/es/index.md @@ -0,0 +1,189 @@ + + +# cordova-plugin-device-orientation + +Este plugin proporciona acceso al compás del dispositivo. La brújula es un sensor que detecta la dirección o rumbo que el dispositivo está apuntado, normalmente desde la parte superior del dispositivo. Mide el rumbo en grados de 0 a 359.99, donde 0 es el norte. + +El acceso es por un global `navigator.compass` objeto. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(navigator.compass)}; + + +## Instalación + + Cordova plugin añade cordova-plugin-device-orientación + + +## Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Explorador +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 y 8 (si está disponible en el hardware) +* Windows 8 + +## Métodos + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Conseguir el actual rumbo de la brújula. El rumbo de la brújula es devuelto vía un `CompassHeading` objeto usando la `compassSuccess` función de callback. + + navigator.compass.getCurrentHeading (compassSuccess, compassError); + + +### Ejemplo + + function onSuccess(heading) {alert (' dirige: ' + heading.magneticHeading);}; + + function onError(error) {alert ('CompassError: "+ error.code);}; + + navigator.compass.getCurrentHeading (onSuccess, onError); + + +## navigator.compass.watchHeading + +Obtiene el título actual del dispositivo a intervalos regulares. Cada vez que se recupera el título, el `headingSuccess` se ejecuta la función callback. + +El identificador devuelto reloj referencias el intervalo reloj brújula. El reloj ID puede utilizarse con `navigator.compass.clearWatch` para dejar de ver la navigator.compass. + + var watchID = navigator.compass.watchHeading (compassSuccess, compassError, [compassOptions]); + + +`compassOptions`puede contener las siguientes claves: + +* **frecuencia**: frecuencia con la que recuperar el rumbo de la brújula en milisegundos. *(Número)* (Por defecto: 100) +* **filtro**: el cambio de grados necesarios para iniciar una devolución de llamada de éxito watchHeading. Cuando se establece este valor, **frecuencia** es ignorado. *(Número)* + +### Ejemplo + + function onSuccess(heading) {var elemento = document.getElementById('heading'); + element.innerHTML = ' Dirección: "+ heading.magneticHeading; + }; + + function onError(compassError) {alert (' error del compás: ' + compassError.code);}; + + var opciones = { + frequency: 3000 + }; Actualizar cada 3 segundos var watchID = navigator.compass.watchHeading (onSuccess, onError, opciones); + + +### Navegador rarezas + +Los valores de partida actual son generados al azar para simular la brújula. + +### iOS rarezas + +Solamente un `watchHeading` puede ser en efecto a la vez en iOS. Si un `watchHeading` utiliza un filtro, llamando a `getCurrentHeading` o `watchHeading` utiliza el valor existente del filtro para especificar los cambios de rumbo. Observando los cambios de rumbo con un filtro es más eficiente que con intervalos de tiempo. + +### Amazon fuego OS rarezas + +* `filter`No se admite. + +### Rarezas Android + +* No hay soporte para`filter`. + +### Firefox OS rarezas + +* No hay soporte para`filter`. + +### Rarezas Tizen + +* No hay soporte para`filter`. + +### Windows Phone 7 y 8 rarezas + +* No hay soporte para`filter`. + +## navigator.compass.clearWatch + +Deja de mirar la brújula al que hace referencia el parámetro ID de reloj. + + navigator.compass.clearWatch(watchID); + + +* **watchID**: el identificador devuelto por`navigator.compass.watchHeading`. + +### Ejemplo + + var watchID = navigator.compass.watchHeading (onSuccess, onError, opciones); + + ... adelante... navigator.compass.clearWatch(watchID); + + +## CompassHeading + +A `CompassHeading` objeto es devuelto a la `compassSuccess` función de callback. + +### Propiedades + +* **magneticHeading**: el rumbo en grados de 0-359.99 en un solo momento. *(Número)* + +* **trueHeading**: el título en relación con el polo norte geográfico en grados 0-359.99 en un solo momento. Un valor negativo indica que no se puede determinar el rumbo verdadero. *(Número)* + +* **headingAccuracy**: la desviación en grados entre el rumbo divulgado y el rumbo verdadero. *(Número)* + +* **timestamp**: el momento en el cual se determinó esta partida. *(milisegundos)* + +### Amazon fuego OS rarezas + +* `trueHeading`No es compatible, pero el mismo valor que los informes`magneticHeading` + +* `headingAccuracy`es siempre 0 porque no hay ninguna diferencia entre el `magneticHeading` y`trueHeading` + +### Rarezas Android + +* El `trueHeading` propiedad no es compatible, pero el mismo valor que los informes`magneticHeading`. + +* El `headingAccuracy` propiedad es siempre 0 porque no hay ninguna diferencia entre el `magneticHeading` y`trueHeading`. + +### Firefox OS rarezas + +* El `trueHeading` propiedad no es compatible, pero el mismo valor que los informes`magneticHeading`. + +* El `headingAccuracy` propiedad es siempre 0 porque no hay ninguna diferencia entre el `magneticHeading` y`trueHeading`. + +### iOS rarezas + +* El `trueHeading` propiedad es devuelto sólo para servicios de localización habilitados mediante`navigator.geolocation.watchLocation()`. + +## CompassError + +A `CompassError` objeto es devuelto a la `compassError` función de devolución de llamada cuando se produce un error. + +### Propiedades + +* **code**: uno de los códigos de error predefinido enumerados a continuación. + +### Constantes + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/fr/README.md b/plugins/cordova-plugin-device-orientation/doc/fr/README.md new file mode 100644 index 0000000..da61200 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/fr/README.md @@ -0,0 +1,191 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +Ce plugin permet d'accéder à la boussole de l'appareil. La boussole est un capteur qui détecte la direction ou la position que l'appareil est pointé, généralement par le haut de l'appareil. Il mesure la position en degrés de 0 à 359.99, où 0 est vers le Nord. + +Accès se fait par un global `navigator.compass` objet. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.compass);} + + +## Installation + + cordova plugin add cordova-plugin-device-orientation + + +## Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Navigateur + * Firefox OS + * iOS + * Paciarelli + * Windows Phone 7 et 8 (s'il est disponible dans le matériel) + * Windows 8 + +## Méthodes + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Téléchargez la cours de la boussole. La boussole est renvoyé via un `CompassHeading` s'opposer à l'aide de la `compassSuccess` fonction de rappel. + + navigator.compass.getCurrentHeading (compassSuccess, compassError) ; + + +### Exemple + + function onSuccess(heading) {alert (' intitulé: "+ heading.magneticHeading);} ; + + function onError(error) {alert ('CompassError: ' + error.code);} ; + + navigator.compass.getCurrentHeading (onSuccess, onError) ; + + +## navigator.compass.watchHeading + +Obtient la position actuelle de l'appareil à intervalle régulier. Chaque fois que le titre est récupéré, la `headingSuccess` fonction de rappel est exécutée. + +Le code retourné montre fait référence à l'intervalle montre boussole. La montre ID peut être utilisé avec `navigator.compass.clearWatch` d'arrêter de regarder le navigator.compass. + + var watchID = navigator.compass.watchHeading (compassSuccess, compassError, [compassOptions]) ; + + +`compassOptions`peut contenir les clés suivantes : + + * **fréquence** : la fréquence de récupération de la boussole en millisecondes. *(Nombre)* (Par défaut : 100) + * **filtre**: le changement en degrés nécessaires pour lancer un rappel de succès watchHeading. Lorsque cette valeur est définie, la **fréquence** est ignoré. *(Nombre)* + +### Exemple + + function onSuccess(heading) {var element = document.getElementById('heading') ; + element.innerHTML = "intitulé:" + heading.magneticHeading ; + }; + + function onError(compassError) {alert (' erreur de boussole: "+ compassError.code);} ; + + options de var = { + frequency: 3000 + } ; Mise à jour chaque 3 secondes var watchID = navigator.compass.watchHeading (onSuccess, onError, options) ; + + +### Bizarreries navigateur + +Valeurs pour la rubrique actuelle sont générés au hasard afin de simuler la boussole. + +### Notes au sujet d'iOS + +Seul `watchHeading` peut être en effet à un moment donné dans l'iOS. Si un `watchHeading` utilise un filtre, appeler `getCurrentHeading` ou `watchHeading` utilise la valeur existante de filtre pour spécifier des changements de position. En regardant les changements de position avec un filtre est plus efficace qu'avec des intervalles de temps. + +### Amazon Fire OS Quirks + + * `filter`n'est pas pris en charge. + +### Quirks Android + + * Pas de support pour`filter`. + +### Firefox OS Quirks + + * Pas de support pour`filter`. + +### Bizarreries de paciarelli + + * Pas de support pour`filter`. + +### Notes au sujet de Windows Phone 7 et 8 + + * Pas de support pour`filter`. + +## navigator.compass.clearWatch + +Arrêter de regarder la boussole référencée par le paramètre ID de montre. + + navigator.compass.clearWatch(watchID) ; + + + * **watchID**: l'ID retourné par`navigator.compass.watchHeading`. + +### Exemple + + var watchID = navigator.compass.watchHeading (onSuccess, onError, options) ; + + ... plus tard... navigator.compass.clearWatch(watchID) ; + + +## CompassHeading + +A `CompassHeading` objet est retourné à la `compassSuccess` fonction de rappel. + +### Propriétés + + * **magneticHeading**: la position en degrés de 0-359,99 à un instant donné. *(Nombre)* + + * **trueHeading**: la position par rapport au pôle Nord géographique en degrés 0-359,99 à un instant donné. Une valeur négative indique que le cap vrai ne peut être déterminée. *(Nombre)* + + * **headingAccuracy**: la déviation en degrés entre la direction signalée et la véritable direction. *(Nombre)* + + * **horodatage**: l'heure à laquelle cette direction a été déterminée. *(millisecondes)* + +### Amazon Fire OS Quirks + + * `trueHeading`n'est pas pris en charge, mais la même valeur que les rapports`magneticHeading` + + * `headingAccuracy`est toujours 0 car il n'y a pas de différence entre la `magneticHeading` et`trueHeading` + +### Quirks Android + + * La `trueHeading` propriété n'est pas pris en charge, mais la même valeur que des rapports`magneticHeading`. + + * La `headingAccuracy` propriété est toujours 0 car il n'y a pas de différence entre la `magneticHeading` et`trueHeading`. + +### Firefox OS Quirks + + * La `trueHeading` propriété n'est pas pris en charge, mais la même valeur que des rapports`magneticHeading`. + + * La `headingAccuracy` propriété est toujours 0 car il n'y a pas de différence entre la `magneticHeading` et`trueHeading`. + +### Notes au sujet d'iOS + + * La `trueHeading` propriété est retournée uniquement pour les services de localisation activées via`navigator.geolocation.watchLocation()`. + +## CompassError + +A `CompassError` objet est retourné à la `compassError` fonction de rappel lorsqu'une erreur survient. + +### Propriétés + + * **code**: l'un des codes d'erreur prédéfinis énumérés ci-dessous. + +### Constantes + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/fr/index.md b/plugins/cordova-plugin-device-orientation/doc/fr/index.md new file mode 100644 index 0000000..f04a6a9 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/fr/index.md @@ -0,0 +1,189 @@ + + +# cordova-plugin-device-orientation + +Ce plugin permet d'accéder à la boussole de l'appareil. La boussole est un capteur qui détecte la direction ou la position que l'appareil est pointé, généralement par le haut de l'appareil. Il mesure la position en degrés de 0 à 359.99, où 0 est vers le Nord. + +Accès se fait par un global `navigator.compass` objet. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.compass);} + + +## Installation + + Cordova plugin ajouter cordova-plugin-device-orientation + + +## Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Navigateur +* Firefox OS +* iOS +* Paciarelli +* Windows Phone 7 et 8 (s'il est disponible dans le matériel) +* Windows 8 + +## Méthodes + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Téléchargez la cours de la boussole. La boussole est renvoyé via un `CompassHeading` s'opposer à l'aide de la `compassSuccess` fonction de rappel. + + navigator.compass.getCurrentHeading (compassSuccess, compassError) ; + + +### Exemple + + function onSuccess(heading) {alert (' intitulé: "+ heading.magneticHeading);} ; + + function onError(error) {alert ('CompassError: ' + error.code);} ; + + navigator.compass.getCurrentHeading (onSuccess, onError) ; + + +## navigator.compass.watchHeading + +Obtient la position actuelle de l'appareil à intervalle régulier. Chaque fois que le titre est récupéré, la `headingSuccess` fonction de rappel est exécutée. + +Le code retourné montre fait référence à l'intervalle montre boussole. La montre ID peut être utilisé avec `navigator.compass.clearWatch` d'arrêter de regarder le navigator.compass. + + var watchID = navigator.compass.watchHeading (compassSuccess, compassError, [compassOptions]) ; + + +`compassOptions`peut contenir les clés suivantes : + +* **fréquence** : la fréquence de récupération de la boussole en millisecondes. *(Nombre)* (Par défaut : 100) +* **filtre**: le changement en degrés nécessaires pour lancer un rappel de succès watchHeading. Lorsque cette valeur est définie, la **fréquence** est ignoré. *(Nombre)* + +### Exemple + + function onSuccess(heading) {var element = document.getElementById('heading') ; + element.innerHTML = "intitulé:" + heading.magneticHeading ; + }; + + function onError(compassError) {alert (' erreur de boussole: "+ compassError.code);} ; + + options de var = { + frequency: 3000 + } ; Mise à jour chaque 3 secondes var watchID = navigator.compass.watchHeading (onSuccess, onError, options) ; + + +### Bizarreries navigateur + +Valeurs pour la rubrique actuelle sont générés au hasard afin de simuler la boussole. + +### iOS Quirks + +Seul `watchHeading` peut être en effet à un moment donné dans l'iOS. Si un `watchHeading` utilise un filtre, appeler `getCurrentHeading` ou `watchHeading` utilise la valeur existante de filtre pour spécifier des changements de position. En regardant les changements de position avec un filtre est plus efficace qu'avec des intervalles de temps. + +### Amazon Fire OS Quirks + +* `filter`n'est pas pris en charge. + +### Quirks Android + +* Pas de support pour`filter`. + +### Firefox OS Quirks + +* Pas de support pour`filter`. + +### Bizarreries de paciarelli + +* Pas de support pour`filter`. + +### Windows Phone 7 et 8 Quirks + +* Pas de support pour`filter`. + +## navigator.compass.clearWatch + +Arrêter de regarder la boussole référencée par le paramètre ID de montre. + + navigator.compass.clearWatch(watchID) ; + + +* **watchID**: l'ID retourné par`navigator.compass.watchHeading`. + +### Exemple + + var watchID = navigator.compass.watchHeading (onSuccess, onError, options) ; + + ... plus tard... navigator.compass.clearWatch(watchID) ; + + +## CompassHeading + +A `CompassHeading` objet est retourné à la `compassSuccess` fonction de rappel. + +### Propriétés + +* **magneticHeading**: la position en degrés de 0-359,99 à un instant donné. *(Nombre)* + +* **trueHeading**: la position par rapport au pôle Nord géographique en degrés 0-359,99 à un instant donné. Une valeur négative indique que le cap vrai ne peut être déterminée. *(Nombre)* + +* **headingAccuracy**: la déviation en degrés entre la direction signalée et la véritable direction. *(Nombre)* + +* **horodatage**: l'heure à laquelle cette direction a été déterminée. *(millisecondes)* + +### Amazon Fire OS Quirks + +* `trueHeading`n'est pas pris en charge, mais la même valeur que les rapports`magneticHeading` + +* `headingAccuracy`est toujours 0 car il n'y a pas de différence entre la `magneticHeading` et`trueHeading` + +### Quirks Android + +* La `trueHeading` propriété n'est pas pris en charge, mais la même valeur que des rapports`magneticHeading`. + +* La `headingAccuracy` propriété est toujours 0 car il n'y a pas de différence entre la `magneticHeading` et`trueHeading`. + +### Firefox OS Quirks + +* La `trueHeading` propriété n'est pas pris en charge, mais la même valeur que des rapports`magneticHeading`. + +* La `headingAccuracy` propriété est toujours 0 car il n'y a pas de différence entre la `magneticHeading` et`trueHeading`. + +### iOS Quirks + +* La `trueHeading` propriété est retournée uniquement pour les services de localisation activées via`navigator.geolocation.watchLocation()`. + +## CompassError + +A `CompassError` objet est retourné à la `compassError` fonction de rappel lorsqu'une erreur survient. + +### Propriétés + +* **code**: l'un des codes d'erreur prédéfinis énumérés ci-dessous. + +### Constantes + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/it/README.md b/plugins/cordova-plugin-device-orientation/doc/it/README.md new file mode 100644 index 0000000..76133c1 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/it/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +Questo plugin consente di accedere alla bussola del dispositivo. La bussola è un sensore che rileva la direzione o la voce che il dispositivo è puntato, in genere dalla parte superiore del dispositivo. Esso misura la rotta in gradi da 0 a 359.99, dove 0 è a nord. + +L'accesso avviene tramite un oggetto globale `navigator.compass`. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Installazione + + cordova plugin add cordova-plugin-device-orientation + + +## Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 e 8 (se disponibili nell'hardware) + * Windows 8 + +## Metodi + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Ottenere la corrente della bussola. La bussola viene restituita tramite un oggetto `CompassHeading` utilizzando la funzione di callback `compassSuccess`. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### Esempio + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Ottiene il titolo attuale del dispositivo a intervalli regolari. Ogni volta che viene recuperato il titolo, viene eseguita la funzione di callback `headingSuccess`. + +L'orologio restituito ID fa riferimento l'intervallo orologio bussola. L'ID di orologio utilizzabile con `navigator.compass.clearWatch` a smettere di guardare la navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` può contenere i seguenti tasti: + + * **frequency**: la frequenza di recuperare la bussola in millisecondi. *(Numero)* (Default: 100) + * **filter**: il cambiamento in gradi necessari per avviare un callback di successo watchHeading. Quando questo valore è impostato, la **frequency** viene ignorata. *(Numero)* + +### Esempio + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Stranezze browser + +I valori per la rubrica attuale sono generati casualmente al fine di simulare la bussola. + +### iOS stranezze + +Solo un `watchHeading` può essere in effetti una volta in iOS. Se un `watchHeading` utilizza un filtro, chiamata `getCurrentHeading` o `watchHeading` utilizza il valore di filtro esistenti per specificare le modifiche intestazione. Guardando i cambiamenti di direzione con un filtro è più efficiente con intervalli di tempo. + +### Amazon fuoco OS stranezze + + * `filter`non è supportato. + +### Stranezze Android + + * Nessun supporto per`filter`. + +### Firefox OS stranezze + + * Nessun supporto per`filter`. + +### Tizen stranezze + + * Nessun supporto per`filter`. + +### Windows Phone 7 e 8 stranezze + + * Nessun supporto per`filter`. + +## navigator.compass.clearWatch + +Smettere di guardare la bussola a cui fa riferimento il parametro ID orologio. + + navigator.compass.clearWatch(watchID); + + + * **watchID**: l'ID restituito da`navigator.compass.watchHeading`. + +### Esempio + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +Alla funzione di callback `compassSuccess` viene restituito un oggetto `CompassHeading`. + +### Proprietà + + * **magneticHeading**: la rotta in gradi da 0-359.99 in un unico momento. *(Numero)* + + * **trueHeading**: la voce rispetto al Polo Nord geografico in gradi 0-359.99 in un unico momento. Un valore negativo indica che non è possibile determinare la vera voce. *(Numero)* + + * **headingAccuracy**: lo scostamento in gradi tra il titolo segnalato e la vera voce. *(Numero)* + + * **timestamp**: il tempo in cui questa voce è stata determinata. *(millisecondi)* + +### Amazon fuoco OS stranezze + + * `trueHeading`non è supportato, ma riporta lo stesso valore`magneticHeading` + + * `headingAccuracy`è sempre 0 perché non non c'è alcuna differenza tra la `magneticHeading` e`trueHeading` + +### Stranezze Android + + * La `trueHeading` proprietà non è supportata, ma riporta lo stesso valore`magneticHeading`. + + * La `headingAccuracy` proprietà è sempre 0 perché non non c'è alcuna differenza tra la `magneticHeading` e`trueHeading`. + +### Firefox OS stranezze + + * La `trueHeading` proprietà non è supportata, ma riporta lo stesso valore`magneticHeading`. + + * La `headingAccuracy` proprietà è sempre 0 perché non non c'è alcuna differenza tra la `magneticHeading` e`trueHeading`. + +### iOS stranezze + + * La `trueHeading` proprietà viene restituito solo per servizi di localizzazione attivate tramite`navigator.geolocation.watchLocation()`. + +## CompassError + +Un oggetto `CompassError` viene restituito alla funzione di callback `compassError` quando si verifica un errore. + +### Proprietà + + * **codice**: uno dei codici di errore predefiniti elencati di seguito. + +### Costanti + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/it/index.md b/plugins/cordova-plugin-device-orientation/doc/it/index.md new file mode 100644 index 0000000..975b5e0 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/it/index.md @@ -0,0 +1,202 @@ + + +# cordova-plugin-device-orientation + +Questo plugin consente di accedere alla bussola del dispositivo. La bussola è un sensore che rileva la direzione o la voce che il dispositivo è puntato, in genere dalla parte superiore del dispositivo. Esso misura la rotta in gradi da 0 a 359.99, dove 0 è a nord. + +L'accesso avviene tramite un oggetto globale `navigator.compass`. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Installazione + + cordova plugin add cordova-plugin-device-orientation + + +## Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 e 8 (se disponibili nell'hardware) +* Windows 8 + +## Metodi + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Ottenere la corrente della bussola. La bussola viene restituita tramite un oggetto `CompassHeading` utilizzando la funzione di callback `compassSuccess`. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### Esempio + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Ottiene il titolo attuale del dispositivo a intervalli regolari. Ogni volta che viene recuperato il titolo, viene eseguita la funzione di callback `headingSuccess`. + +L'orologio restituito ID fa riferimento l'intervallo orologio bussola. L'ID di orologio utilizzabile con `navigator.compass.clearWatch` a smettere di guardare la navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` può contenere i seguenti tasti: + +* **frequency**: la frequenza di recuperare la bussola in millisecondi. *(Numero)* (Default: 100) +* **filter**: il cambiamento in gradi necessari per avviare un callback di successo watchHeading. Quando questo valore è impostato, la **frequency** viene ignorata. *(Numero)* + +### Esempio + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Stranezze browser + +I valori per la rubrica attuale sono generati casualmente al fine di simulare la bussola. + +### iOS stranezze + +Solo un `watchHeading` può essere in effetti una volta in iOS. Se un `watchHeading` utilizza un filtro, chiamata `getCurrentHeading` o `watchHeading` utilizza il valore di filtro esistenti per specificare le modifiche intestazione. Guardando i cambiamenti di direzione con un filtro è più efficiente con intervalli di tempo. + +### Amazon fuoco OS stranezze + +* `filter`non è supportato. + +### Stranezze Android + +* Nessun supporto per`filter`. + +### Firefox OS stranezze + +* Nessun supporto per`filter`. + +### Tizen stranezze + +* Nessun supporto per`filter`. + +### Windows Phone 7 e 8 stranezze + +* Nessun supporto per`filter`. + +## navigator.compass.clearWatch + +Smettere di guardare la bussola a cui fa riferimento il parametro ID orologio. + + navigator.compass.clearWatch(watchID); + + +* **watchID**: l'ID restituito da`navigator.compass.watchHeading`. + +### Esempio + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +Alla funzione di callback `compassSuccess` viene restituito un oggetto `CompassHeading`. + +### Proprietà + +* **magneticHeading**: la rotta in gradi da 0-359.99 in un unico momento. *(Numero)* + +* **trueHeading**: la voce rispetto al Polo Nord geografico in gradi 0-359.99 in un unico momento. Un valore negativo indica che non è possibile determinare la vera voce. *(Numero)* + +* **headingAccuracy**: lo scostamento in gradi tra il titolo segnalato e la vera voce. *(Numero)* + +* **timestamp**: il tempo in cui questa voce è stata determinata. *(millisecondi)* + +### Amazon fuoco OS stranezze + +* `trueHeading`non è supportato, ma riporta lo stesso valore`magneticHeading` + +* `headingAccuracy`è sempre 0 perché non non c'è alcuna differenza tra la `magneticHeading` e`trueHeading` + +### Stranezze Android + +* La `trueHeading` proprietà non è supportata, ma riporta lo stesso valore`magneticHeading`. + +* La `headingAccuracy` proprietà è sempre 0 perché non non c'è alcuna differenza tra la `magneticHeading` e`trueHeading`. + +### Firefox OS stranezze + +* La `trueHeading` proprietà non è supportata, ma riporta lo stesso valore`magneticHeading`. + +* La `headingAccuracy` proprietà è sempre 0 perché non non c'è alcuna differenza tra la `magneticHeading` e`trueHeading`. + +### iOS stranezze + +* La `trueHeading` proprietà viene restituito solo per servizi di localizzazione attivate tramite`navigator.geolocation.watchLocation()`. + +## CompassError + +Un oggetto `CompassError` viene restituito alla funzione di callback `compassError` quando si verifica un errore. + +### Proprietà + +* **codice**: uno dei codici di errore predefiniti elencati di seguito. + +### Costanti + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/ja/README.md b/plugins/cordova-plugin-device-orientation/doc/ja/README.md new file mode 100644 index 0000000..f129ddc --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/ja/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®ã‚³ãƒ³ãƒ‘スã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã—ã¾ã™ã€‚ ã‚³ãƒ³ãƒ‘ã‚¹ã¯æ–¹å‘ã¾ãŸã¯ã¨ã„ã†ãƒ‡ãƒã‚¤ã‚¹ã¯ã€é€šå¸¸ã‹ã‚‰æŒ‡æ‘˜è£…ç½®ã®ä¸Šéƒ¨ã®è¦‹å‡ºã—を検出ã™ã‚‹ã‚»ãƒ³ã‚µãƒ¼ã§ã™ã€‚ 359.99ã€0 ã¯åŒ—ã« 0 ã‹ã‚‰ã®è§’度ã§è¦‹å‡ºã—を測定ã—ã¾ã™ã€‚ + +アクセスã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.compass` オブジェクトを介ã—ã¦ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## インストール + + cordova plugin add cordova-plugin-device-orientation + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * ブラウザー + * Firefox ã® OS + * iOS + * Tizen + * Windows Phone 7 ãŠã‚ˆã³ 8 (å¯èƒ½ãªå ´åˆãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã§) + * Windows 8 + +## メソッド + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +ç¾åœ¨ã®ã‚³ãƒ³ãƒ‘ス方ä½ã‚’å–å¾—ã—ã¾ã™ã€‚コンパスé‡è·¯ãŒ `compassSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã‚’使用ã—㦠`CompassHeading` オブジェクトを介ã—ã¦è¿”ã•れã¾ã™ã€‚ + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### 例 + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +デãƒã‚¤ã‚¹ã®å®šæœŸçš„ãªé–“éš”ã§ç¾åœ¨ã®æ–¹ä½ã‚’å–å¾—ã—ã¾ã™ã€‚見出ã—ã‚’å–り出ã™ãŸã³ã« `headingSuccess` コールãƒãƒƒã‚¯é–¢æ•°ãŒå®Ÿè¡Œã•れã¾ã™ã€‚ + +è¿”ã•れる時計 ID コンパス時計腕時計間隔をå‚ç…§ã—ã¾ã™ã€‚時計 ID ã¯ã€navigator.compass を見ã¦åœæ­¢ã™ã‚‹ `navigator.compass.clearWatch` を使用ã§ãã¾ã™ã€‚ + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` ã¯ã€æ¬¡ã®ã‚­ãƒ¼ã‚’å«ã‚ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + + * **周波数**: 多ãã®å ´åˆã€ã‚³ãƒ³ãƒ‘スé‡è·¯ (ミリ秒å˜ä½) ã‚’å–å¾—ã™ã‚‹æ–¹æ³•。*(æ•°)*(デフォルト: 100) + * **フィルター**: watchHeading æˆåŠŸæ™‚ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã‚’é–‹å§‹ã™ã‚‹å¿…è¦åº¦ã®å¤‰åŒ–。ã“ã®å€¤ã‚’設定ã™ã‚‹ã¨ã€**頻度**ã¯ç„¡è¦–ã•れã¾ã™ã€‚*(æ•°)* + +### 例 + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### ブラウザーã®ç™– + +ç¾åœ¨ã®è¦‹å‡ºã—ã®å€¤ã¯ã€ã‚³ãƒ³ãƒ‘スをシミュレートã™ã‚‹ãŸã‚ã«ãƒ©ãƒ³ãƒ€ãƒ ã«ç”Ÿæˆã•れã¾ã™ã€‚ + +### iOS ã®ç™– + +1 ã¤ã ã‘ `watchHeading` 㯠iOS ã®åŠ¹æžœã‚’åŒæ™‚ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ `watchHeading` ã¯ãƒ•ィルターを使用ã—ã¦ã€`getCurrentHeading` ã¾ãŸã¯ `watchHeading` を呼ã³å‡ºã™è¦‹å‡ºã—ã®å¤‰æ›´ã‚’指定ã™ã‚‹æ—¢å­˜ã®ãƒ•ィルター値を使用ã—ã¾ã™ã€‚ フィルターを使用ã—ã¦è¦‹å‡ºã—ã®å¤‰æ›´ã‚’è¦‹ã¦æ™‚間間隔よりも効率的ã«ãƒ•ァイルã§ã™ã€‚ + +### ã‚¢ãƒžã‚¾ãƒ³ç« OS ç™– + + * `filter`サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Android ã®ç™– + + * サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +### Firefox OS ç™– + + * サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +### Tizen ã®ç™– + + * サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +### Windows Phone 7 㨠8 ç™– + + * サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +## navigator.compass.clearWatch + +時計 ID パラメーターã«ã‚ˆã£ã¦å‚ç…§ã•れるコンパスを見ã¦åœæ­¢ã—ã¾ã™ã€‚ + + navigator.compass.clearWatch(watchID); + + + * **watchID**: ã«ã‚ˆã£ã¦è¿”ã•れる ID`navigator.compass.watchHeading`. + +### 例 + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +`CompassHeading` オブジェクトã¯ã€`compassSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«è¿”ã•れã¾ã™ã€‚ + +### プロパティ + + * **magneticHeading**: 1 ã¤ã®æ™‚点㧠0 359.99 ã‹ã‚‰è§’度ã§ã®è¦‹å‡ºã—。*(æ•°)* + + * **trueHeading**: 度 0 359.99 ã§åœ°ç†çš„ãªåŒ—極を基準ã«ã—ã¦ã€1 ã¤ã®æ™‚点ã§ã®è¦‹å‡ºã—。 è² ã®å€¤ã¯çœŸé‡è·¯ã‚’特定ã§ããªã„ã“ã¨ã‚’示ã—ã¾ã™ã€‚ *(æ•°)* + + * **headingAccuracy**: 報告ã•れãŸè¦‹å‡ºã—ã¨çœŸæ–¹ä½è§’度å差。*(æ•°)* + + * **タイムスタンプ**: ã“ã®è¦‹å‡ºã—を決定ã—ãŸæ™‚。*(ミリ秒)* + +### ã‚¢ãƒžã‚¾ãƒ³ç« OS ç™– + + * `trueHeading`レãƒãƒ¼ãƒˆã¨åŒã˜å€¤ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`magneticHeading` + + * `headingAccuracy`常㫠0 ã®é–“ã®é•ã„ã¯ã‚りã¾ã›ã‚“ã®ã§ã€ `magneticHeading` ã¨`trueHeading` + +### Android ã®ç™– + + * `trueHeading`プロパティã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã¨åŒã˜å€¤ã‚’報告`magneticHeading`. + + * `headingAccuracy`プロパティã¯å¸¸ã« 0 ã®é–“ã®é•ã„ã¯ã‚りã¾ã›ã‚“ã®ã§ã€ `magneticHeading` ã¨`trueHeading`. + +### Firefox OS ç™– + + * `trueHeading`プロパティã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã¨åŒã˜å€¤ã‚’報告`magneticHeading`. + + * `headingAccuracy`プロパティã¯å¸¸ã« 0 ã®é–“ã®é•ã„ã¯ã‚りã¾ã›ã‚“ã®ã§ã€ `magneticHeading` ã¨`trueHeading`. + +### iOS ã®ç™– + + * `trueHeading`çµŒç”±ã§æœ‰åйã«ãªã£ã¦ã„ã‚‹ä½ç½®æƒ…報サービスã®ãƒ—ロパティãŒè¿”ã•れã¾ã™ã®ã¿`navigator.geolocation.watchLocation()`. + +## CompassError + +`CompassError` オブジェクトã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã« `compassError` コールãƒãƒƒã‚¯é–¢æ•°ã«è¿”ã•れã¾ã™ã€‚ + +### プロパティ + + * **コード**: 次ã®ã„ãšã‚Œã‹ã®å®šç¾©æ¸ˆã¿ã®ã‚¨ãƒ©ãƒ¼ コード。 + +### 定数 + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/ja/index.md b/plugins/cordova-plugin-device-orientation/doc/ja/index.md new file mode 100644 index 0000000..82774cc --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/ja/index.md @@ -0,0 +1,202 @@ + + +# cordova-plugin-device-orientation + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®ã‚³ãƒ³ãƒ‘スã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã—ã¾ã™ã€‚ ã‚³ãƒ³ãƒ‘ã‚¹ã¯æ–¹å‘ã¾ãŸã¯ã¨ã„ã†ãƒ‡ãƒã‚¤ã‚¹ã¯ã€é€šå¸¸ã‹ã‚‰æŒ‡æ‘˜è£…ç½®ã®ä¸Šéƒ¨ã®è¦‹å‡ºã—を検出ã™ã‚‹ã‚»ãƒ³ã‚µãƒ¼ã§ã™ã€‚ 359.99ã€0 ã¯åŒ—ã« 0 ã‹ã‚‰ã®è§’度ã§è¦‹å‡ºã—を測定ã—ã¾ã™ã€‚ + +アクセスã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.compass` オブジェクトを介ã—ã¦ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## インストール + + cordova plugin add cordova-plugin-device-orientation + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* ブラウザー +* Firefox ã® OS +* iOS +* Tizen +* Windows Phone 7 ãŠã‚ˆã³ 8 (å¯èƒ½ãªå ´åˆãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã§) +* Windows 8 + +## メソッド + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +ç¾åœ¨ã®ã‚³ãƒ³ãƒ‘ス方ä½ã‚’å–å¾—ã—ã¾ã™ã€‚コンパスé‡è·¯ãŒ `compassSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã‚’使用ã—㦠`CompassHeading` オブジェクトを介ã—ã¦è¿”ã•れã¾ã™ã€‚ + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### 例 + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +デãƒã‚¤ã‚¹ã®å®šæœŸçš„ãªé–“éš”ã§ç¾åœ¨ã®æ–¹ä½ã‚’å–å¾—ã—ã¾ã™ã€‚見出ã—ã‚’å–り出ã™ãŸã³ã« `headingSuccess` コールãƒãƒƒã‚¯é–¢æ•°ãŒå®Ÿè¡Œã•れã¾ã™ã€‚ + +è¿”ã•れる時計 ID コンパス時計腕時計間隔をå‚ç…§ã—ã¾ã™ã€‚時計 ID ã¯ã€navigator.compass を見ã¦åœæ­¢ã™ã‚‹ `navigator.compass.clearWatch` を使用ã§ãã¾ã™ã€‚ + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` ã¯ã€æ¬¡ã®ã‚­ãƒ¼ã‚’å«ã‚ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + +* **周波数**: 多ãã®å ´åˆã€ã‚³ãƒ³ãƒ‘スé‡è·¯ (ミリ秒å˜ä½) ã‚’å–å¾—ã™ã‚‹æ–¹æ³•。*(æ•°)*(デフォルト: 100) +* **フィルター**: watchHeading æˆåŠŸæ™‚ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã‚’é–‹å§‹ã™ã‚‹å¿…è¦åº¦ã®å¤‰åŒ–。ã“ã®å€¤ã‚’設定ã™ã‚‹ã¨ã€**頻度**ã¯ç„¡è¦–ã•れã¾ã™ã€‚*(æ•°)* + +### 例 + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### ブラウザーã®ç™– + +ç¾åœ¨ã®è¦‹å‡ºã—ã®å€¤ã¯ã€ã‚³ãƒ³ãƒ‘スをシミュレートã™ã‚‹ãŸã‚ã«ãƒ©ãƒ³ãƒ€ãƒ ã«ç”Ÿæˆã•れã¾ã™ã€‚ + +### iOS ã®ç™– + +1 ã¤ã ã‘ `watchHeading` 㯠iOS ã®åŠ¹æžœã‚’åŒæ™‚ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ `watchHeading` ã¯ãƒ•ィルターを使用ã—ã¦ã€`getCurrentHeading` ã¾ãŸã¯ `watchHeading` を呼ã³å‡ºã™è¦‹å‡ºã—ã®å¤‰æ›´ã‚’指定ã™ã‚‹æ—¢å­˜ã®ãƒ•ィルター値を使用ã—ã¾ã™ã€‚ フィルターを使用ã—ã¦è¦‹å‡ºã—ã®å¤‰æ›´ã‚’è¦‹ã¦æ™‚間間隔よりも効率的ã«ãƒ•ァイルã§ã™ã€‚ + +### ã‚¢ãƒžã‚¾ãƒ³ç« OS ç™– + +* `filter`サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + +### Android ã®ç™– + +* サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +### Firefox OS ç™– + +* サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +### Tizen ã®ç™– + +* サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +### Windows Phone 7 㨠8 ç™– + +* サãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`filter`. + +## navigator.compass.clearWatch + +時計 ID パラメーターã«ã‚ˆã£ã¦å‚ç…§ã•れるコンパスを見ã¦åœæ­¢ã—ã¾ã™ã€‚ + + navigator.compass.clearWatch(watchID); + + +* **watchID**: ã«ã‚ˆã£ã¦è¿”ã•れる ID`navigator.compass.watchHeading`. + +### 例 + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +`CompassHeading` オブジェクトã¯ã€`compassSuccess` コールãƒãƒƒã‚¯é–¢æ•°ã«è¿”ã•れã¾ã™ã€‚ + +### プロパティ + +* **magneticHeading**: 1 ã¤ã®æ™‚点㧠0 359.99 ã‹ã‚‰è§’度ã§ã®è¦‹å‡ºã—。*(æ•°)* + +* **trueHeading**: 度 0 359.99 ã§åœ°ç†çš„ãªåŒ—極を基準ã«ã—ã¦ã€1 ã¤ã®æ™‚点ã§ã®è¦‹å‡ºã—。 è² ã®å€¤ã¯çœŸé‡è·¯ã‚’特定ã§ããªã„ã“ã¨ã‚’示ã—ã¾ã™ã€‚ *(æ•°)* + +* **headingAccuracy**: 報告ã•れãŸè¦‹å‡ºã—ã¨çœŸæ–¹ä½è§’度å差。*(æ•°)* + +* **タイムスタンプ**: ã“ã®è¦‹å‡ºã—を決定ã—ãŸæ™‚。*(ミリ秒)* + +### ã‚¢ãƒžã‚¾ãƒ³ç« OS ç™– + +* `trueHeading`レãƒãƒ¼ãƒˆã¨åŒã˜å€¤ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“`magneticHeading` + +* `headingAccuracy`常㫠0 ã®é–“ã®é•ã„ã¯ã‚りã¾ã›ã‚“ã®ã§ã€ `magneticHeading` ã¨`trueHeading` + +### Android ã®ç™– + +* `trueHeading`プロパティã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã¨åŒã˜å€¤ã‚’報告`magneticHeading`. + +* `headingAccuracy`プロパティã¯å¸¸ã« 0 ã®é–“ã®é•ã„ã¯ã‚りã¾ã›ã‚“ã®ã§ã€ `magneticHeading` ã¨`trueHeading`. + +### Firefox OS ç™– + +* `trueHeading`プロパティã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã¨åŒã˜å€¤ã‚’報告`magneticHeading`. + +* `headingAccuracy`プロパティã¯å¸¸ã« 0 ã®é–“ã®é•ã„ã¯ã‚りã¾ã›ã‚“ã®ã§ã€ `magneticHeading` ã¨`trueHeading`. + +### iOS ã®ç™– + +* `trueHeading`çµŒç”±ã§æœ‰åйã«ãªã£ã¦ã„ã‚‹ä½ç½®æƒ…報サービスã®ãƒ—ロパティãŒè¿”ã•れã¾ã™ã®ã¿`navigator.geolocation.watchLocation()`. + +## CompassError + +`CompassError` オブジェクトã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã« `compassError` コールãƒãƒƒã‚¯é–¢æ•°ã«è¿”ã•れã¾ã™ã€‚ + +### プロパティ + +* **コード**: 次ã®ã„ãšã‚Œã‹ã®å®šç¾©æ¸ˆã¿ã®ã‚¨ãƒ©ãƒ¼ コード。 + +### 定数 + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/ko/README.md b/plugins/cordova-plugin-device-orientation/doc/ko/README.md new file mode 100644 index 0000000..26388cb --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/ko/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +ì´ í”ŒëŸ¬ê·¸ì¸ ë””ë°”ì´ìŠ¤ì˜ ë‚˜ì¹¨ë°˜ì— ëŒ€ 한 액세스를 제공합니다. 나침반 ë°©í–¥ ë˜ëŠ” 표제는 장치 ì§€ì  ì´ë‹¤, ì¼ë°˜ì ìœ¼ë¡œ 장치 위ì—서 ê°ì§€ 하는 센서입니다. 359.99, 0ê°€ ë¶ìª½ì„ 0ì—서ë„ì—서 ë¨¸ë¦¬ê¸€ì„ ì¸¡ì • 합니다. + +글로벌 `navigator.compass` 개체를 통해 액세스가입니다. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## 설치 + + cordova plugin add cordova-plugin-device-orientation + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * 브ë¼ìš°ì € + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Tizen + * Windows Phone 7, 8 (사용 가능한 경우 하드웨어) + * 윈ë„ìš° 8 + +## 메서드 + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +현재 나침반 제목 좀. 나침반 제목 `compassSuccess` 콜백 함수를 사용 하 ì—¬ `CompassHeading` 개체를 통해 반환 ë©ë‹ˆë‹¤. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### 예를 들어 + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +ì •ê¸°ì  ìž¥ì¹˜ì˜ í˜„ìž¬ ë¨¸ë¦¬ê¸€ì„ ê°€ì ¸ì˜µë‹ˆë‹¤. 제목 검색 때마다 `headingSuccess` 콜백 함수가 실행 ë©ë‹ˆë‹¤. + +반환 ëœ ì‹œê³„ ID 나침반 시계 ê°„ê²©ì„ ì°¸ì¡°í•©ë‹ˆë‹¤. 시계 ID는 navigator.compass를 보는 중지 하 `navigator.compass.clearWatch`와 함께 사용할 수 있습니다. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions`는 ë‹¤ìŒ í‚¤ë¥¼ í¬í•¨í•  수 있습니다. + + * **frequency**: ìžì£¼ 밀리초ì—서 나침반 ë¨¸ë¦¬ê¸€ì„ ê²€ìƒ‰ 하는 방법. *(수)* (기본값: 100) + * **filter**:ë„ watchHeading 성공 ì½œë°±ì„ ì‹œìž‘ 하는 ë° í•„ìš”í•œ 변경. ì´ ê°’ì„ ì„¤ì • 하는 경우 **주파수** 는 무시 ë©ë‹ˆë‹¤. *(수)* + +### 예를 들어 + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### 브ë¼ìš°ì € 만지면 + +현재 ì œëª©ì— ëŒ€ 한 ê°’ì€ ë‚˜ì¹¨ë°˜ì„ ì‹œë®¬ë ˆì´ì…˜ 하기 위해 ìž„ì˜ë¡œ ìƒì„± ë©ë‹ˆë‹¤. + +### iOS ë‹¨ì  + +í•˜ë‚˜ì˜ `watchHeading` iOSì—서 한 ë²ˆì— ì ìš©ì—서 ë  ìˆ˜ 있습니다. `watchHeading` 필터를 사용 하는 경우 `getCurrentHeading` ë˜ëŠ” `watchHeading` 호출 사용 하 ì—¬ 기존 í•„í„° ê°’ 지정 제목 변경. 필터와 제목 변화를 ë³´ê³  ì‹œê°„ì„ ê°„ê²©ìœ¼ë¡œ 보다 ë” íš¨ìœ¨ì ìž…니다. + +### 아마존 화재 OS ë‹¨ì  + + * `filter`ì§€ì› ë˜ì§€ 않습니다. + +### 안 드 로ì´ë“œ ë‹¨ì  + + * 대 한 ì§€ì›`filter`. + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + + * 대 한 ì§€ì›`filter`. + +### Tizen 특수 + + * 대 한 ì§€ì›`filter`. + +### Windows Phone 7, 8 특수 + + * 대 한 ì§€ì›`filter`. + +## navigator.compass.clearWatch + +시계 ID 매개 변수ì—서 참조 하는 ë‚˜ì¹¨ë°˜ì„ ë³´ê³  중지 합니다. + + navigator.compass.clearWatch(watchID); + + + * **watchID**: ID 반환`navigator.compass.watchHeading`. + +### 예를 들어 + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +`CompassHeading` 개체는 `compassSuccess` 콜백 í•¨ìˆ˜ì— ë°˜í™˜ ë©ë‹ˆë‹¤. + +### ì†ì„± + + * **magneticHeading**: ë‹¨ì¼ ì‹œì ì—서 0-359.99ì—ì„œë„ ì œëª©. *(수)* + + * **trueHeading**: ë‹¨ì¼ ì‹œì ì—서 0-359.99ì—서ì—서 ì§€ë¦¬ì  ë¶ê·¹ì„ 기준으로 향하고. ìŒìˆ˜ ê°’ì„ ë‚˜íƒ€ëƒ…ë‹ˆë‹¤ 진정한 표제를 확ì¸í•  수 없습니다. *(수)* + + * **headingAccuracy**: ë³´ê³  ëœ ë¨¸ë¦¬ê¸€ 사ì´ì˜ 진정한 ì œëª©ë„ íŽ¸ì°¨. *(수)* + + * **타임 스탬프**:ì´ ì œëª© ê²°ì • 하는 시간. *(밀리초)* + +### 아마존 화재 OS ë‹¨ì  + + * `trueHeading`ì§€ì› ë˜ì§€ 않습니다 하지만 ê°™ì€ ê°’ìœ¼ë¡œ ë³´ê³ `magneticHeading` + + * `headingAccuracy`í•­ìƒ 0 ì‚¬ì´ ì°¨ì´ê°€ 있기 ë•Œë¬¸ì— `magneticHeading` 와`trueHeading` + +### 안 드 로ì´ë“œ ë‹¨ì  + + * `trueHeading`ì†ì„±ì€ ì§€ì› ë˜ì§€ 않습니다 하지만 ê°™ì€ ê°’ìœ¼ë¡œ ë³´ê³ `magneticHeading`. + + * `headingAccuracy`ì†ì„±ì€ í•­ìƒ 0 ì‚¬ì´ ì°¨ì´ê°€ 있기 ë•Œë¬¸ì— `magneticHeading` 와`trueHeading`. + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + + * `trueHeading`ì†ì„±ì€ ì§€ì› ë˜ì§€ 않습니다 하지만 ê°™ì€ ê°’ìœ¼ë¡œ ë³´ê³ `magneticHeading`. + + * `headingAccuracy`ì†ì„±ì€ í•­ìƒ 0 ì‚¬ì´ ì°¨ì´ê°€ 있기 ë•Œë¬¸ì— `magneticHeading` 와`trueHeading`. + +### iOS ë‹¨ì  + + * `trueHeading`ì†ì„±ì„ 통해 위치 ì„œë¹„ìŠ¤ì— ëŒ€ 한 반환만`navigator.geolocation.watchLocation()`. + +## CompassError + +`CompassError` 개체는 오류가 ë°œìƒ í•˜ë©´ `compassError` 콜백 í•¨ìˆ˜ì— ë°˜í™˜ ë©ë‹ˆë‹¤. + +### ì†ì„± + + * **코드**: 미리 ì •ì˜ ëœ ì˜¤ë¥˜ 코드 중 하나가 ì•„ëž˜ì— ë‚˜ì—´ ëœ. + +### ìƒìˆ˜ + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/ko/index.md b/plugins/cordova-plugin-device-orientation/doc/ko/index.md new file mode 100644 index 0000000..38c9980 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/ko/index.md @@ -0,0 +1,202 @@ + + +# cordova-plugin-device-orientation + +ì´ í”ŒëŸ¬ê·¸ì¸ ë””ë°”ì´ìŠ¤ì˜ ë‚˜ì¹¨ë°˜ì— ëŒ€ 한 액세스를 제공합니다. 나침반 ë°©í–¥ ë˜ëŠ” 표제는 장치 ì§€ì  ì´ë‹¤, ì¼ë°˜ì ìœ¼ë¡œ 장치 위ì—서 ê°ì§€ 하는 센서입니다. 359.99, 0ê°€ ë¶ìª½ì„ 0ì—서ë„ì—서 ë¨¸ë¦¬ê¸€ì„ ì¸¡ì • 합니다. + +글로벌 `navigator.compass` 개체를 통해 액세스가입니다. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## 설치 + + cordova plugin add cordova-plugin-device-orientation + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* 브ë¼ìš°ì € +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Tizen +* Windows Phone 7, 8 (사용 가능한 경우 하드웨어) +* 윈ë„ìš° 8 + +## 메서드 + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +현재 나침반 제목 좀. 나침반 제목 `compassSuccess` 콜백 함수를 사용 하 ì—¬ `CompassHeading` 개체를 통해 반환 ë©ë‹ˆë‹¤. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### 예를 들어 + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +ì •ê¸°ì  ìž¥ì¹˜ì˜ í˜„ìž¬ ë¨¸ë¦¬ê¸€ì„ ê°€ì ¸ì˜µë‹ˆë‹¤. 제목 검색 때마다 `headingSuccess` 콜백 함수가 실행 ë©ë‹ˆë‹¤. + +반환 ëœ ì‹œê³„ ID 나침반 시계 ê°„ê²©ì„ ì°¸ì¡°í•©ë‹ˆë‹¤. 시계 ID는 navigator.compass를 보는 중지 하 `navigator.compass.clearWatch`와 함께 사용할 수 있습니다. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions`는 ë‹¤ìŒ í‚¤ë¥¼ í¬í•¨í•  수 있습니다. + +* **frequency**: ìžì£¼ 밀리초ì—서 나침반 ë¨¸ë¦¬ê¸€ì„ ê²€ìƒ‰ 하는 방법. *(수)* (기본값: 100) +* **filter**:ë„ watchHeading 성공 ì½œë°±ì„ ì‹œìž‘ 하는 ë° í•„ìš”í•œ 변경. ì´ ê°’ì„ ì„¤ì • 하는 경우 **주파수** 는 무시 ë©ë‹ˆë‹¤. *(수)* + +### 예를 들어 + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### 브ë¼ìš°ì € 만지면 + +현재 ì œëª©ì— ëŒ€ 한 ê°’ì€ ë‚˜ì¹¨ë°˜ì„ ì‹œë®¬ë ˆì´ì…˜ 하기 위해 ìž„ì˜ë¡œ ìƒì„± ë©ë‹ˆë‹¤. + +### iOS ë‹¨ì  + +í•˜ë‚˜ì˜ `watchHeading` iOSì—서 한 ë²ˆì— ì ìš©ì—서 ë  ìˆ˜ 있습니다. `watchHeading` 필터를 사용 하는 경우 `getCurrentHeading` ë˜ëŠ” `watchHeading` 호출 사용 하 ì—¬ 기존 í•„í„° ê°’ 지정 제목 변경. 필터와 제목 변화를 ë³´ê³  ì‹œê°„ì„ ê°„ê²©ìœ¼ë¡œ 보다 ë” íš¨ìœ¨ì ìž…니다. + +### 아마존 화재 OS ë‹¨ì  + +* `filter`ì§€ì› ë˜ì§€ 않습니다. + +### 안 드 로ì´ë“œ ë‹¨ì  + +* 대 한 ì§€ì›`filter`. + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + +* 대 한 ì§€ì›`filter`. + +### Tizen 특수 + +* 대 한 ì§€ì›`filter`. + +### Windows Phone 7, 8 특수 + +* 대 한 ì§€ì›`filter`. + +## navigator.compass.clearWatch + +시계 ID 매개 변수ì—서 참조 하는 ë‚˜ì¹¨ë°˜ì„ ë³´ê³  중지 합니다. + + navigator.compass.clearWatch(watchID); + + +* **watchID**: ID 반환`navigator.compass.watchHeading`. + +### 예를 들어 + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +`CompassHeading` 개체는 `compassSuccess` 콜백 í•¨ìˆ˜ì— ë°˜í™˜ ë©ë‹ˆë‹¤. + +### ì†ì„± + +* **magneticHeading**: ë‹¨ì¼ ì‹œì ì—서 0-359.99ì—ì„œë„ ì œëª©. *(수)* + +* **trueHeading**: ë‹¨ì¼ ì‹œì ì—서 0-359.99ì—서ì—서 ì§€ë¦¬ì  ë¶ê·¹ì„ 기준으로 향하고. ìŒìˆ˜ ê°’ì„ ë‚˜íƒ€ëƒ…ë‹ˆë‹¤ 진정한 표제를 확ì¸í•  수 없습니다. *(수)* + +* **headingAccuracy**: ë³´ê³  ëœ ë¨¸ë¦¬ê¸€ 사ì´ì˜ 진정한 ì œëª©ë„ íŽ¸ì°¨. *(수)* + +* **타임 스탬프**:ì´ ì œëª© ê²°ì • 하는 시간. *(밀리초)* + +### 아마존 화재 OS ë‹¨ì  + +* `trueHeading`ì§€ì› ë˜ì§€ 않습니다 하지만 ê°™ì€ ê°’ìœ¼ë¡œ ë³´ê³ `magneticHeading` + +* `headingAccuracy`í•­ìƒ 0 ì‚¬ì´ ì°¨ì´ê°€ 있기 ë•Œë¬¸ì— `magneticHeading` 와`trueHeading` + +### 안 드 로ì´ë“œ ë‹¨ì  + +* `trueHeading`ì†ì„±ì€ ì§€ì› ë˜ì§€ 않습니다 하지만 ê°™ì€ ê°’ìœ¼ë¡œ ë³´ê³ `magneticHeading`. + +* `headingAccuracy`ì†ì„±ì€ í•­ìƒ 0 ì‚¬ì´ ì°¨ì´ê°€ 있기 ë•Œë¬¸ì— `magneticHeading` 와`trueHeading`. + +### 파ì´ì–´ í­ìФ OS ë‹¨ì  + +* `trueHeading`ì†ì„±ì€ ì§€ì› ë˜ì§€ 않습니다 하지만 ê°™ì€ ê°’ìœ¼ë¡œ ë³´ê³ `magneticHeading`. + +* `headingAccuracy`ì†ì„±ì€ í•­ìƒ 0 ì‚¬ì´ ì°¨ì´ê°€ 있기 ë•Œë¬¸ì— `magneticHeading` 와`trueHeading`. + +### iOS ë‹¨ì  + +* `trueHeading`ì†ì„±ì„ 통해 위치 ì„œë¹„ìŠ¤ì— ëŒ€ 한 반환만`navigator.geolocation.watchLocation()`. + +## CompassError + +`CompassError` 개체는 오류가 ë°œìƒ í•˜ë©´ `compassError` 콜백 í•¨ìˆ˜ì— ë°˜í™˜ ë©ë‹ˆë‹¤. + +### ì†ì„± + +* **코드**: 미리 ì •ì˜ ëœ ì˜¤ë¥˜ 코드 중 하나가 ì•„ëž˜ì— ë‚˜ì—´ ëœ. + +### ìƒìˆ˜ + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/pl/README.md b/plugins/cordova-plugin-device-orientation/doc/pl/README.md new file mode 100644 index 0000000..875fae7 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/pl/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +Ten plugin umożliwia dostÄ™p do urzÄ…dzenia kompas. Kompas jest czujnik, który wykrywa kierunek lub pozycji, że urzÄ…dzenie jest wskazywany, zazwyczaj z górnej części urzÄ…dzenia. Mierzy on nagłówek w stopniach od 0 do 359.99, gdzie 0 jest północ. + +DostÄ™p odbywa siÄ™ za pomocÄ… obiektu globalnego `navigator.compass`. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Instalacja + + cordova plugin add cordova-plugin-device-orientation + + +## ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * PrzeglÄ…darka + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 i 8 (jeÅ›li jest dostÄ™pny w sprzÄ™cie) + * Windows 8 + +## Metody + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Uzyskać bieżącej pozycji kompas. Kompas pozycji jest zwracana za poÅ›rednictwem obiektu `CompassHeading` za pomocÄ… funkcji wywoÅ‚ania zwrotnego `compassSuccess`. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### PrzykÅ‚ad + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Pobiera bieżący nagłówek urzÄ…dzenia w regularnych odstÄ™pach czasu. Każdym razem, gdy nagłówek jest źródÅ‚o, funkcja wywoÅ‚ania zwrotnego `headingSuccess` jest wykonywany. + +Identyfikator zwrócony zegarek odwoÅ‚uje interwaÅ‚ kompas zegarek. OglÄ…daj identyfikator może być używany z `navigator.compass.clearWatch`, aby przestać oglÄ…dać navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` może zawierać nastÄ™pujÄ…ce klucze: + + * **czÄ™stotliwość**: jak czÄ™sto pobrać kompas pozycji w milisekundach. *(Liczba)* (DomyÅ›lnie: 100) + * **Filtr**: zmiana stopni wymagane zainicjować wywoÅ‚ania zwrotnego watchHeading sukces. Gdy ta wartość jest ustawiona, **czÄ™stotliwość** jest ignorowana. *(Liczba)* + +### PrzykÅ‚ad + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Quirks przeglÄ…darki + +WartoÅ›ci dla bieżącej pozycji sÄ… losowo generowane w celu symulacji kompas. + +### Dziwactwa iOS + +Tylko jeden `watchHeading` może być efekt w tym samym czasie w iOS. JeÅ›li `watchHeading` używa filtru, `getCurrentHeading` lub `watchHeading` używa istniejÄ…cych wartoÅ›ci filtru okreÅ›lić zmiany pozycji. ObserwujÄ…c zmiany pozycji z filtrem jest bardziej efektywne niż z odstÄ™pach czasu. + +### Amazon ogieÅ„ OS dziwactwa + + * `filter`nie jest obsÅ‚ugiwane. + +### Dziwactwa Androida + + * Brak wsparcia dla`filter`. + +### Firefox OS dziwactwa + + * Brak wsparcia dla`filter`. + +### Dziwactwa Tizen + + * Brak wsparcia dla`filter`. + +### Windows Phone 7 i 8 dziwactwa + + * Brak wsparcia dla`filter`. + +## navigator.compass.clearWatch + +Przestać oglÄ…dać okreÅ›lany przez parametr ID Zegarek kompas. + + navigator.compass.clearWatch(watchID); + + + * **watchID**: Identyfikator zwrócony przez`navigator.compass.watchHeading`. + +### PrzykÅ‚ad + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +Obiekt `CompassHeading` jest zwracany do funkcji wywoÅ‚ania zwrotnego `compassSuccess`. + +### WÅ‚aÅ›ciwoÅ›ci + + * **magneticHeading**: pozycja w stopniach od 0-359.99 w jednym momencie. *(Liczba)* + + * **trueHeading**: nagłówek do geograficznego Bieguna Północnego w stopniu 0-359.99 w jednym momencie. Wartość ujemna wskazuje, że prawda pozycji nie może być ustalona. *(Liczba)* + + * **headingAccuracy**: odchylenie w stopniach miÄ™dzy zgÅ‚oszonych pozycji i pozycji prawda. *(Liczba)* + + * **sygnatura czasowa**: czas, w którym pozycja ta zostaÅ‚a ustalona. *(w milisekundach)* + +### Amazon ogieÅ„ OS dziwactwa + + * `trueHeading`nie jest obsÅ‚ugiwane, ale raporty takÄ… samÄ… wartość jak`magneticHeading` + + * `headingAccuracy`jest zawsze 0, ponieważ nie ma żadnej różnicy miÄ™dzy `magneticHeading` i`trueHeading` + +### Dziwactwa Androida + + * `trueHeading`WÅ‚aÅ›ciwość nie jest obsÅ‚ugiwany, ale raporty takÄ… samÄ… wartość jak`magneticHeading`. + + * `headingAccuracy`WÅ‚aÅ›ciwość jest zawsze 0, ponieważ nie ma żadnej różnicy miÄ™dzy `magneticHeading` i`trueHeading`. + +### Firefox OS dziwactwa + + * `trueHeading`WÅ‚aÅ›ciwość nie jest obsÅ‚ugiwany, ale raporty takÄ… samÄ… wartość jak`magneticHeading`. + + * `headingAccuracy`WÅ‚aÅ›ciwość jest zawsze 0, ponieważ nie ma żadnej różnicy miÄ™dzy `magneticHeading` i`trueHeading`. + +### Dziwactwa iOS + + * `trueHeading`WÅ‚aÅ›ciwość jest zwracana tylko dla lokalizacji usÅ‚ug włączone za pomocÄ…`navigator.geolocation.watchLocation()`. + +## CompassError + +Gdy wystÄ…pi błąd, funkcja wywoÅ‚ania zwrotnego `compassError` zwracany jest obiekt `CompassError`. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Kod**: jeden z kodów błędów wstÄ™pnie zdefiniowanych poniżej. + +### StaÅ‚e + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/pl/index.md b/plugins/cordova-plugin-device-orientation/doc/pl/index.md new file mode 100644 index 0000000..ae37966 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/pl/index.md @@ -0,0 +1,202 @@ + + +# cordova-plugin-device-orientation + +Ten plugin umożliwia dostÄ™p do urzÄ…dzenia kompas. Kompas jest czujnik, który wykrywa kierunek lub pozycji, że urzÄ…dzenie jest wskazywany, zazwyczaj z górnej części urzÄ…dzenia. Mierzy on nagłówek w stopniach od 0 do 359.99, gdzie 0 jest północ. + +DostÄ™p odbywa siÄ™ za pomocÄ… obiektu globalnego `navigator.compass`. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## Instalacja + + cordova plugin add cordova-plugin-device-orientation + + +## ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* PrzeglÄ…darka +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 i 8 (jeÅ›li jest dostÄ™pny w sprzÄ™cie) +* Windows 8 + +## Metody + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Uzyskać bieżącej pozycji kompas. Kompas pozycji jest zwracana za poÅ›rednictwem obiektu `CompassHeading` za pomocÄ… funkcji wywoÅ‚ania zwrotnego `compassSuccess`. + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### PrzykÅ‚ad + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Pobiera bieżący nagłówek urzÄ…dzenia w regularnych odstÄ™pach czasu. Każdym razem, gdy nagłówek jest źródÅ‚o, funkcja wywoÅ‚ania zwrotnego `headingSuccess` jest wykonywany. + +Identyfikator zwrócony zegarek odwoÅ‚uje interwaÅ‚ kompas zegarek. OglÄ…daj identyfikator może być używany z `navigator.compass.clearWatch`, aby przestać oglÄ…dać navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` może zawierać nastÄ™pujÄ…ce klucze: + +* **czÄ™stotliwość**: jak czÄ™sto pobrać kompas pozycji w milisekundach. *(Liczba)* (DomyÅ›lnie: 100) +* **Filtr**: zmiana stopni wymagane zainicjować wywoÅ‚ania zwrotnego watchHeading sukces. Gdy ta wartość jest ustawiona, **czÄ™stotliwość** jest ignorowana. *(Liczba)* + +### PrzykÅ‚ad + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Quirks przeglÄ…darki + +WartoÅ›ci dla bieżącej pozycji sÄ… losowo generowane w celu symulacji kompas. + +### Dziwactwa iOS + +Tylko jeden `watchHeading` może być efekt w tym samym czasie w iOS. JeÅ›li `watchHeading` używa filtru, `getCurrentHeading` lub `watchHeading` używa istniejÄ…cych wartoÅ›ci filtru okreÅ›lić zmiany pozycji. ObserwujÄ…c zmiany pozycji z filtrem jest bardziej efektywne niż z odstÄ™pach czasu. + +### Amazon ogieÅ„ OS dziwactwa + +* `filter`nie jest obsÅ‚ugiwane. + +### Dziwactwa Androida + +* Brak wsparcia dla`filter`. + +### Firefox OS dziwactwa + +* Brak wsparcia dla`filter`. + +### Dziwactwa Tizen + +* Brak wsparcia dla`filter`. + +### Windows Phone 7 i 8 dziwactwa + +* Brak wsparcia dla`filter`. + +## navigator.compass.clearWatch + +Przestać oglÄ…dać okreÅ›lany przez parametr ID Zegarek kompas. + + navigator.compass.clearWatch(watchID); + + +* **watchID**: Identyfikator zwrócony przez`navigator.compass.watchHeading`. + +### PrzykÅ‚ad + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +Obiekt `CompassHeading` jest zwracany do funkcji wywoÅ‚ania zwrotnego `compassSuccess`. + +### WÅ‚aÅ›ciwoÅ›ci + +* **magneticHeading**: pozycja w stopniach od 0-359.99 w jednym momencie. *(Liczba)* + +* **trueHeading**: nagłówek do geograficznego Bieguna Północnego w stopniu 0-359.99 w jednym momencie. Wartość ujemna wskazuje, że prawda pozycji nie może być ustalona. *(Liczba)* + +* **headingAccuracy**: odchylenie w stopniach miÄ™dzy zgÅ‚oszonych pozycji i pozycji prawda. *(Liczba)* + +* **sygnatura czasowa**: czas, w którym pozycja ta zostaÅ‚a ustalona. *(w milisekundach)* + +### Amazon ogieÅ„ OS dziwactwa + +* `trueHeading`nie jest obsÅ‚ugiwane, ale raporty takÄ… samÄ… wartość jak`magneticHeading` + +* `headingAccuracy`jest zawsze 0, ponieważ nie ma żadnej różnicy miÄ™dzy `magneticHeading` i`trueHeading` + +### Dziwactwa Androida + +* `trueHeading`WÅ‚aÅ›ciwość nie jest obsÅ‚ugiwany, ale raporty takÄ… samÄ… wartość jak`magneticHeading`. + +* `headingAccuracy`WÅ‚aÅ›ciwość jest zawsze 0, ponieważ nie ma żadnej różnicy miÄ™dzy `magneticHeading` i`trueHeading`. + +### Firefox OS dziwactwa + +* `trueHeading`WÅ‚aÅ›ciwość nie jest obsÅ‚ugiwany, ale raporty takÄ… samÄ… wartość jak`magneticHeading`. + +* `headingAccuracy`WÅ‚aÅ›ciwość jest zawsze 0, ponieważ nie ma żadnej różnicy miÄ™dzy `magneticHeading` i`trueHeading`. + +### Dziwactwa iOS + +* `trueHeading`WÅ‚aÅ›ciwość jest zwracana tylko dla lokalizacji usÅ‚ug włączone za pomocÄ…`navigator.geolocation.watchLocation()`. + +## CompassError + +Gdy wystÄ…pi błąd, funkcja wywoÅ‚ania zwrotnego `compassError` zwracany jest obiekt `CompassError`. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Kod**: jeden z kodów błędów wstÄ™pnie zdefiniowanych poniżej. + +### StaÅ‚e + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/ru/index.md b/plugins/cordova-plugin-device-orientation/doc/ru/index.md new file mode 100644 index 0000000..e66f94c --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/ru/index.md @@ -0,0 +1,192 @@ + + +# cordova-plugin-device-orientation + +Этот плагин обеÑпечивает доÑтуп к уÑтройÑтва компаÑ. КомпаÑ-Ñто датчик, который определÑет направление или заголовок, что уÑтройÑтво указывает, как правило в верхней чаÑти уÑтройÑтва. Он измерÑет направление в градуÑах от 0 до 359,99 градуÑов, где 0 — Ñевер. + +## УÑтановка + + cordova plugin add cordova-plugin-device-orientation + + +## Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Обозреватель +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 и 8 (при наличии оборудованиÑ) +* Windows 8 + +## Методы + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +Получите текущий курÑ. ÐšÑƒÑ€Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ÑÑ Ñ‡ÐµÑ€ÐµÐ· `CompassHeading` объекта Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ `compassSuccess` функции обратного вызова. + + navigator.compass.getCurrentHeading (compassSuccess, compassError); + + +### Пример + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +Получает текущий заголовок уÑтройÑтва в регулÑрном интервале. Каждый раз, когда извлекаютÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº, `headingSuccess` выполнÑетÑÑ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова. + +Идентификатор возвращаемой Ñмотреть ÑÑылки ÐºÐ¾Ð¼Ð¿Ð°Ñ Ñ‡Ð°ÑÑ‹ интервал. ЧаÑÑ‹, ID может быть иÑпользован Ñ `navigator.compass.clearWatch` чтобы оÑтановить проÑмотр navigator.compass. + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions`может Ñодержать Ñледующие разделы: + +* **ЧаÑтота**: как чаÑто получить ÐºÑƒÑ€Ñ Ð² миллиÑекундах. *(ЧиÑло)* (По умолчанию: 100) +* **Фильтр**: Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² градуÑах, требуемых Ð´Ð»Ñ Ð¸Ð½Ð¸Ñ†Ð¸Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð³Ð¾ вызова watchHeading уÑпех. ЕÑли Ñто значение задано, **ЧаÑтота** учитываетÑÑ. *(ЧиÑло)* + +### Пример + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### Браузер причуды + +Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы имитировать ÐºÐ¾Ð¼Ð¿Ð°Ñ Ð³ÐµÐ½ÐµÑ€Ð¸Ñ€ÑƒÑŽÑ‚ÑÑ Ñлучайным образом Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ заголовка. + +### ОÑобенноÑти iOS + +Только один `watchHeading` может быть в одно Ð²Ñ€ÐµÐ¼Ñ Ñффекта в iOS. ЕÑли `watchHeading` иÑпользует фильтр, вызов `getCurrentHeading` или `watchHeading` Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° иÑпользуетÑÑ ÑущеÑтвующее значение фильтра. ÐÐ°Ð±Ð»ÑŽÐ´Ð°Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фильтра ÑвлÑетÑÑ Ð±Ð¾Ð»ÐµÐµ Ñффективным, чем Ñ Ð¸Ð½Ñ‚ÐµÑ€Ð²Ð°Ð»Ð¾Ð² времени. + +### ОÑобенноÑти Amazon Fire OS + +* `filter`не поддерживаетÑÑ. + +### ОÑобенноÑти Android + +* Поддержка отÑутÑтвует`filter`. + +### ОÑобенноÑти Firefox OS + +* Поддержка отÑутÑтвует`filter`. + +### ОÑобенноÑти Tizen + +* Поддержка отÑутÑтвует`filter`. + +### ОÑобенноÑти Windows Phone 7 и 8 + +* Поддержка отÑутÑтвует`filter`. + +## navigator.compass.clearWatch + +ПереÑтать Ñмотреть компаÑ, на который ÑÑылаетÑÑ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€ ID Ñмотреть. + + navigator.compass.clearWatch(watchID); + + +* **watchID**: идентификатор, возвращенный`navigator.compass.watchHeading`. + +### Пример + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +A `CompassHeading` объект возвращаетÑÑ Ðº `compassSuccess` функции обратного вызова. + +### Параметры + +* **magneticHeading**: направление в градуÑах от 0-359,99 в один момент времени. *(ЧиÑло)* + +* **trueHeading**: заголовок отноÑительно географичеÑкого Северного полюÑа в градуÑах 0-359,99 в один момент времени. Отрицательное значение указывает, что заголовок правда не может быть определено. *(ЧиÑло)* + +* **headingAccuracy**: отклонение в градуÑах между Ñообщил заголовок и заголовок верно. *(ЧиÑло)* + +* **отметка времени**: времÑ, на котором был определен Ñтот заголовок. *(в миллиÑекундах)* + +### ОÑобенноÑти Amazon Fire OS + +* `trueHeading`не поддерживаетÑÑ, но Ñообщает то же значение`magneticHeading` + +* `headingAccuracy`Это вÑегда 0 потому, что нет никакой разницы между `magneticHeading` и`trueHeading` + +### ОÑобенноÑти Android + +* `trueHeading`СвойÑтво не поддерживаетÑÑ, но Ñообщает то же значение`magneticHeading`. + +* `headingAccuracy`СвойÑтво вÑегда имеет 0 потому, что нет никакой разницы между `magneticHeading` и`trueHeading`. + +### ОÑобенноÑти Firefox OS + +* `trueHeading`СвойÑтво не поддерживаетÑÑ, но Ñообщает то же значение`magneticHeading`. + +* `headingAccuracy`СвойÑтво вÑегда имеет 0 потому, что нет никакой разницы между `magneticHeading` и`trueHeading`. + +### ОÑобенноÑти iOS + +* `trueHeading`СвойÑтва возвращаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ñлужб Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¼ÐµÑÑ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° через`navigator.geolocation.watchLocation()`. + +## CompassError + +A `CompassError` объект возвращаетÑÑ Ðº `compassError` функцию обратного вызова при возникновении ошибки. + +### Параметры + +* **code**: один из Ñтандартных кодов ошибок, перечиÑленных ниже. + +### КонÑтанты + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/doc/zh/README.md b/plugins/cordova-plugin-device-orientation/doc/zh/README.md new file mode 100644 index 0000000..fd7a020 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/zh/README.md @@ -0,0 +1,204 @@ + + +# cordova-plugin-device-orientation + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device-orientation.svg)](https://travis-ci.org/apache/cordova-plugin-device-orientation) + +é€™å€‹å¤–æŽ›ç¨‹å¼æä¾›äº†å°è¨­å‚™çš„æŒ‡å—é‡çš„訪å•。 ç¾…ç›¤æ˜¯æ„Ÿæ‡‰å™¨ï¼Œå¯æª¢æ¸¬çš„æ–¹å‘或設備通常指從設備的頂部的標題。 它的措施中從 0 度到 359.99,其中 0 是北部的標題。 + +è¨ªå•æ˜¯é€šéŽä¸€å€‹å…¨çƒ `navigator.compass` 物件。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-device-orientation + + +## 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç€è¦½å™¨ + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Tizen + * Windows Phone 7 和第 8 (如果在硬體中å¯ç”¨ï¼‰ + * Windows 8 + +## 方法 + + * navigator.compass.getCurrentHeading + * navigator.compass.watchHeading + * navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +ç²å–ç•¶å‰çš„羅經航å‘。羅經航å‘被經由一個 `CompassHeading` 物件,使用 `compassSuccess` 回呼函數。 + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### 示例 + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +ç²å–è¨­å‚™çš„ç•¶å‰æ¨™é¡Œçš„é–“éš”æ™‚é–“å®šæœŸã€‚æª¢ç´¢åˆ°çš„æ¨™é¡Œï¼Œæ¯æ¬¡åŸ·è¡Œ `headingSuccess` 回呼函數。 + +返回的表 ID 引用的指å—釿‰‹éŒ¶çš„æ™‚間間隔。表 ID å¯ç”¨æ–¼èˆ‡ `navigator.compass.clearWatch` åœæ­¢çœ‹ navigator.compass。 + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` å¯èƒ½åŒ…å«ä»¥ä¸‹é …: + + * **frequency**: 經常如何檢索以毫秒為單ä½çš„羅經航å‘。*(Number)*(é è¨­å€¼ï¼š 100) + * **filter**: 啟動 watchHeading æˆåŠŸå›žæª”æ‰€éœ€çš„åº¦çš„è®ŠåŒ–ã€‚ç•¶è¨­ç½®æ­¤å€¼æ™‚ï¼Œ**frequency**將被忽略。*(Number)* + +### 示例 + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### ç€è¦½å™¨çš„æ€ªç™– + +隨機生æˆç•¶å‰æ¨™é¡Œçš„值,以便類比羅盤。 + +### iOS 的怪癖 + +åªæœ‰ä¸€å€‹ `watchHeading` å¯ä»¥åœ¨ iOS 中一次的效果。 如果 `watchHeading` 使用一個篩é¸å™¨ï¼Œè‡´é›» `getCurrentHeading` 或 `watchHeading` ä½¿ç”¨ç¾æœ‰çš„篩é¸å™¨å€¼ä¾†æŒ‡å®šæ¨™é¡Œçš„變化。 帶有篩é¸å™¨çœ‹æ¨™é¡Œçš„變化是與時間間隔比效率更高。 + +### 亞馬éœç« OS 怪癖 + + * `filter`ä¸å—支æ´ã€‚ + +### Android 的怪癖 + + * 䏿”¯æ´`filter`. + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + + * 䏿”¯æ´`filter`. + +### Tizen 怪癖 + + * 䏿”¯æ´`filter`. + +### Windows Phone 7 å’Œ 8 怪癖 + + * 䏿”¯æ´`filter`. + +## navigator.compass.clearWatch + +別看手錶 ID åƒæ•¸æ‰€å¼•用的指å—é‡ã€‚ + + navigator.compass.clearWatch(watchID); + + + * **watchID**: 由返回的 ID`navigator.compass.watchHeading`. + +### 示例 + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +`CompassSuccess` 回呼函數返回一個 `CompassHeading` 物件。 + +### 屬性 + + * **magneticHeading**: 在æŸä¸€æ™‚刻在時間中從 0-359.99 度的標題。*(人數)* + + * **trueHeading**: 在æŸä¸€æ™‚刻的時間與地ç†åŒ—極在 0-359.99 度標題。 負值表示ä¸èƒ½ç¢ºå®šçœŸæ­£çš„æ¨™é¡Œã€‚ *(人數)* + + * **headingAccuracy**: 中度報告的標題和真正標題之間的å差。*(人數)* + + * **timestamp**: 本項決定在其中的時間。*(毫秒)* + +### 亞馬éœç« OS 怪癖 + + * `trueHeading`ä¸å—支æ´ï¼Œä½†å ±å‘Šç›¸åŒçš„值`magneticHeading` + + * `headingAccuracy`是始終為 0 因為有沒有å€åˆ¥ `magneticHeading` å’Œ`trueHeading` + +### Android 的怪癖 + + * `trueHeading`屬性ä¸å—支æ´ï¼Œä½†å ±å‘Šç›¸åŒçš„值`magneticHeading`. + + * `headingAccuracy`屬性始終是 0 因為有沒有å€åˆ¥ `magneticHeading` å’Œ`trueHeading`. + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + + * `trueHeading`屬性ä¸å—支æ´ï¼Œä½†å ±å‘Šç›¸åŒçš„值`magneticHeading`. + + * `headingAccuracy`屬性始終是 0 因為有沒有å€åˆ¥ `magneticHeading` å’Œ`trueHeading`. + +### iOS 的怪癖 + + * `trueHeading`屬性åªè¿”回ä½ç½®æœå‹™é€šéŽä»¥ä¸‹æ–¹å¼å•Ÿç”¨`navigator.geolocation.watchLocation()`. + +## CompassError + +當發生錯誤時,`compassError` 回呼函數情æ³ä¸‹æœƒè¿”回一個 `CompassError` 物件。 + +### 屬性 + + * **code**: 下é¢åˆ—出的é å®šç¾©çš„錯誤代碼之一。 + +### å¸¸é‡ + + * `CompassError.COMPASS_INTERNAL_ERR` + * `CompassError.COMPASS_NOT_SUPPORTED` \ No newline at end of file diff --git a/plugins/cordova-plugin-device-orientation/doc/zh/index.md b/plugins/cordova-plugin-device-orientation/doc/zh/index.md new file mode 100644 index 0000000..4ca2a59 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/doc/zh/index.md @@ -0,0 +1,202 @@ + + +# cordova-plugin-device-orientation + +é€™å€‹å¤–æŽ›ç¨‹å¼æä¾›äº†å°è¨­å‚™çš„æŒ‡å—é‡çš„訪å•。 ç¾…ç›¤æ˜¯æ„Ÿæ‡‰å™¨ï¼Œå¯æª¢æ¸¬çš„æ–¹å‘或設備通常指從設備的頂部的標題。 它的措施中從 0 度到 359.99,其中 0 是北部的標題。 + +è¨ªå•æ˜¯é€šéŽä¸€å€‹å…¨çƒ `navigator.compass` 物件。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.compass); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-device-orientation + + +## 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç€è¦½å™¨ +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* æ³° +* Windows Phone 7 和第 8 (如果在硬體中å¯ç”¨ï¼‰ +* Windows 8 + +## 方法 + +* navigator.compass.getCurrentHeading +* navigator.compass.watchHeading +* navigator.compass.clearWatch + +## navigator.compass.getCurrentHeading + +ç²å–ç•¶å‰çš„羅經航å‘。羅經航å‘被經由一個 `CompassHeading` 物件,使用 `compassSuccess` 回呼函數。 + + navigator.compass.getCurrentHeading(compassSuccess, compassError); + + +### 示例 + + function onSuccess(heading) { + alert('Heading: ' + heading.magneticHeading); + }; + + function onError(error) { + alert('CompassError: ' + error.code); + }; + + navigator.compass.getCurrentHeading(onSuccess, onError); + + +## navigator.compass.watchHeading + +ç²å–è¨­å‚™çš„ç•¶å‰æ¨™é¡Œçš„é–“éš”æ™‚é–“å®šæœŸã€‚æª¢ç´¢åˆ°çš„æ¨™é¡Œï¼Œæ¯æ¬¡åŸ·è¡Œ `headingSuccess` 回呼函數。 + +返回的表 ID 引用的指å—釿‰‹éŒ¶çš„æ™‚間間隔。表 ID å¯ç”¨æ–¼èˆ‡ `navigator.compass.clearWatch` åœæ­¢çœ‹ navigator.compass。 + + var watchID = navigator.compass.watchHeading(compassSuccess, compassError, [compassOptions]); + + +`compassOptions` å¯èƒ½åŒ…å«ä»¥ä¸‹é …: + +* **frequency**: 經常如何檢索以毫秒為單ä½çš„羅經航å‘。*(Number)*(é è¨­å€¼ï¼š 100) +* **filter**: 啟動 watchHeading æˆåŠŸå›žæª”æ‰€éœ€çš„åº¦çš„è®ŠåŒ–ã€‚ç•¶è¨­ç½®æ­¤å€¼æ™‚ï¼Œ**frequency**將被忽略。*(Number)* + +### 示例 + + function onSuccess(heading) { + var element = document.getElementById('heading'); + element.innerHTML = 'Heading: ' + heading.magneticHeading; + }; + + function onError(compassError) { + alert('Compass error: ' + compassError.code); + }; + + var options = { + frequency: 3000 + }; // Update every 3 seconds + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + +### ç€è¦½å™¨çš„æ€ªç™– + +隨機生æˆç•¶å‰æ¨™é¡Œçš„值,以便類比羅盤。 + +### iOS 的怪癖 + +åªæœ‰ä¸€å€‹ `watchHeading` å¯ä»¥åœ¨ iOS 中一次的效果。 如果 `watchHeading` 使用一個篩é¸å™¨ï¼Œè‡´é›» `getCurrentHeading` 或 `watchHeading` ä½¿ç”¨ç¾æœ‰çš„篩é¸å™¨å€¼ä¾†æŒ‡å®šæ¨™é¡Œçš„變化。 帶有篩é¸å™¨çœ‹æ¨™é¡Œçš„變化是與時間間隔比效率更高。 + +### 亞馬éœç« OS 怪癖 + +* `filter`ä¸å—支æ´ã€‚ + +### Android 的怪癖 + +* 䏿”¯æ´`filter`. + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +* 䏿”¯æ´`filter`. + +### 泰怪癖 + +* 䏿”¯æ´`filter`. + +### Windows Phone 7 å’Œ 8 的怪癖 + +* 䏿”¯æ´`filter`. + +## navigator.compass.clearWatch + +別看手錶 ID åƒæ•¸æ‰€å¼•用的指å—é‡ã€‚ + + navigator.compass.clearWatch(watchID); + + +* **watchID**: 由返回的 ID`navigator.compass.watchHeading`. + +### 示例 + + var watchID = navigator.compass.watchHeading(onSuccess, onError, options); + + // ... later on ... + + navigator.compass.clearWatch(watchID); + + +## CompassHeading + +`CompassSuccess` 回呼函數返回一個 `CompassHeading` 物件。 + +### 屬性 + +* **magneticHeading**: 在æŸä¸€æ™‚刻在時間中從 0-359.99 度的標題。*(人數)* + +* **trueHeading**: 在æŸä¸€æ™‚刻的時間與地ç†åŒ—極在 0-359.99 度標題。 負值表示ä¸èƒ½ç¢ºå®šçœŸæ­£çš„æ¨™é¡Œã€‚ *(人數)* + +* **headingAccuracy**: 中度報告的標題和真正標題之間的å差。*(人數)* + +* **timestamp**: 本項決定在其中的時間。*(毫秒)* + +### 亞馬éœç« OS 怪癖 + +* `trueHeading`ä¸å—支æ´ï¼Œä½†å ±å‘Šç›¸åŒçš„值`magneticHeading` + +* `headingAccuracy`是始終為 0 因為有沒有å€åˆ¥ `magneticHeading` å’Œ`trueHeading` + +### Android 的怪癖 + +* `trueHeading`屬性ä¸å—支æ´ï¼Œä½†å ±å‘Šç›¸åŒçš„值`magneticHeading`. + +* `headingAccuracy`屬性始終是 0 因為有沒有å€åˆ¥ `magneticHeading` å’Œ`trueHeading`. + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +* `trueHeading`屬性ä¸å—支æ´ï¼Œä½†å ±å‘Šç›¸åŒçš„值`magneticHeading`. + +* `headingAccuracy`屬性始終是 0 因為有沒有å€åˆ¥ `magneticHeading` å’Œ`trueHeading`. + +### iOS 的怪癖 + +* `trueHeading`屬性åªè¿”回ä½ç½®æœå‹™é€šéŽä»¥ä¸‹æ–¹å¼å•Ÿç”¨`navigator.geolocation.watchLocation()`. + +## CompassError + +當發生錯誤時,`compassError` 回呼函數情æ³ä¸‹æœƒè¿”回一個 `CompassError` 物件。 + +### 屬性 + +* **code**: 下é¢åˆ—出的é å®šç¾©çš„錯誤代碼之一。 + +### å¸¸é‡ + +* `CompassError.COMPASS_INTERNAL_ERR` +* `CompassError.COMPASS_NOT_SUPPORTED` diff --git a/plugins/cordova-plugin-device-orientation/package.json b/plugins/cordova-plugin-device-orientation/package.json new file mode 100644 index 0000000..cb5279f --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/package.json @@ -0,0 +1,58 @@ +{ + "name": "cordova-plugin-device-orientation", + "version": "1.0.5", + "description": "Cordova Device Orientation Plugin", + "cordova": { + "id": "cordova-plugin-device-orientation", + "platforms": [ + "firefoxos", + "android", + "amazon-fireos", + "ubuntu", + "blackberry10", + "ios", + "wp7", + "wp8", + "windows8", + "tizen", + "browser" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-device-orientation" + }, + "keywords": [ + "cordova", + "device", + "orientation", + "ecosystem:cordova", + "cordova-firefoxos", + "cordova-android", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-blackberry10", + "cordova-ios", + "cordova-wp7", + "cordova-wp8", + "cordova-windows8", + "cordova-tizen", + "cordova-browser" + ], + "scripts": { + "test": "npm run jshint", + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "engines": { + "cordovaDependencies": { + "2.0.0": { + "cordova": ">100" + } + } + }, + "devDependencies": { + "jshint": "^2.6.0" + } +} diff --git a/plugins/cordova-plugin-device-orientation/plugin.xml b/plugins/cordova-plugin-device-orientation/plugin.xml new file mode 100644 index 0000000..b6cd656 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/plugin.xml @@ -0,0 +1,173 @@ + + + + + + Device Orientation + Cordova Device Orientation Plugin + Apache 2.0 + cordova,device,orientation + https://git-wip-us.apache.org/repos/asf/cordova-plugin-device-orientation.git + https://issues.apache.org/jira/browse/CB/component/12320637 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-device-orientation/src/android/CompassListener.java b/plugins/cordova-plugin-device-orientation/src/android/CompassListener.java new file mode 100755 index 0000000..194db0d --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/android/CompassListener.java @@ -0,0 +1,296 @@ +/* + 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.deviceorientation; + +import java.util.List; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.content.Context; + +import android.os.Handler; +import android.os.Looper; + +/** + * This class listens to the compass sensor and stores the latest heading value. + */ +public class CompassListener extends CordovaPlugin implements SensorEventListener { + + public static int STOPPED = 0; + public static int STARTING = 1; + public static int RUNNING = 2; + public static int ERROR_FAILED_TO_START = 3; + + public long TIMEOUT = 30000; // Timeout in msec to shut off listener + + int status; // status of listener + float heading; // most recent heading value + long timeStamp; // time of most recent value + long lastAccessTime; // time the value was last retrieved + int accuracy; // accuracy of the sensor + + private SensorManager sensorManager;// Sensor manager + Sensor mSensor; // Compass sensor returned by sensor manager + + private CallbackContext callbackContext; + + /** + * Constructor. + */ + public CompassListener() { + this.heading = 0; + this.timeStamp = 0; + this.setStatus(CompassListener.STOPPED); + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackS=Context The callback id used when calling back into JavaScript. + * @return True if the action was valid. + * @throws JSONException + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (action.equals("start")) { + this.start(); + } + else if (action.equals("stop")) { + this.stop(); + } + else if (action.equals("getStatus")) { + int i = this.getStatus(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i)); + } + else if (action.equals("getHeading")) { + // If not running, then this is an async call, so don't worry about waiting + if (this.status != CompassListener.RUNNING) { + int r = this.start(); + if (r == CompassListener.ERROR_FAILED_TO_START) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START)); + return true; + } + // Set a timeout callback on the main thread. + Handler handler = new Handler(Looper.getMainLooper()); + handler.postDelayed(new Runnable() { + public void run() { + CompassListener.this.timeout(); + } + }, 2000); + } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, getCompassHeading())); + } + else if (action.equals("setTimeout")) { + this.setTimeout(args.getLong(0)); + } + else if (action.equals("getTimeout")) { + long l = this.getTimeout(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l)); + } else { + // Unsupported action + return false; + } + return true; + } + + /** + * Called when listener is to be shut down and object is being destroyed. + */ + public void onDestroy() { + this.stop(); + } + + /** + * Called when app has navigated and JS listeners have been destroyed. + */ + public void onReset() { + this.stop(); + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Start listening for compass sensor. + * + * @return status of listener + */ + public int start() { + + // If already starting or running, then just return + if ((this.status == CompassListener.RUNNING) || (this.status == CompassListener.STARTING)) { + return this.status; + } + + // Get compass sensor from sensor manager + @SuppressWarnings("deprecation") + List list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION); + + // If found, then register as listener + if (list != null && list.size() > 0) { + this.mSensor = list.get(0); + this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL); + this.lastAccessTime = System.currentTimeMillis(); + this.setStatus(CompassListener.STARTING); + } + + // If error, then set status to error + else { + this.setStatus(CompassListener.ERROR_FAILED_TO_START); + } + + return this.status; + } + + /** + * Stop listening to compass sensor. + */ + public void stop() { + if (this.status != CompassListener.STOPPED) { + this.sensorManager.unregisterListener(this); + } + this.setStatus(CompassListener.STOPPED); + } + + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO Auto-generated method stub + } + + /** + * Called after a delay to time out if the listener has not attached fast enough. + */ + private void timeout() { + if (this.status == CompassListener.STARTING) { + this.setStatus(CompassListener.ERROR_FAILED_TO_START); + if (this.callbackContext != null) { + this.callbackContext.error("Compass listener failed to start."); + } + } + } + + /** + * Sensor listener event. + * + * @param SensorEvent event + */ + public void onSensorChanged(SensorEvent event) { + + // We only care about the orientation as far as it refers to Magnetic North + float heading = event.values[0]; + + // Save heading + this.timeStamp = System.currentTimeMillis(); + this.heading = heading; + this.setStatus(CompassListener.RUNNING); + + // If heading hasn't been read for TIMEOUT time, then turn off compass sensor to save power + if ((this.timeStamp - this.lastAccessTime) > this.TIMEOUT) { + this.stop(); + } + } + + /** + * Get status of compass sensor. + * + * @return status + */ + public int getStatus() { + return this.status; + } + + /** + * Get the most recent compass heading. + * + * @return heading + */ + public float getHeading() { + this.lastAccessTime = System.currentTimeMillis(); + return this.heading; + } + + /** + * Set the timeout to turn off compass sensor if getHeading() hasn't been called. + * + * @param timeout Timeout in msec. + */ + public void setTimeout(long timeout) { + this.TIMEOUT = timeout; + } + + /** + * Get the timeout to turn off compass sensor if getHeading() hasn't been called. + * + * @return timeout in msec + */ + public long getTimeout() { + return this.TIMEOUT; + } + + /** + * Set the status and send it to JavaScript. + * @param status + */ + private void setStatus(int status) { + this.status = status; + } + + /** + * Create the CompassHeading JSON object to be returned to JavaScript + * + * @return a compass heading + */ + private JSONObject getCompassHeading() throws JSONException { + JSONObject obj = new JSONObject(); + + obj.put("magneticHeading", this.getHeading()); + obj.put("trueHeading", this.getHeading()); + // Since the magnetic and true heading are always the same our and accuracy + // is defined as the difference between true and magnetic always return zero + obj.put("headingAccuracy", 0); + obj.put("timestamp", this.timeStamp); + + return obj; + } + +} diff --git a/plugins/cordova-plugin-device-orientation/src/blackberry10/index.js b/plugins/cordova-plugin-device-orientation/src/blackberry10/index.js new file mode 100644 index 0000000..cd1bdf5 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/blackberry10/index.js @@ -0,0 +1,35 @@ +/* + * Copyright 2013 Research In Motion Limited. + * + * 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. + */ + +/* global PluginResult */ + +module.exports = { + getHeading: function (success, fail, args, env) { + var result = new PluginResult(args, env), + callback = function (orientation) { + var info = { + magneticHeading: orientation.alpha, + trueHeading: 360-orientation.alpha, + headingAccuracy: 360-(2*orientation.alpha), + timestamp: new Date().getTime() + }; + window.removeEventListener("deviceorientation", callback); + result.callbackOk(info, false); + }; + window.addEventListener("deviceorientation", callback); + result.noResult(true); + } +}; diff --git a/plugins/cordova-plugin-device-orientation/src/browser/CompassProxy.js b/plugins/cordova-plugin-device-orientation/src/browser/CompassProxy.js new file mode 100644 index 0000000..24e571a --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/browser/CompassProxy.js @@ -0,0 +1,43 @@ +/* + * + * 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 Compass = { + getHeading: function(success, error) { + var listener = function() { + var orient = {}; + var heading = (Math.round((Math.random() * 360) * 100) / 100); + + orient.trueHeading = heading; + orient.magneticHeading = heading; + orient.headingAccuracy = 0; + orient.timestamp = new Date().getTime(); + + success(orient); + + window.removeEventListener('deviceorientation', listener, false); + }; + + return window.addEventListener('deviceorientation', listener, false); + } +}; + +module.exports = Compass; +require('cordova/exec/proxy').add('Compass', Compass); diff --git a/plugins/cordova-plugin-device-orientation/src/firefoxos/compass.js b/plugins/cordova-plugin-device-orientation/src/firefoxos/compass.js new file mode 100644 index 0000000..95ac02c --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/firefoxos/compass.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. + * +*/ + +var Compass = { + getHeading: function(success, error) { + var listener = function(ev) { + var orient = { + trueHeading: ev.alpha, + magneticHeading: ev.alpha, + headingAccuracy: 0, + timestamp: new Date().getTime() + }; + success(orient); + // remove listener after first response + window.removeEventListener('deviceorientation', listener, false); + }; + return window.addEventListener('deviceorientation', listener, false); + } +}; + +module.exports = Compass; +require('cordova/exec/proxy').add('Compass', Compass); + diff --git a/plugins/cordova-plugin-device-orientation/src/ios/CDVCompass.h b/plugins/cordova-plugin-device-orientation/src/ios/CDVCompass.h new file mode 100644 index 0000000..529e11b --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/ios/CDVCompass.h @@ -0,0 +1,68 @@ +/* + 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 +#import +#import + +enum CDVHeadingStatus { + HEADINGSTOPPED = 0, + HEADINGSTARTING, + HEADINGRUNNING, + HEADINGERROR +}; +typedef NSUInteger CDVHeadingStatus; + +// simple object to keep track of heading information +@interface CDVHeadingData : NSObject {} + +@property (nonatomic, assign) CDVHeadingStatus headingStatus; +@property (nonatomic, strong) CLHeading* headingInfo; +@property (nonatomic, strong) NSMutableArray* headingCallbacks; +@property (nonatomic, copy) NSString* headingFilter; +@property (nonatomic, strong) NSDate* headingTimestamp; +@property (assign) NSInteger timeout; + +@end + +@interface CDVCompass : CDVPlugin { + @private BOOL __locationStarted; + @private BOOL __highAccuracyEnabled; + CDVHeadingData* headingData; +} + +@property (nonatomic, strong) CLLocationManager* locationManager; +@property (strong) CDVHeadingData* headingData; + +- (BOOL)hasHeadingSupport; + +- (void)locationManager:(CLLocationManager*)manager + didFailWithError:(NSError*)error; + +- (void)getHeading:(CDVInvokedUrlCommand*)command; +- (void)returnHeadingInfo:(NSString*)callbackId keepCallback:(BOOL)bRetain; +- (void)watchHeadingFilter:(CDVInvokedUrlCommand*)command; +- (void)stopHeading:(CDVInvokedUrlCommand*)command; +- (void)startHeadingWithFilter:(CLLocationDegrees)filter; +- (void)locationManager:(CLLocationManager*)manager + didUpdateHeading:(CLHeading*)heading; + +- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager*)manager; + +@end diff --git a/plugins/cordova-plugin-device-orientation/src/ios/CDVCompass.m b/plugins/cordova-plugin-device-orientation/src/ios/CDVCompass.m new file mode 100644 index 0000000..192264b --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/ios/CDVCompass.m @@ -0,0 +1,289 @@ +/* + 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 "CDVCompass.h" + +#pragma mark Constants + +#define kPGLocationErrorDomain @"kPGLocationErrorDomain" +#define kPGLocationDesiredAccuracyKey @"desiredAccuracy" +#define kPGLocationForcePromptKey @"forcePrompt" +#define kPGLocationDistanceFilterKey @"distanceFilter" +#define kPGLocationFrequencyKey @"frequency" + +#pragma mark - +#pragma mark CDVHeadingData + +@implementation CDVHeadingData + +@synthesize headingStatus, headingInfo, headingCallbacks, headingFilter, headingTimestamp, timeout; +- (CDVHeadingData*)init +{ + self = (CDVHeadingData*)[super init]; + if (self) { + self.headingStatus = HEADINGSTOPPED; + self.headingInfo = nil; + self.headingCallbacks = nil; + self.headingFilter = nil; + self.headingTimestamp = nil; + self.timeout = 10; + } + return self; +} + +@end + +#pragma mark - +#pragma mark CDVLocation + +@implementation CDVCompass + +@synthesize locationManager, headingData; + +- (void)pluginInitialize +{ + self.locationManager = [[CLLocationManager alloc] init]; + self.locationManager.delegate = self; // Tells the location manager to send updates to this object + __locationStarted = NO; + __highAccuracyEnabled = NO; + self.headingData = nil; +} + +- (BOOL)hasHeadingSupport +{ + BOOL headingInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(headingAvailable)]; // iOS 3.x + BOOL headingClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(headingAvailable)]; // iOS 4.x + + if (headingInstancePropertyAvailable) { // iOS 3.x + return [(id)self.locationManager headingAvailable]; + } else if (headingClassPropertyAvailable) { // iOS 4.x + return [CLLocationManager headingAvailable]; + } else { // iOS 2.x + return NO; + } +} + +// called to get the current heading +// Will call location manager to startUpdatingHeading if necessary + +- (void)getHeading:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSDictionary* options = [command argumentAtIndex:0 withDefault:nil]; + NSNumber* filter = [options valueForKey:@"filter"]; + + if (filter) { + [self watchHeadingFilter:command]; + return; + } + if ([self hasHeadingSupport] == NO) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:20]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + // heading retrieval does is not affected by disabling locationServices and authorization of app for location services + if (!self.headingData) { + self.headingData = [[CDVHeadingData alloc] init]; + } + CDVHeadingData* hData = self.headingData; + + if (!hData.headingCallbacks) { + hData.headingCallbacks = [NSMutableArray arrayWithCapacity:1]; + } + // add the callbackId into the array so we can call back when get data + [hData.headingCallbacks addObject:callbackId]; + + if ((hData.headingStatus != HEADINGRUNNING) && (hData.headingStatus != HEADINGERROR)) { + // Tell the location manager to start notifying us of heading updates + [self startHeadingWithFilter:0.2]; + } else { + [self returnHeadingInfo:callbackId keepCallback:NO]; + } + } +} + +// called to request heading updates when heading changes by a certain amount (filter) +- (void)watchHeadingFilter:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSDictionary* options = [command argumentAtIndex:0 withDefault:nil]; + NSNumber* filter = [options valueForKey:@"filter"]; + CDVHeadingData* hData = self.headingData; + + if ([self hasHeadingSupport] == NO) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:20]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + if (!hData) { + self.headingData = [[CDVHeadingData alloc] init]; + hData = self.headingData; + } + if (hData.headingStatus != HEADINGRUNNING) { + // Tell the location manager to start notifying us of heading updates + [self startHeadingWithFilter:[filter doubleValue]]; + } else { + // if already running check to see if due to existing watch filter + if (hData.headingFilter && ![hData.headingFilter isEqualToString:callbackId]) { + // new watch filter being specified + // send heading data one last time to clear old successCallback + [self returnHeadingInfo:hData.headingFilter keepCallback:NO]; + } + } + // save the new filter callback and update the headingFilter setting + hData.headingFilter = callbackId; + // check if need to stop and restart in order to change value??? + self.locationManager.headingFilter = [filter doubleValue]; + } +} + +- (void)returnHeadingInfo:(NSString*)callbackId keepCallback:(BOOL)bRetain +{ + CDVPluginResult* result = nil; + CDVHeadingData* hData = self.headingData; + + self.headingData.headingTimestamp = [NSDate date]; + + if (hData && (hData.headingStatus == HEADINGERROR)) { + // return error + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0]; + } else if (hData && (hData.headingStatus == HEADINGRUNNING) && hData.headingInfo) { + // if there is heading info, return it + CLHeading* hInfo = hData.headingInfo; + NSMutableDictionary* returnInfo = [NSMutableDictionary dictionaryWithCapacity:4]; + NSNumber* timestamp = [NSNumber numberWithDouble:([hInfo.timestamp timeIntervalSince1970] * 1000)]; + [returnInfo setObject:timestamp forKey:@"timestamp"]; + [returnInfo setObject:[NSNumber numberWithDouble:hInfo.magneticHeading] forKey:@"magneticHeading"]; + id trueHeading = __locationStarted ? (id)[NSNumber numberWithDouble : hInfo.trueHeading] : (id)[NSNull null]; + [returnInfo setObject:trueHeading forKey:@"trueHeading"]; + [returnInfo setObject:[NSNumber numberWithDouble:hInfo.headingAccuracy] forKey:@"headingAccuracy"]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:returnInfo]; + [result setKeepCallbackAsBool:bRetain]; + } + if (result) { + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } +} + +- (void)stopHeading:(CDVInvokedUrlCommand*)command +{ + // CDVHeadingData* hData = self.headingData; + if (self.headingData && (self.headingData.headingStatus != HEADINGSTOPPED)) { + if (self.headingData.headingFilter) { + // callback one last time to clear callback + [self returnHeadingInfo:self.headingData.headingFilter keepCallback:NO]; + self.headingData.headingFilter = nil; + } + [self.locationManager stopUpdatingHeading]; + NSLog(@"heading STOPPED"); + self.headingData = nil; + } +} + +// helper method to check the orientation and start updating headings +- (void)startHeadingWithFilter:(CLLocationDegrees)filter +{ + self.locationManager.headingFilter = filter; + [self.locationManager startUpdatingHeading]; + self.headingData.headingStatus = HEADINGSTARTING; +} + +- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager*)manager +{ + return YES; +} + +- (void)locationManager:(CLLocationManager*)manager + didUpdateHeading:(CLHeading*)heading +{ + CDVHeadingData* hData = self.headingData; + + // normally we would clear the delegate to stop getting these notifications, but + // we are sharing a CLLocationManager to get location data as well, so we do a nil check here + // ideally heading and location should use their own CLLocationManager instances + if (hData == nil) { + return; + } + + // save the data for next call into getHeadingData + hData.headingInfo = heading; + BOOL bTimeout = NO; + if (!hData.headingFilter && hData.headingTimestamp) { + bTimeout = fabs([hData.headingTimestamp timeIntervalSinceNow]) > hData.timeout; + } + + if (hData.headingStatus == HEADINGSTARTING) { + hData.headingStatus = HEADINGRUNNING; // so returnHeading info will work + + // this is the first update + for (NSString* callbackId in hData.headingCallbacks) { + [self returnHeadingInfo:callbackId keepCallback:NO]; + } + + [hData.headingCallbacks removeAllObjects]; + } + if (hData.headingFilter) { + [self returnHeadingInfo:hData.headingFilter keepCallback:YES]; + } else if (bTimeout) { + [self stopHeading:nil]; + } + hData.headingStatus = HEADINGRUNNING; // to clear any error + __locationStarted = YES; +} + +- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error +{ + NSLog(@"locationManager::didFailWithError %@", [error localizedFailureReason]); + + // Compass Error + if ([error code] == kCLErrorHeadingFailure) { + CDVHeadingData* hData = self.headingData; + if (hData) { + if (hData.headingStatus == HEADINGSTARTING) { + // heading error during startup - report error + for (NSString* callbackId in hData.headingCallbacks) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + + [hData.headingCallbacks removeAllObjects]; + } // else for frequency watches next call to getCurrentHeading will report error + if (hData.headingFilter) { + CDVPluginResult* resultFilter = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0]; + [self.commandDelegate sendPluginResult:resultFilter callbackId:hData.headingFilter]; + } + hData.headingStatus = HEADINGERROR; + } + } + + [self.locationManager stopUpdatingLocation]; + __locationStarted = NO; +} + +- (void)dealloc +{ + self.locationManager.delegate = nil; +} + +- (void)onReset +{ + [self.locationManager stopUpdatingHeading]; + self.headingData = nil; +} + +@end diff --git a/plugins/cordova-plugin-device-orientation/src/tizen/CompassProxy.js b/plugins/cordova-plugin-device-orientation/src/tizen/CompassProxy.js new file mode 100644 index 0000000..0e03af9 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/tizen/CompassProxy.js @@ -0,0 +1,56 @@ +/* + * + * 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 CompassHeading = require('cordova-plugin-device-orientation.CompassHeading'), + CompassError = require('cordova-plugin-device-orientation.CompassError'); + +var compassCallback = null, + compassReady = false; + + +module.exports = { + getHeading: function (successCallback, errorCallback) { + if (window.DeviceOrientationEvent !== undefined) { + compassCallback = function (orientation) { + var heading = 360 - orientation.alpha; + + if (compassReady) { + if (successCallback) + successCallback( new CompassHeading (heading, heading, 0, 0)); + window.removeEventListener("deviceorientation", compassCallback, true); + } + compassReady = true; + }; + compassReady = false; // workaround invalid first event value returned by WRT + window.addEventListener("deviceorientation", compassCallback, true); + } + else { + if (errorCallback) + errorCallback(CompassError.COMPASS_NOT_SUPPORTED); + } + }, + + stopHeading: function (successCallback, errorCallback) { + console.log("Compass stopHeading: not implemented yet."); + } +}; + +require("cordova/tizen/commandProxy").add("Compass", module.exports); diff --git a/plugins/cordova-plugin-device-orientation/src/ubuntu/compass.cpp b/plugins/cordova-plugin-device-orientation/src/ubuntu/compass.cpp new file mode 100644 index 0000000..a2c3bee --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/ubuntu/compass.cpp @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#include "compass.h" + +DeviceOrientation::DeviceOrientation(Cordova *cordova): CPlugin(cordova), _validData(false) { + _compass.connectToBackend(); + connect(&_compass, SIGNAL(readingChanged()), SLOT(updateSensor())); + connect(&_compass, SIGNAL(sensorError(int)), SLOT(sensorError(int))); +} + +void DeviceOrientation::getHeading(int scId, int ecId, QVariantMap options) { + Q_UNUSED(options); + if (_compass.isConnectedToBackend() || !_compass.start()) { + this->callback(ecId, "CompassError.COMPASS_NOT_SUPPORTED"); + return; + } + + _successCallbacks << scId; + _errorCallbacks << ecId; + + if (_validData) { + reportResult(); + return; + } +} + +void DeviceOrientation::sensorError(int error) { + Q_UNUSED(error); + + for (int ecId: _errorCallbacks) { + this->callback(ecId, "CompassError.COMPASS_INTERNAL_ERR"); + } + + _errorCallbacks.clear(); + _successCallbacks.clear(); + _validData = false; +} + +void DeviceOrientation::reportResult() { + QVariantMap obj; + + obj.insert("magneticHeading", _azymuth); + obj.insert("trueHeading", _azymuth); + obj.insert("headingAccuracy", _accuracy); + obj.insert("timestamp", _timestamp); + + for (int scId: _successCallbacks) { + this->cb(scId, obj); + } + + _errorCallbacks.clear(); + _successCallbacks.clear(); +} + +void DeviceOrientation::updateSensor(){ + QCompassReading *heading = _compass.reading(); + _azymuth = heading->azimuth(); + _accuracy = heading->calibrationLevel(); + _timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + _validData = true; + reportResult(); +} diff --git a/plugins/cordova-plugin-device-orientation/src/ubuntu/compass.h b/plugins/cordova-plugin-device-orientation/src/ubuntu/compass.h new file mode 100644 index 0000000..a1f421e --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/ubuntu/compass.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef COMPASS_H_HKFSAHKDFAS +#define COMPASS_H_HKFSAHKDFAS + +#include +#include +#include + +class DeviceOrientation: public CPlugin { + Q_OBJECT +public: + explicit DeviceOrientation(Cordova *cordova); + + virtual const QString fullName() override { + return DeviceOrientation::fullID(); + } + + virtual const QString shortName() override { + return "Compass"; + } + + static const QString fullID() { + return "Compass"; + } + +public slots: + void getHeading(int scId, int ecId, QVariantMap options); + +protected slots: + void updateSensor(); + void sensorError(int error); + +private: + void reportResult(); + QCompass _compass; + QList _successCallbacks; + QList _errorCallbacks; + + double _azymuth; + double _accuracy; + qtimestamp _timestamp; + bool _validData; +}; + +#endif diff --git a/plugins/cordova-plugin-device-orientation/src/windows/CompassProxy.js b/plugins/cordova-plugin-device-orientation/src/windows/CompassProxy.js new file mode 100644 index 0000000..4302636 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/windows/CompassProxy.js @@ -0,0 +1,53 @@ +/* + * + * 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. + * +*/ + +/*jslint sloppy:true */ +/*global Windows:true, require, module, setTimeout */ + +var CompassHeading = require('cordova-plugin-device-orientation.CompassHeading'), + CompassError = require('cordova-plugin-device-orientation.CompassError'); + + +module.exports = { + + onReadingChanged: null, + getHeading: function (win, lose) { + var deviceCompass = Windows.Devices.Sensors.Compass.getDefault(); + if (!deviceCompass) { + setTimeout(function () { + lose(CompassError.COMPASS_NOT_SUPPORTED); + }, 0); + } else { + var reading = deviceCompass.getCurrentReading(), + heading = new CompassHeading(reading.headingMagneticNorth, reading.headingTrueNorth, null, reading.timestamp.getTime()); + setTimeout(function () { + win(heading); + }, 0); + } + }, + stopHeading: function (win, lose) { + setTimeout(function () { + win(); + }, 0); + } +}; + +require("cordova/exec/proxy").add("Compass", module.exports); diff --git a/plugins/cordova-plugin-device-orientation/src/wp/Compass.cs b/plugins/cordova-plugin-device-orientation/src/wp/Compass.cs new file mode 100644 index 0000000..d1ce894 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/src/wp/Compass.cs @@ -0,0 +1,362 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using DeviceCompass = Microsoft.Devices.Sensors.Compass; +using System.Windows.Threading; +using System.Runtime.Serialization; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Threading; +using Microsoft.Devices.Sensors; + +namespace WPCordovaClassLib.Cordova.Commands +{ + + public class Compass : BaseCommand + { + #region Static members + + /// + /// Status of listener + /// + private static int currentStatus; + + /// + /// Id for get getCompass method + /// + private static string getCompassId = "getCompassId"; + + /// + /// Compass + /// + private static DeviceCompass compass = new DeviceCompass(); + + /// + /// Listeners for callbacks + /// + private static Dictionary watchers = new Dictionary(); + + #endregion + + #region Status codes + + public const int Stopped = 0; + public const int Starting = 1; + public const int Running = 2; + public const int ErrorFailedToStart = 4; + public const int Not_Supported = 20; + + /* + * // Capture error codes + CompassError.COMPASS_INTERNAL_ERR = 0; + CompassError.COMPASS_NOT_SUPPORTED = 20; + * */ + + #endregion + + #region CompassOptions class + /// + /// Represents Accelerometer options. + /// + [DataContract] + public class CompassOptions + { + /// + /// How often to retrieve the Acceleration in milliseconds + /// + [DataMember(IsRequired = false, Name = "frequency")] + public int Frequency { get; set; } + + /// + /// The change in degrees required to initiate a watchHeadingFilter success callback. + /// + [DataMember(IsRequired = false, Name = "filter")] + public int Filter { get; set; } + + /// + /// Watcher id + /// + [DataMember(IsRequired = false, Name = "id")] + public string Id { get; set; } + + } + #endregion + + + /// + /// Time the value was last changed + /// + //private DateTime lastValueChangedTime; + + /// + /// Accelerometer options + /// + private CompassOptions compassOptions; + + //bool isDataValid; + + //bool calibrating = false; + + public Compass() + { + + } + + /// + /// Formats current coordinates into JSON format + /// + /// Coordinates in JSON format + private string GetHeadingFormatted(CompassReading reading) + { + // NOTE: timestamp is generated on the JS side, to avoid issues with format conversions + string result = String.Format("\"magneticHeading\":{0},\"headingAccuracy\":{1},\"trueHeading\":{2}", + reading.MagneticHeading.ToString("0.0", CultureInfo.InvariantCulture), + reading.HeadingAccuracy.ToString("0.0", CultureInfo.InvariantCulture), + reading.TrueHeading.ToString("0.0", CultureInfo.InvariantCulture)); + return "{" + result + "}"; + } + + public void getHeading(string options) + { + if (!DeviceCompass.IsSupported) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, Not_Supported)); + } + else + { + //if (compass == null) + //{ + // // Instantiate the compass. + // compass = new DeviceCompass(); + // compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(40); + // compass.CurrentValueChanged += new EventHandler>(compass_CurrentValueChanged); + // compass.Calibrate += new EventHandler(compass_Calibrate); + //} + + + //compass.Start(); + + } + + try + { + if (currentStatus != Running) + { + lock (compass) + { + compass.CurrentValueChanged += compass_SingleHeadingValueChanged; + compass.Start(); + this.SetStatus(Starting); + } + + long timeout = 2000; + while ((currentStatus == Starting) && (timeout > 0)) + { + timeout = timeout - 100; + Thread.Sleep(100); + } + + if (currentStatus != Running) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart)); + return; + } + } + lock (compass) + { + compass.CurrentValueChanged -= compass_SingleHeadingValueChanged; + if (watchers.Count < 1) + { + compass.Stop(); + this.SetStatus(Stopped); + } + } + } + catch (UnauthorizedAccessException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, ErrorFailedToStart)); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart)); + } + } + + void compass_SingleHeadingValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs e) + { + this.SetStatus(Running); + if (compass.IsDataValid) + { + // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time. + // magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. + // A negative value indicates that the true heading could not be determined. + // headingAccuracy :: The deviation in degrees between the reported heading and the true heading. + //rawMagnetometerReading = e.SensorReading.MagnetometerReading; + + //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading)); + + PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading)); + + DispatchCommandResult(result); + } + } + + /// + /// Starts listening for compass sensor + /// + /// status of listener + private int start() + { + if ((currentStatus == Running) || (currentStatus == Starting)) + { + return currentStatus; + } + try + { + lock (compass) + { + watchers.Add(getCompassId, this); + compass.CurrentValueChanged += watchers[getCompassId].compass_CurrentValueChanged; + compass.Start(); + this.SetStatus(Starting); + } + } + catch (Exception) + { + this.SetStatus(ErrorFailedToStart); + } + return currentStatus; + } + + public void startWatch(string options) + { + if (!DeviceCompass.IsSupported) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, Not_Supported)); + } + + try + { + compassOptions = JSON.JsonHelper.Deserialize(options); + } + catch (Exception ex) + { + this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message)); + return; + } + + if (string.IsNullOrEmpty(compassOptions.Id)) + { + this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + try + { + lock (compass) + { + watchers.Add(compassOptions.Id, this); + compass.CurrentValueChanged += watchers[compassOptions.Id].compass_CurrentValueChanged; + compass.Start(); + this.SetStatus(Starting); + } + } + catch (Exception) + { + this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart)); + return; + } + } + + public void stopWatch(string options) + { + try + { + compassOptions = JSON.JsonHelper.Deserialize(options); + } + catch (Exception ex) + { + this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message)); + return; + } + + if (string.IsNullOrEmpty(compassOptions.Id)) + { + this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + if (currentStatus != Stopped) + { + lock (compass) + { + Compass watcher = watchers[compassOptions.Id]; + compass.CurrentValueChanged -= watcher.compass_CurrentValueChanged; + watchers.Remove(compassOptions.Id); + watcher.Dispose(); + } + } + this.SetStatus(Stopped); + + this.DispatchCommandResult(); + } + + void compass_Calibrate(object sender, Microsoft.Devices.Sensors.CalibrationEventArgs e) + { + //throw new NotImplementedException(); + // TODO: pass calibration error to JS + } + + void compass_CurrentValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs e) + { + this.SetStatus(Running); + if (compass.IsDataValid) + { + // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time. + // magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. + // A negative value indicates that the true heading could not be determined. + // headingAccuracy :: The deviation in degrees between the reported heading and the true heading. + //rawMagnetometerReading = e.SensorReading.MagnetometerReading; + + //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading)); + + PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading)); + result.KeepCallback = true; + + DispatchCommandResult(result); + } + } + + /// + /// Sets current status + /// + /// current status + private void SetStatus(int status) + { + currentStatus = status; + } + + } +} diff --git a/plugins/cordova-plugin-device-orientation/tests/plugin.xml b/plugins/cordova-plugin-device-orientation/tests/plugin.xml new file mode 100644 index 0000000..895039d --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/tests/plugin.xml @@ -0,0 +1,30 @@ + + + + + Cordova Device Orientation Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-device-orientation/tests/tests.js b/plugins/cordova-plugin-device-orientation/tests/tests.js new file mode 100644 index 0000000..7d56214 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/tests/tests.js @@ -0,0 +1,319 @@ +/* +* +* 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. +* +*/ + +/* jshint jasmine: true */ + +exports.defineAutoTests = function () { + var fail = function (done, message) { + message = (typeof message !== 'string') ? "Forced failure: wrong callback called" : message; + expect(true).toFailWithMessage(message); + done(); + }, + unexpectedFailure = "Forced failure: error callback should not have been called"; + + describe('Compass (navigator.compass)', function () { + beforeEach(function () { + jasmine.Expectation.addMatchers({ + toFailWithMessage: function () { + return { + compare: function (actual, customMessage) { + var pass = false; + if (customMessage === undefined) { + customMessage = "Forced failure: wrong callback called"; + } + return { + pass: pass, + message: customMessage + }; + } + }; + } + }); + }); + + var isCompassAvailable = true; + + beforeEach(function (done) { + if (!isCompassAvailable) { + // if we're already ensured that compass is not available, no need to check it again + done(); + return; + } + // Try to access compass device, and if it is not available + // set hardwarefailure flag to mark some tests pending + navigator.compass.getCurrentHeading(done, function (error) { + if (error.code == CompassError.COMPASS_NOT_SUPPORTED) { + isCompassAvailable = false; + } + done(); + }); + }); + + it("compass.spec.1 should exist", function () { + expect(navigator.compass).toBeDefined(); + }); + + it("compass.spec.2 should contain a getCurrentHeading function", function () { + expect(navigator.compass.getCurrentHeading).toBeDefined(); + expect(typeof navigator.compass.getCurrentHeading == 'function').toBe(true); + }); + + it("compass.spec.3 getCurrentHeading success callback should be called with a Heading object", function (done) { + if (!isCompassAvailable) { + pending(); + } + navigator.compass.getCurrentHeading(function (a) { + expect(a instanceof CompassHeading).toBe(true); + expect(a.magneticHeading).toBeDefined(); + expect(typeof a.magneticHeading == 'number').toBe(true); + expect(a.trueHeading).not.toBe(undefined); + expect(typeof a.trueHeading == 'number' || a.trueHeading === null).toBe(true); + expect(a.headingAccuracy).not.toBe(undefined); + expect(typeof a.headingAccuracy == 'number' || a.headingAccuracy === null).toBe(true); + expect(typeof a.timestamp == 'number').toBe(true); + done(); + }, fail.bind(null, done, unexpectedFailure)); + }); + + it("compass.spec.4 should contain a watchHeading function", function () { + expect(navigator.compass.watchHeading).toBeDefined(); + expect(typeof navigator.compass.watchHeading == 'function').toBe(true); + }); + + it("compass.spec.5 should contain a clearWatch function", function () { + expect(navigator.compass.clearWatch).toBeDefined(); + expect(typeof navigator.compass.clearWatch == 'function').toBe(true); + }); + + describe('Compass Constants (window.CompassError)', function () { + it("compass.spec.1 should exist", function () { + expect(window.CompassError).toBeDefined(); + expect(window.CompassError.COMPASS_INTERNAL_ERR).toBe(0); + expect(window.CompassError.COMPASS_NOT_SUPPORTED).toBe(20); + }); + }); + + describe('Compass Heading model (CompassHeading)', function () { + it("compass.spec.1 should exist", function () { + expect(CompassHeading).toBeDefined(); + }); + + it("compass.spec.8 should be able to create a new CompassHeading instance with no parameters", function () { + var h = new CompassHeading(); + expect(h).toBeDefined(); + expect(h.magneticHeading).toBeUndefined(); + expect(h.trueHeading).toBeUndefined(); + expect(h.headingAccuracy).toBeUndefined(); + expect(typeof h.timestamp == 'number').toBe(true); + }); + + it("compass.spec.9 should be able to create a new CompassHeading instance with parameters", function () { + var h = new CompassHeading(1, 2, 3, 4); + expect(h.magneticHeading).toBe(1); + expect(h.trueHeading).toBe(2); + expect(h.headingAccuracy).toBe(3); + expect(h.timestamp.valueOf()).toBe(4); + expect(typeof h.timestamp == 'number').toBe(true); + }); + }); + + describe("Compass watch heading", function() { + it("compass.spec.10 watchCurrentHeading called with a Heading object", function (done) { + if (!isCompassAvailable) { + pending(); + } + + var calledOnce = false; + + var watchId = navigator.compass.watchHeading( + function (a){ + expect(a instanceof CompassHeading).toBe(true); + expect(a.magneticHeading).toBeDefined(); + expect(typeof a.magneticHeading == 'number').toBe(true); + expect(a.trueHeading).not.toBe(undefined); + expect(typeof a.trueHeading == 'number' || a.trueHeading === null).toBe(true); + expect(a.headingAccuracy).not.toBe(undefined); + expect(typeof a.headingAccuracy == 'number' || a.headingAccuracy === null).toBe(true); + expect(typeof a.timestamp == 'number').toBe(true); + + if (calledOnce) { + navigator.compass.clearWatch(watchId); + done(); + } + + calledOnce = true; + }, + function (compassError){}, + { frequency: 50 } + ); + }); + + it("compass.spec.11 the watch success callback should not be called once the watch is cleared", function (done) { + if (!isCompassAvailable) { + pending(); + } + + var calledOnce = false; + var watchCleared = false; + + var watchId = navigator.compass.watchHeading( + function (a){ + // Don't invoke this function if we have cleared the watch + expect(watchCleared).toBe(false); + + if (calledOnce) { + navigator.compass.clearWatch(watchId); + watchCleared = true; + setInterval(function(){ + done(); + }, 100); + } + + calledOnce = true; + }, + function (compassError){}, + { frequency: 50 } + ); + }); + }); + }); +}; + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +exports.defineManualTests = function (contentEl, createActionButton) { + function roundNumber(num) { + var dec = 3; + var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + return result; + } + + var watchCompassId = null; + + /** + * Set compass status + */ + function setCompassStatus(status) { + document.getElementById('compass_status').innerHTML = status; + } + + // Success callback for both watchHeading and getCurrentHeading + function success(a) { + var magneticHeading = document.getElementById('magneticHeading'); + var trueHeading = document.getElementById("trueHeading"); + var headingAccuracy = document.getElementById("headingAccuracy"); + var timestamp = document.getElementById("timestamp"); + + magneticHeading.innerHTML = roundNumber(a.magneticHeading); + trueHeading.innerHTML = roundNumber(a.trueHeading); + headingAccuracy.innerHTML = a.headingAccuracy; + timestamp.innerHTML = a.timestamp; + } + + /** + * Stop watching the acceleration + */ + function stopCompass() { + setCompassStatus("Stopped"); + if (watchCompassId) { + navigator.compass.clearWatch(watchCompassId); + watchCompassId = null; + } + } + + /** + * Start watching compass + */ + var watchCompass = function () { + console.log("watchCompass()"); + + // Fail callback + var fail = function (e) { + console.log("watchCompass fail callback with error: " + JSON.stringify(e)); + stopCompass(); + setCompassStatus(e); + }; + + // Stop compass if running + stopCompass(); + + // Update heading every 1 sec + var opt = {}; + opt.frequency = 1000; + watchCompassId = navigator.compass.watchHeading(success, fail, opt); + + setCompassStatus("Running"); + }; + + /** + * Get current compass + */ + var getCompass = function () { + console.log("getCompass()"); + + // Stop compass if running + stopCompass(); + + // Fail callback + var fail = function (e) { + console.log("getCompass fail callback with error: " + JSON.stringify(e)); + setCompassStatus(e); + }; + + // Make call + var opt = {}; + navigator.compass.getCurrentHeading(success, fail, opt); + }; + + /******************************************************************************/ + + var orientation_tests = '

iOS devices may bring up a calibration screen when initiating these tests

' + + '
' + + 'Expected result: Will update the status box with current heading. Status will read "Stopped"' + + '

' + + 'Expected result: When pressed, will start a watch on the compass and update the heading value when heading changes. Status will read "Running"' + + '

' + + 'Expected result: Will clear the compass watch, so heading value will no longer be updated. Status will read "Stopped"'; + + contentEl.innerHTML = '
Status: ' + + 'Stopped' + + '' + + '' + + '' + + '' + + '' + + '
Magnetic heading:
True heading:
Heading accuracy:
Timestamp:
' + + orientation_tests; + + createActionButton('Get Compass', function () { + getCompass(); + }, 'getCompass'); + + createActionButton('Start Watching Compass', function () { + watchCompass(); + }, 'watchCompass'); + + createActionButton('Stop Watching Compass', function () { + stopCompass(); + }, 'stopCompass'); +}; diff --git a/plugins/cordova-plugin-device-orientation/www/CompassError.js b/plugins/cordova-plugin-device-orientation/www/CompassError.js new file mode 100644 index 0000000..7b5b485 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/www/CompassError.js @@ -0,0 +1,34 @@ +/* + * + * 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. + * +*/ + +/** + * CompassError. + * An error code assigned by an implementation when an error has occurred + * @constructor + */ +var CompassError = function(err) { + this.code = (err !== undefined ? err : null); +}; + +CompassError.COMPASS_INTERNAL_ERR = 0; +CompassError.COMPASS_NOT_SUPPORTED = 20; + +module.exports = CompassError; diff --git a/plugins/cordova-plugin-device-orientation/www/CompassHeading.js b/plugins/cordova-plugin-device-orientation/www/CompassHeading.js new file mode 100644 index 0000000..70343ee --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/www/CompassHeading.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. + * +*/ + +var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) { + this.magneticHeading = magneticHeading; + this.trueHeading = trueHeading; + this.headingAccuracy = headingAccuracy; + this.timestamp = timestamp || new Date().getTime(); +}; + +module.exports = CompassHeading; diff --git a/plugins/cordova-plugin-device-orientation/www/compass.js b/plugins/cordova-plugin-device-orientation/www/compass.js new file mode 100644 index 0000000..603e727 --- /dev/null +++ b/plugins/cordova-plugin-device-orientation/www/compass.js @@ -0,0 +1,118 @@ +/* + * + * 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'), + exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('./CompassHeading'), + CompassError = require('./CompassError'), + + timers = {}, + eventTimerId = null, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + argscheck.checkArgs('fFO', 'compass.getCurrentHeading', arguments); + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = errorCallback && function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + argscheck.checkArgs('fFO', 'compass.watchHeading', arguments); + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + if (cordova.platformId === 'browser' && !eventTimerId) { + // Start firing deviceorientation events if haven't already + var deviceorientationEvent = new Event('deviceorientation'); + eventTimerId = window.setInterval(function() { + window.dispatchEvent(deviceorientationEvent); + }, 200); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} id The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + + if (eventTimerId && Object.keys(timers).length === 0) { + // No more watchers, so stop firing 'deviceorientation' events + window.clearInterval(eventTimerId); + eventTimerId = null; + } + } + } + }; + +module.exports = compass; diff --git a/plugins/cordova-plugin-device/CONTRIBUTING.md b/plugins/cordova-plugin-device/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-device/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-device/LICENSE b/plugins/cordova-plugin-device/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-device/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/plugins/cordova-plugin-device/NOTICE b/plugins/cordova-plugin-device/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-device/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/plugins/cordova-plugin-device/README.md b/plugins/cordova-plugin-device/README.md new file mode 100644 index 0000000..10b8016 --- /dev/null +++ b/plugins/cordova-plugin-device/README.md @@ -0,0 +1,311 @@ +--- +title: Device +description: Get device information. +--- + + +|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI | +|:-:|:-:|:-:|:-:|:-:|:-:| +|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-device)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-device/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-device)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-device/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-device)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-device/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-device)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-device/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-device)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-device/)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device)| + +# cordova-plugin-device + +This plugin defines a global `device` object, which describes the device's hardware and software. +Although the object is in the global scope, it is not available until after the `deviceready` event. + +```js +document.addEventListener("deviceready", onDeviceReady, false); +function onDeviceReady() { + console.log(device.cordova); +} +``` + +Report issues with this plugin on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Device%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + + +## Installation + + cordova plugin add cordova-plugin-device + +## Properties + +- device.cordova +- device.model +- device.platform +- device.uuid +- device.version +- device.manufacturer +- device.isVirtual +- device.serial + +## device.cordova + +Get the version of Cordova running on the device. + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Browser +- Firefox OS +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows +- OSX + +## device.model + +The `device.model` returns the name of the device's model or +product. The value is set by the device manufacturer and may be +different across versions of the same product. + +### Supported Platforms + +- Android +- BlackBerry 10 +- Browser +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows +- OSX + +### Quick Example + +```js +// Android: Nexus One returns "Passion" (Nexus One code name) +// Motorola Droid returns "voles" +// BlackBerry: Torch 9800 returns "9800" +// Browser: Google Chrome returns "Chrome" +// Safari returns "Safari" +// iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. See http://theiphonewiki.com/wiki/index.php?title=Models +// OSX: returns "x86_64" +// +var model = device.model; +``` + +### Android Quirks + +- Gets the [product name](http://developer.android.com/reference/android/os/Build.html#PRODUCT) instead of the [model name](http://developer.android.com/reference/android/os/Build.html#MODEL), which is often the production code name. For example, the Nexus One returns `Passion`, and Motorola Droid returns `voles`. + +### Tizen Quirks + +- Returns the device model assigned by the vendor, for example, `TIZEN` + +### Windows Phone 7 and 8 Quirks + +- Returns the device model specified by the manufacturer. For example, the Samsung Focus returns `SGH-i917`. + +## device.platform + +Get the device's operating system name. + +```js +var string = device.platform; +``` +### Supported Platforms + +- Android +- BlackBerry 10 +- Browser +- Firefox OS +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows +- OSX + +### Quick Example + +```js +// Depending on the device, a few examples are: +// - "Android" +// - "BlackBerry 10" +// - "browser" +// - "iOS" +// - "WinCE" +// - "Tizen" +// - "Mac OS X" +var devicePlatform = device.platform; +``` + +### Windows Phone 7 Quirks + +Windows Phone 7 devices report the platform as `WinCE`. + +### Windows Phone 8 Quirks + +Windows Phone 8 devices report the platform as `Win32NT`. + +## device.uuid + +Get the device's Universally Unique Identifier ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + +```js +var string = device.uuid; +``` + +### Description + +The details of how a UUID is generated are determined by the device manufacturer and are specific to the device's platform or model. + +### Supported Platforms + +- Android +- BlackBerry 10 +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows +- OSX + +### Quick Example + +```js +// Android: Returns a random 64-bit integer (as a string, again!) +// The integer is generated on the device's first boot +// +// BlackBerry: Returns the PIN number of the device +// This is a nine-digit unique integer (as a string, though!) +// +// iPhone: (Paraphrased from the UIDevice Class documentation) +// Returns the [UIDevice identifierForVendor] UUID which is unique and the same for all apps installed by the same vendor. However the UUID can be different if the user deletes all apps from the vendor and then reinstalls it. +// Windows Phone 7 : Returns a hash of device+current user, +// if the user is not defined, a guid is generated and will persist until the app is uninstalled +// Tizen: returns the device IMEI (International Mobile Equipment Identity or IMEI is a number +// unique to every GSM and UMTS mobile phone. +var deviceID = device.uuid; +``` + +### iOS Quirk + +The `uuid` on iOS uses the identifierForVendor property. It is unique to the device across the same vendor, but will be different for different vendors and will change if all apps from the vendor are deleted and then reinstalled. +Refer [here](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDevice_Class/#//apple_ref/occ/instp/UIDevice/identifierForVendor) for details. +The UUID will be the same if app is restored from a backup or iCloud as it is saved in preferences. Users using older versions of this plugin will still receive the same previous UUID generated by another means as it will be retrieved from preferences. + +### OSX Quirk + +The `uuid` on OSX is generated automatically if it does not exist yet and is stored in the `standardUserDefaults` in the `CDVUUID` property. + +### Windows Phone 7 and 8 Quirks + +The `uuid` for Windows Phone 7 requires the permission +`ID_CAP_IDENTITY_DEVICE`. Microsoft will likely deprecate this +property soon. If the capability is not available, the application +generates a persistent guid that is maintained for the duration of the +application's installation on the device. + +## device.version + +Get the operating system version. + + var string = device.version; + +### Supported Platforms + +- Android 2.1+ +- BlackBerry 10 +- Browser +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows +- OSX + +### Quick Example + +```js +// Android: Froyo OS would return "2.2" +// Eclair OS would return "2.1", "2.0.1", or "2.0" +// Version can also return update level "2.1-update1" +// +// BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" +// +// Browser: Returns version number for the browser +// +// iPhone: iOS 3.2 returns "3.2" +// +// Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 +// Windows 8: return the current OS version, ex on Windows 8.1 returns 6.3.9600.16384 +// Tizen: returns "TIZEN_20120425_2" +// OSX: El Capitan would return "10.11.2" +// +var deviceVersion = device.version; +``` + +## device.manufacturer + +Get the device's manufacturer. + + var string = device.manufacturer; + +### Supported Platforms + +- Android +- BlackBerry 10 +- iOS +- Windows Phone 7 and 8 +- Windows + +### Quick Example + +```js +// Android: Motorola XT1032 would return "motorola" +// BlackBerry: returns "BlackBerry" +// iPhone: returns "Apple" +// +var deviceManufacturer = device.manufacturer; +``` + +## device.isVirtual + +whether the device is running on a simulator. + +```js +var isSim = device.isVirtual; +``` + +### Supported Platforms + +- Android 2.1+ +- iOS +- Windows Phone 8 +- Windows +- OSX + +### OSX Quirk + +The `isVirtual` property on OS X always returns false. + +## device.serial + +Get the device hardware serial number ([SERIAL](http://developer.android.com/reference/android/os/Build.html#SERIAL)). + +```js +var string = device.serial; +``` + +### Supported Platforms + +- Android +- OSX + diff --git a/plugins/cordova-plugin-device/RELEASENOTES.md b/plugins/cordova-plugin-device/RELEASENOTES.md new file mode 100644 index 0000000..c06a44c --- /dev/null +++ b/plugins/cordova-plugin-device/RELEASENOTES.md @@ -0,0 +1,333 @@ + +# Release Notes + +### 1.1.4 (Dec 07, 2016) +* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Updated version and RELEASENOTES.md for release 1.1.3 +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* Readme: Add fenced code blocks with langauage hints +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md +* [CB-11091](https://issues.apache.org/jira/browse/CB-11091) Incremented plugin version. +* Updated version and RELEASENOTES.md for release 1.1.2 +* Close #49 +* Use passed device, follow create policy forf CFUUIDCreate +* [CB-10631](https://issues.apache.org/jira/browse/CB-10631) ios: Fix for device.uuid in iOS 5.1.1 +* Updating the comment to exclude URL +* Minor changes to readme +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins +* chore: edit package.json license to match SPDX id +* Refactored deviceInfo on iOS for better readability. +* [CB-10368](https://issues.apache.org/jira/browse/CB-10368) Incremented plugin version. +* [CB-10368](https://issues.apache.org/jira/browse/CB-10368) Updated version and RELEASENOTES.md for release 1.1.1 +* Property manufacturer documented +* fix typo +* [CB-10238](https://issues.apache.org/jira/browse/CB-10238) Move device-plugin out from cordovalib to the plugin repository +* [CB-10238](https://issues.apache.org/jira/browse/CB-10238) Move device-plugin out from cordovalib to the plugin repository +* [CB-9923](https://issues.apache.org/jira/browse/CB-9923) Update `device.platform` documentation for browser platform +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Incremented plugin version. +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) linked issues in RELEASENOTES.md +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated version and RELEASENOTES.md for release 1.1.0 +* removed r prefix from tags +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated RELEASENOTES to be newest to oldest +* fix whitespace tab to spaces +* Add `isVirtual` for Windows 8.x (Phone) platforms +* [CB-9847](https://issues.apache.org/jira/browse/CB-9847) Fixed merge error of 408c3a20be976ca5b814c9f161c31d4edf1c00fc +* Added basic Android support for hardware serial number +* Cleanup PRs. This closes #27. This closes #17. +* [CB-9865](https://issues.apache.org/jira/browse/CB-9865) Better simulator detection for iOS +* Actually fixing the contribute link. +* Fixing contribute link. +* fix merge conflict, for reals +* add JIRA issue tracker link +* updated docs +* Cleanup wp8 code +* WP8 implementation +* update to use TARGET_OS_SIMULATOR as TARGET_IPHONE_SIMULATOR is deprecated. +* update code to use 'isVirtual' +* create test to verify existence and type of new property 'isVirtual' +* add isSimulator for iOS & Android device +* Fixed typo +* Updated documentation to mention backwards compatibility +* Updated README to reflect new behaviour and quirks on iOS +* Check user defaults first to maintain backwards compatibility +* Revert "Check user defaults first to maintain backwards compatibility" +* Check user defaults first to maintain backwards compatibility +* Remove line incorrectly committed +* Changed UUID to use [UIDevice identifierForVendor] +* remove travis-ci +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Incremented plugin version. +* [CB-9202](https://issues.apache.org/jira/browse/CB-9202) updated repo url to github mirror in package.json +* [CB-9192](https://issues.apache.org/jira/browse/CB-9192) Updated version and RELEASENOTES.md for release 1.0.1 +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-device documentation translation: cordova-plugin-device +* Attempts to corrent npm markdown issue +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version in package.json for release 1.0.0 +* Revert "CB-8858 Incremented plugin version." +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Incremented plugin version. +* [CB-8858](https://issues.apache.org/jira/browse/CB-8858) Updated version and RELEASENOTES.md for release 1.0.0 +* [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) 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 +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* remove defunct windows8 version +* add travis badge +* update plugin path +* Add cross-plugin ios paramedic test running for TravisCI +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Incremented plugin version. +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.3.0 +* Fixes a syntax error introduced in 1a13ee0 +* [CB7285][Windows] add manufacturer property for windows, and cleanup +* [CB-7285](https://issues.apache.org/jira/browse/CB-7285) Add test for manufacturer property +* [CB-7285][WP8] add manufacturer info +* update of manufacturer name +* added device.manufacturer property for Android, iOS (Apple duh), Blackberry (RIM duh) +* Support for Windows Phone 8 ANID2 ANID is only supported up to Windows Phone 7.5 +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) Use a local copy of uniqueAppInstanceIdentifier rather than CordovaLib's version +* - Fixed a bug that caused an "cannot call method of undefined" error if the browser's user agent wasn't recognized - Added support for Internet Explorer user agent strings - NOTE: IE 11's user agent string does not include "MSIE", so "Trident" is searched for instead +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Incremented plugin version. +* [CB-8110](https://issues.apache.org/jira/browse/CB-8110) Updated version and RELEASENOTES.md for release 0.2.13 +* Changing device.platform to always report the platform as "browser". +* [CB-5892](https://issues.apache.org/jira/browse/CB-5892) - Remove deprecated window.Settings +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-device documentation translation: cordova-plugin-device +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Incremented plugin version. +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Updated version and RELEASENOTES.md for release 0.2.12 +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-device documentation translation: cordova-plugin-device +* [CB-7552](https://issues.apache.org/jira/browse/CB-7552) - device.name docs have not been removed +* [fxos] Fix cordova version +* added status box and documentation to manual tests +* [fxos] Fix cordova version +* added status box and documentation to manual tests +* Added plugin support for the browser +* [CB-7262](https://issues.apache.org/jira/browse/CB-7262) Adds support for universal windows apps. +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Incremented plugin version. +* [CB-7244](https://issues.apache.org/jira/browse/CB-7244) Updated version and RELEASENOTES.md for release 0.2.11 +* update DeviceProxy.js +* [CB-7168](https://issues.apache.org/jira/browse/CB-7168) Moving Device autotests to plugin (aka new-style tests) +* CB-6127lisa7cordova-plugin-consolecordova-plugin-device documentation translation: cordova-plugin-device +* Use Windows system calls to get better info +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Incremented plugin version. +* [CB-6877](https://issues.apache.org/jira/browse/CB-6877) Updated version and RELEASENOTES.md for release 0.2.10 +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #12 +* Changing 1.5 to 2.0 +* added firefoxos version - conversion +* added firefoxos version +* [CB-6800](https://issues.apache.org/jira/browse/CB-6800) Add license +* documentation translation: cordova-plugin-device +* Lisa testing pulling in plugins for plugin: cordova-plugin-device +* Lisa testing pulling in plugins for plugin: cordova-plugin-device +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Incremented plugin version on dev branch. +* [CB-6452](https://issues.apache.org/jira/browse/CB-6452) Updated version and RELEASENOTES.md for release 0.2.9 +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460) Update license headers +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422) [windows8] use cordova/exec/proxy +* [CB-5105](https://issues.apache.org/jira/browse/CB-5105) Removed dead code for device.version +* Add NOTICE file +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Incremented plugin version on dev branch. +* Lisa testing pulling in plugins for plugin: cordova-plugin-device +* Lisa testing pulling in plugins for plugin: cordova-plugin-device +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 0.2.8 +* src/tizen: Adding DeviceProxy. +* plugin.xml: Adding platform tizen. +* supported platforms updated +* Delete stale test/ directory +* Incremented plugin version on dev branch. +* Updated version and RELEASENOTES.md for release 0.2.7 +* [CB-5737](https://issues.apache.org/jira/browse/CB-5737) Fix exception on close caused by left over telephony code from [CB-5504](https://issues.apache.org/jira/browse/CB-5504) +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Incremented plugin version on dev branch. +* [CB-5719](https://issues.apache.org/jira/browse/CB-5719) Updated version and RELEASENOTES.md for release 0.2.6 +* [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 Device plugin +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs +* [CB-5504](https://issues.apache.org/jira/browse/CB-5504) Moving Telephony Logic out of Device +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Incremented plugin version on dev branch. +* [CB-5565](https://issues.apache.org/jira/browse/CB-5565) Updated version and RELEASENOTES.md for release 0.2.5 +* [CB-5316](https://issues.apache.org/jira/browse/CB-5316) Spell Cordova as a brand unless it's a command or script +* [ubuntu] use cordova/exec/proxy +* add ubuntu platform +* Modify Device.platform logic to use amazon-fireos as the platform for Amazon Devices +* 1. Added amazon-fireos platform. 2. Change to use cordova-amazon-fireos as the platform if user agent contains 'cordova-amazon-fireos' +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) +* [CB-5188](https://issues.apache.org/jira/browse/CB-5188) Updated version and RELEASENOTES.md for release 0.2.4 +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128) added repo + issue tag in plugin.xml for device plugin +* [CB-5085](https://issues.apache.org/jira/browse/CB-5085) device.cordova returning wrong value +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Updated version and RELEASENOTES.md for release 0.2.3 +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [windows8] commandProxy has moved +* [BlackBerry10] removed uneeded permission tags in plugin.xml +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.device to org.apache.cordova.device +* Rename CHANGELOG.md -> RELEASENOTES.md +* updated to use commandProxy for ffos +* add firefoxos support +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. +* [BlackBerry10] removed uneeded permission tags in plugin.xml +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Updated version and changelog +* removed extraneous print statement +* [CB-4432](https://issues.apache.org/jira/browse/CB-4432) copyright notice change +* removed extraneous print statement +* [CB-4432](https://issues.apache.org/jira/browse/CB-4432) copyright notice change +* [CB-4595](https://issues.apache.org/jira/browse/CB-4595) updated version +* reverted name element to be user readable +* [CB-4417](https://issues.apache.org/jira/browse/CB-4417) Move cordova-plugin-device to its own Java package. +* updated namespace +* updated readme, namespace and name tag +* [plugin.xml] adding rim namespace +* [plugin.xml] adding android namespace +* [plugin.xml] standardizing license + meta +* [license] adding apache license file +* updating plugin.xml with registry data +* [Windows8] Windows8 is now supported +* added basic Device support for Windows8 + +### 1.1.3 (Sep 08, 2016) +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* Readme: Add fenced code blocks with langauage hints +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to `README.md` + +### 1.1.2 (Apr 15, 2016) +* Use passed device, follow create policy forf `CFUUIDCreate` +* [CB-10631](https://issues.apache.org/jira/browse/CB-10631) Fix for `device.uuid` in **iOS 5.1.1** +* Updating the comment to exclude URL +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins +* Refactored `deviceInfo` on **iOS** for better readability. + +### 1.1.1 (Jan 15, 2016) +* [CB-10238](https://issues.apache.org/jira/browse/CB-10238) **OSX** Move `device-plugin` out from `cordovalib` to the plugin repository +* [CB-9923](https://issues.apache.org/jira/browse/CB-9923) Update `device.platform` documentation for **Browser** platform + +### 1.1.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* Add `isVirtual` for **Windows Phone 8.x** +* Added basic **Android** support for hardware serial number +* [CB-9865](https://issues.apache.org/jira/browse/CB-9865) Better simulator detection for **iOS** +* Fixing contribute link. +* Added **WP8** implementation +* update to use `TARGET_OS_SIMULATOR` as `TARGET_IPHONE_SIMULATOR` is deprecated. +* update code to use 'isVirtual' +* create test to verify existence and type of new property 'isVirtual' +* add `isSimulator` for **iOS** & **Android** device +* Updated documentation to mention backwards compatibility +* Updated **README** to reflect new behaviour and quirks on **iOS** +* Check user defaults first to maintain backwards compatibility +* Changed `UUID` to use `[UIDevice identifierForVendor]` + +### 1.0.1 (Jun 17, 2015) +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-device documentation translation: cordova-plugin-device +* Attempts to corrent npm markdown issue + +### 1.0.0 (Apr 15, 2015) +* [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) 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 +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* remove defunct windows8 version +* add travis badge +* Add cross-plugin ios paramedic test running for TravisCI +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file + +### 0.3.0 (Feb 04, 2015) +* Added device.manufacturer property for Android, iOS, Blackberry, WP8 +* Support for Windows Phone 8 ANID2 ANID is only supported up to Windows Phone 7.5 +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) Use a local copy of uniqueAppInstanceIdentifier rather than CordovaLib's version +* browser: Fixed a bug that caused an "cannot call method of undefined" error if the browser's user agent wasn't recognized + +### 0.2.13 (Dec 02, 2014) +* Changing `device.platform` to always report the platform as "browser". +* [CB-5892](https://issues.apache.org/jira/browse/CB-5892) - Remove deprecated `window.Settings` +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-device documentation translation: cordova-plugin-device +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin + +### 0.2.12 (Sep 17, 2014) +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-device documentation translation +* [CB-7552](https://issues.apache.org/jira/browse/CB-7552) device.name docs have not been removed +* [fxos] Fix cordova version +* added status box and documentation to manual tests +* [fxos] Fix cordova version +* added status box and documentation to manual tests +* Added plugin support for the browser +* [CB-7262](https://issues.apache.org/jira/browse/CB-7262) Adds support for universal windows apps. + +### 0.2.11 (Aug 06, 2014) +* [FFOS] update DeviceProxy.js +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* Use Windows system calls to get better info + +### 0.2.10 (Jun 05, 2014) +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #12 +* Changing 1.5 to 2.0 +* added firefoxos version - conversion +* added firefoxos version +* [CB-6800](https://issues.apache.org/jira/browse/CB-6800) Add license +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md + +### 0.2.9 (Apr 17, 2014) +* [CB-5105](https://issues.apache.org/jira/browse/CB-5105): [Android, windows8, WP, BlackBerry10] Removed dead code for device.version +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* Add NOTICE file + +### 0.2.8 (Feb 05, 2014) +* Tizen support added + +### 0.2.7 (Jan 07, 2014) +* [CB-5737](https://issues.apache.org/jira/browse/CB-5737) Fix exception on close caused by left over telephony code from [CB-5504](https://issues.apache.org/jira/browse/CB-5504) + +### 0.2.6 (Jan 02, 2014) +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Device plugin +* [CB-5504](https://issues.apache.org/jira/browse/CB-5504) Moving Telephony Logic out of Device + +### 0.2.5 (Dec 4, 2013) +* [CB-5316](https://issues.apache.org/jira/browse/CB-5316) Spell Cordova as a brand unless it's a command or script +* [ubuntu] use cordova/exec/proxy +* add ubuntu platform +* Modify Device.platform logic to use amazon-fireos as the platform for Amazon Devices +* 1. Added amazon-fireos platform. 2. Change to use cordova-amazon-fireos as the platform if user agent contains 'cordova-amazon-fireos' + +### 0.2.4 (Oct 28, 2013) +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag in plugin.xml for device plugin +* [CB-5085](https://issues.apache.org/jira/browse/CB-5085) device.cordova returning wrong value +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.2.3 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [windows8] commandProxy has moved +* [BlackBerry10] removed uneeded permission tags in plugin.xml +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.device to org.apache.cordova.device +* Rename CHANGELOG.md -> RELEASENOTES.md +* updated to use commandProxy for ffos +* add firefoxos support +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. + +### 0.2.1 (Sept 5, 2013) +* removed extraneous print statement +* [CB-4432](https://issues.apache.org/jira/browse/CB-4432) copyright notice change diff --git a/plugins/cordova-plugin-device/doc/de/README.md b/plugins/cordova-plugin-device/doc/de/README.md new file mode 100644 index 0000000..81f89e9 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/de/README.md @@ -0,0 +1,203 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +Dieses Plugin definiert eine globale `device` -Objekt, das des Geräts Hard- und Software beschreibt. Das Objekt im globalen Gültigkeitsbereich ist es zwar nicht verfügbar bis nach dem `deviceready` Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Installation + + cordova plugin add cordova-plugin-device + + +## Eigenschaften + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +Rufen Sie die Version von Cordova, die auf dem Gerät ausgeführt. + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + +## device.model + +Die `device.model` gibt den Namen der Modell- oder des Geräts zurück. Der Wert wird vom Gerätehersteller festgelegt und kann zwischen den Versionen des gleichen Produkts unterschiedlich sein. + +### Unterstützte Plattformen + + * Android + * BlackBerry 10 + * Browser + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + +### Kurzes Beispiel + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Finden Sie unter http://theiphonewiki.com/wiki/index.php?title=Models / / Var-Modell = device.model; + + +### Android Eigenarten + + * Ruft den [Produktname](http://developer.android.com/reference/android/os/Build.html#PRODUCT) anstelle des [Modellnamens](http://developer.android.com/reference/android/os/Build.html#MODEL), das ist oft der Codename für die Produktion. Beispielsweise das Nexus One gibt `Passion` , und Motorola Droid gibt`voles`. + +### Tizen Macken + + * Gibt z. B. das Gerätemodell von dem Kreditor zugeordnet,`TIZEN` + +### Windows Phone 7 und 8 Eigenarten + + * Gibt das vom Hersteller angegebenen Gerätemodell zurück. Beispielsweise gibt der Samsung-Fokus`SGH-i917`. + +## device.platform + +Name des Betriebssystems des Geräts zu erhalten. + + var string = device.platform; + + +### Unterstützte Plattformen + + * Android + * BlackBerry 10 + * Browser4 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + +### Kurzes Beispiel + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 Macken + +Windows Phone 7 Geräte melden die Plattform als`WinCE`. + +### Windows Phone 8 Macken + +Windows Phone 8 Geräte melden die Plattform als`Win32NT`. + +## device.uuid + +Des Geräts Universally Unique Identifier ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier) zu erhalten). + + var string = device.uuid; + + +### Beschreibung + +Die Details wie eine UUID generiert wird werden vom Gerätehersteller und beziehen sich auf die Plattform oder das Modell des Geräts. + +### Unterstützte Plattformen + + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + +### Kurzes Beispiel + + / / Android: wird eine zufällige 64-Bit-Ganzzahl (als Zeichenfolge, wieder!) / / die ganze Zahl wird beim ersten Start des Geräts erzeugt / / / / BlackBerry: gibt die PIN-Nummer des Gerätes / / Dies ist eine neunstellige eindeutige Ganzzahl (als String, obwohl!) / / / / iPhone: (paraphrasiert aus der Dokumentation zur UIDevice-Klasse) / / liefert eine Reihe von Hash-Werte, die aus mehreren Hardware erstellt identifiziert. + / / Es ist gewährleistet, dass für jedes Gerät eindeutig sein und kann nicht gebunden werden / / an den Benutzer weitergeleitet. + / / Windows Phone 7: gibt einen Hash des Gerät + aktueller Benutzer, / / wenn der Benutzer nicht definiert ist, eine Guid generiert und wird weiter bestehen, bis die app deinstalliert wird / / Tizen: gibt das Gerät IMEI (International Mobile Equipment Identity oder IMEI ist eine Zahl / / einzigartig für jedes GSM- und UMTS-Handy. + var deviceID = device.uuid; + + +### iOS Quirk + +Die `uuid` auf iOS ist nicht eindeutig zu einem Gerät, aber für jede Anwendung, für jede Installation variiert. Es ändert sich, wenn Sie löschen und neu die app installieren, und möglicherweise auch beim iOS zu aktualisieren, oder auch ein Upgrade möglich die app pro Version (scheinbaren in iOS 5.1). Die `uuid` ist kein zuverlässiger Wert. + +### Windows Phone 7 und 8 Eigenarten + +Die `uuid` für Windows Phone 7 die Berechtigung erfordert `ID_CAP_IDENTITY_DEVICE` . Microsoft wird diese Eigenschaft wahrscheinlich bald abzuschaffen. Wenn die Funktion nicht verfügbar ist, generiert die Anwendung eine persistente Guid, die für die Dauer der Installation der Anwendung auf dem Gerät gewährleistet ist. + +## device.version + +Version des Betriebssystems zu erhalten. + + var string = device.version; + + +### Unterstützte Plattformen + + * Android 2.1 + + * BlackBerry 10 + * Browser + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + +### Kurzes Beispiel + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/de/index.md b/plugins/cordova-plugin-device/doc/de/index.md new file mode 100644 index 0000000..e3a537e --- /dev/null +++ b/plugins/cordova-plugin-device/doc/de/index.md @@ -0,0 +1,206 @@ + + +# cordova-plugin-device + +Dieses Plugin definiert eine globale `device` -Objekt, das des Geräts Hard- und Software beschreibt. Das Objekt im globalen Gültigkeitsbereich ist es zwar nicht verfügbar bis nach dem `deviceready` Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Installation + + cordova plugin add cordova-plugin-device + + +## Eigenschaften + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +Rufen Sie die Version von Cordova, die auf dem Gerät ausgeführt. + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 + +## device.model + +Die `device.model` gibt den Namen der Modell- oder des Geräts zurück. Der Wert wird vom Gerätehersteller festgelegt und kann zwischen den Versionen des gleichen Produkts unterschiedlich sein. + +### Unterstützte Plattformen + +* Android +* BlackBerry 10 +* Browser +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 + +### Kurzes Beispiel + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Finden Sie unter http://theiphonewiki.com/wiki/index.php?title=Models / / Var-Modell = device.model; + + +### Android Eigenarten + +* Ruft den [Produktname][1] anstelle des [Modellnamens][2], das ist oft der Codename für die Produktion. Beispielsweise das Nexus One gibt `Passion` , und Motorola Droid gibt`voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### Tizen Macken + +* Gibt z. B. das Gerätemodell von dem Kreditor zugeordnet,`TIZEN` + +### Windows Phone 7 und 8 Eigenarten + +* Gibt das vom Hersteller angegebenen Gerätemodell zurück. Beispielsweise gibt der Samsung-Fokus`SGH-i917`. + +## device.platform + +Name des Betriebssystems des Geräts zu erhalten. + + var string = device.platform; + + +### Unterstützte Plattformen + +* Android +* BlackBerry 10 +* Browser4 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 + +### Kurzes Beispiel + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 Macken + +Windows Phone 7 Geräte melden die Plattform als`WinCE`. + +### Windows Phone 8 Macken + +Windows Phone 8 Geräte melden die Plattform als`Win32NT`. + +## device.uuid + +Des Geräts Universally Unique Identifier ([UUID][3] zu erhalten). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### Beschreibung + +Die Details wie eine UUID generiert wird werden vom Gerätehersteller und beziehen sich auf die Plattform oder das Modell des Geräts. + +### Unterstützte Plattformen + +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 + +### Kurzes Beispiel + + / / Android: wird eine zufällige 64-Bit-Ganzzahl (als Zeichenfolge, wieder!) / / die ganze Zahl wird beim ersten Start des Geräts erzeugt / / / / BlackBerry: gibt die PIN-Nummer des Gerätes / / Dies ist eine neunstellige eindeutige Ganzzahl (als String, obwohl!) / / / / iPhone: (paraphrasiert aus der Dokumentation zur UIDevice-Klasse) / / liefert eine Reihe von Hash-Werte, die aus mehreren Hardware erstellt identifiziert. + / / Es ist gewährleistet, dass für jedes Gerät eindeutig sein und kann nicht gebunden werden / / an den Benutzer weitergeleitet. + / / Windows Phone 7: gibt einen Hash des Gerät + aktueller Benutzer, / / wenn der Benutzer nicht definiert ist, eine Guid generiert und wird weiter bestehen, bis die app deinstalliert wird / / Tizen: gibt das Gerät IMEI (International Mobile Equipment Identity oder IMEI ist eine Zahl / / einzigartig für jedes GSM- und UMTS-Handy. + var deviceID = device.uuid; + + +### iOS Quirk + +Die `uuid` auf iOS ist nicht eindeutig zu einem Gerät, aber für jede Anwendung, für jede Installation variiert. Es ändert sich, wenn Sie löschen und neu die app installieren, und möglicherweise auch beim iOS zu aktualisieren, oder auch ein Upgrade möglich die app pro Version (scheinbaren in iOS 5.1). Die `uuid` ist kein zuverlässiger Wert. + +### Windows Phone 7 und 8 Eigenarten + +Die `uuid` für Windows Phone 7 die Berechtigung erfordert `ID_CAP_IDENTITY_DEVICE` . Microsoft wird diese Eigenschaft wahrscheinlich bald abzuschaffen. Wenn die Funktion nicht verfügbar ist, generiert die Anwendung eine persistente Guid, die für die Dauer der Installation der Anwendung auf dem Gerät gewährleistet ist. + +## device.version + +Version des Betriebssystems zu erhalten. + + var string = device.version; + + +### Unterstützte Plattformen + +* Android 2.1 + +* BlackBerry 10 +* Browser +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 + +### Kurzes Beispiel + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/es/README.md b/plugins/cordova-plugin-device/doc/es/README.md new file mode 100644 index 0000000..a27abfb --- /dev/null +++ b/plugins/cordova-plugin-device/doc/es/README.md @@ -0,0 +1,216 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +Este plugin define un global `device` objeto que describe del dispositivo hardware y software. Aunque el objeto está en el ámbito global, no está disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Instalación + + cordova plugin add cordova-plugin-device + + +## Propiedades + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +Obtener la versión de Cordova que se ejecuta en el dispositivo. + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Explorador + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + +## device.model + +El `device.model` devuelve el nombre de modelo del dispositivo o producto. El valor es fijado por el fabricante del dispositivo y puede ser diferente a través de versiones del mismo producto. + +### Plataformas soportadas + + * Android + * BlackBerry 10 + * Explorador + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + +### Ejemplo rápido + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. See http://theiphonewiki.com/wiki/index.php?title=Models + // + var model = device.model; + + +### Rarezas Android + + * Obtiene el [nombre del producto](http://developer.android.com/reference/android/os/Build.html#PRODUCT) en lugar del [nombre de la modelo](http://developer.android.com/reference/android/os/Build.html#MODEL), que es a menudo el nombre de código de producción. Por ejemplo, el Nexus One devuelve `Passion` y Motorola Droid devuelve `voles`. + +### Rarezas Tizen + + * Devuelve que el modelo de dispositivo asignado por el proveedor, por ejemplo, `TIZEN` + +### Windows Phone 7 y 8 rarezas + + * Devuelve el modelo de dispositivo especificado por el fabricante. Por ejemplo, el Samsung Focus devuelve `SGH-i917`. + +## device.platform + +Obtener el nombre del sistema operativo del dispositivo. + + var string = device.platform; + + +### Plataformas soportadas + + * Android + * BlackBerry 10 + * Browser4 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + +### Ejemplo rápido + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 rarezas + +Dispositivos Windows Phone 7 informe de la plataforma como `WinCE`. + +### Windows Phone 8 rarezas + +Dispositivos Windows Phone 8 Informe la plataforma como `Win32NT`. + +## device.uuid + +Obtener identificador universalmente única del dispositivo ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + + var string = device.uuid; + + +### Descripción + +Los detalles de cómo se genera un UUID son determinados por el fabricante del dispositivo y son específicos a la plataforma del dispositivo o modelo. + +### Plataformas soportadas + + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + +### Ejemplo rápido + + // Android: Returns a random 64-bit integer (as a string, again!) + // The integer is generated on the device's first boot + // + // BlackBerry: Returns the PIN number of the device + // This is a nine-digit unique integer (as a string, though!) + // + // iPhone: (Paraphrased from the UIDevice Class documentation) + // Returns a string of hash values created from multiple hardware identifies. + // It is guaranteed to be unique for every device and can't be tied + // to the user account. + // Windows Phone 7 : Returns a hash of device+current user, + // if the user is not defined, a guid is generated and will persist until the app is uninstalled + // Tizen: returns the device IMEI (International Mobile Equipment Identity or IMEI is a number + // unique to every GSM and UMTS mobile phone. + var deviceID = device.uuid; + + +### Rarezas de iOS + +El `uuid` en iOS no es exclusiva de un dispositivo, pero varía para cada aplicación, para cada instalación. Cambia si puedes borrar y volver a instalar la aplicación, y posiblemente también cuándo actualizar iOS, o incluso mejorar la aplicación por la versión (evidente en iOS 5.1). El `uuid` no es un valor confiable. + +### Windows Phone 7 y 8 rarezas + +El `uuid` para Windows Phone 7 requiere el permiso `ID_CAP_IDENTITY_DEVICE`. Microsoft pronto probablemente desaprueban esta propiedad. Si la capacidad no está disponible, la aplicación genera un guid persistente que se mantiene durante la duración de la instalación de la aplicación en el dispositivo. + +## device.version + +Obtener la versión del sistema operativo. + + var string = device.version; + + +### Plataformas soportadas + + * Android 2.1 + + * BlackBerry 10 + * Explorador + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + +### Ejemplo rápido + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/es/index.md b/plugins/cordova-plugin-device/doc/es/index.md new file mode 100644 index 0000000..f4a5897 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/es/index.md @@ -0,0 +1,220 @@ + + +# cordova-plugin-device + +Este plugin define un global `device` objeto que describe del dispositivo hardware y software. Aunque el objeto está en el ámbito global, no está disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Instalación + + cordova plugin add cordova-plugin-device + + +## Propiedades + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +Obtener la versión de Cordova que se ejecuta en el dispositivo. + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Explorador +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 + +## device.model + +El `device.model` devuelve el nombre de modelo del dispositivo o producto. El valor es fijado por el fabricante del dispositivo y puede ser diferente a través de versiones del mismo producto. + +### Plataformas soportadas + +* Android +* BlackBerry 10 +* Explorador +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 + +### Ejemplo rápido + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. See http://theiphonewiki.com/wiki/index.php?title=Models + // + var model = device.model; + + +### Rarezas Android + +* Obtiene el [nombre del producto][1] en lugar del [nombre de la modelo][2], que es a menudo el nombre de código de producción. Por ejemplo, el Nexus One devuelve `Passion` y Motorola Droid devuelve `voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### Rarezas Tizen + +* Devuelve que el modelo de dispositivo asignado por el proveedor, por ejemplo, `TIZEN` + +### Windows Phone 7 y 8 rarezas + +* Devuelve el modelo de dispositivo especificado por el fabricante. Por ejemplo, el Samsung Focus devuelve `SGH-i917`. + +## device.platform + +Obtener el nombre del sistema operativo del dispositivo. + + var string = device.platform; + + +### Plataformas soportadas + +* Android +* BlackBerry 10 +* Browser4 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 + +### Ejemplo rápido + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 rarezas + +Dispositivos Windows Phone 7 informe de la plataforma como `WinCE`. + +### Windows Phone 8 rarezas + +Dispositivos Windows Phone 8 Informe la plataforma como `Win32NT`. + +## device.uuid + +Obtener identificador universalmente única del dispositivo ([UUID][3]). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### Descripción + +Los detalles de cómo se genera un UUID son determinados por el fabricante del dispositivo y son específicos a la plataforma del dispositivo o modelo. + +### Plataformas soportadas + +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 + +### Ejemplo rápido + + // Android: devuelve un entero de 64 bits al azar (como una cadena, otra vez!) + // el entero es generado en el primer arranque del dispositivo + // + // BlackBerry: devuelve el número PIN del dispositivo + // este es un entero único de nueve dígitos (como una cadena, aunque!) + // + // iPhone: (parafraseado de la documentación de la clase UIDevice) + // devuelve una cadena de valores hash creado a partir + // de múltiples hardware identifica. + / / Está garantizado para ser único para cada dispositivo y no puede ser atado / / a la cuenta de usuario. + // Windows Phone 7: devuelve un hash de dispositivo + usuario actual, + // si el usuario no está definido, un guid generado y persistirá hasta que se desinstala la aplicación + // + // Tizen: devuelve el dispositivo IMEI (identidad de equipo móvil internacional o IMEI es un número + // único para cada teléfono móvil GSM y UMTS. + var deviceID = device.uuid; + + +### iOS chanfle + +El `uuid` en iOS no es exclusiva de un dispositivo, pero varía para cada aplicación, para cada instalación. Cambia si puedes borrar y volver a instalar la aplicación, y posiblemente también cuándo actualizar iOS, o incluso mejorar la aplicación por la versión (evidente en iOS 5.1). El `uuid` no es un valor confiable. + +### Windows Phone 7 y 8 rarezas + +El `uuid` para Windows Phone 7 requiere el permiso `ID_CAP_IDENTITY_DEVICE`. Microsoft pronto probablemente desaprueban esta propiedad. Si la capacidad no está disponible, la aplicación genera un guid persistente que se mantiene durante la duración de la instalación de la aplicación en el dispositivo. + +## device.version + +Obtener la versión del sistema operativo. + + var string = device.version; + + +### Plataformas soportadas + +* Android 2.1 + +* BlackBerry 10 +* Explorador +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 + +### Ejemplo rápido + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. el Mango se vuelve 7.10.7720 + // Tizen: devuelve "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/fr/README.md b/plugins/cordova-plugin-device/doc/fr/README.md new file mode 100644 index 0000000..4101fd9 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/fr/README.md @@ -0,0 +1,215 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +Ce plugin définit un global `device` objet qui décrit le matériel et les logiciels de l'appareil. Bien que l'objet est dans la portée globale, il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Installation + + cordova plugin add cordova-plugin-device + + +## Propriétés + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +Retourne la version de Cordova en cours d'exécution sur l'appareil. + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Navigateur + * Firefox OS + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + +## device.model + +L'objet `device.model` retourne le nom du modèle de l'appareil/produit. Cette valeur est définie par le fabricant du périphérique et peut varier entre les différentes versions d'un même produit. + +### Plates-formes supportées + + * Android + * BlackBerry 10 + * Navigateur + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + +### Exemple court + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Voir http://theiphonewiki.com/wiki/index.php?title=Models + // + var model = device.model; + + +### Quirks Android + + * Retourne le [nom du produit](http://developer.android.com/reference/android/os/Build.html#PRODUCT) au lieu du [nom du modèle](http://developer.android.com/reference/android/os/Build.html#MODEL), ce qui équivaut souvent au nom de code de production. Par exemple, `Passion` pour le Nexus One et `voles` pour le Motorola Droid. + +### Bizarreries de paciarelli + + * Retourne le modèle du dispositif, assigné par le vendeur, par exemple `TIZEN` + +### Notes au sujet de Windows Phone 7 et 8 + + * Retourne le modèle de l'appareil spécifié par le fabricant. Par exemple `SGH-i917` pour le Samsung Focus. + +## device.platform + +Obtenir le nom de système d'exploitation de l'appareil. + + var string = device.platform; + + +### Plates-formes supportées + + * Android + * BlackBerry 10 + * Browser4 + * Firefox OS + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + +### Exemple court + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 Quirks + +Appareils Windows Phone 7 rapport de la plate-forme comme`WinCE`. + +### Notes au sujet de Windows Phone 8 + +Appareils Windows Phone 8 rapport de la plate-forme comme`Win32NT`. + +## device.uuid + +Obtenir Universally Unique Identifier de l'appareil ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + + var string = device.uuid; + + +### Description + +Les détails de comment un UUID généré sont déterminées par le fabricant du périphérique et sont spécifiques à la plate-forme ou le modèle de l'appareil. + +### Plates-formes supportées + + * Android + * BlackBerry 10 + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + +### Exemple court + + // Android : retourne un nombre entier 64-bit aléatoire (sous la forme d'une chaîne de caractères, encore !) + // Ce nombre entier est généré lors du premier démarrage de l'appareil + // + // BlackBerry : retourne le numéro PIN de l'appareil + // Il s'agit d'un nombre entier unique à neuf chiffres (sous la forme d'une chaîne de caractères cependant !) + // + // iPhone : (copié depuis la documentation de la classe UIDevice) + // Retourne une chaîne de caractères générée à partir de plusieurs caractéristiques matérielles. + / / Il est garanti pour être unique pour chaque appareil et ne peut pas être lié / / pour le compte d'utilisateur. + // Windows Phone 7 : retourne un hashage généré à partir de appareil+utilisateur actuel, + // si aucun utilisateur n'est défini, un guid est généré persistera jusqu'à ce que l'application soit désinstallée + // Tizen : retourne le numéro IMEI (International Mobile Equipment Identity) de l'appareil, ce numéro est + // unique pour chaque téléphone GSM et UMTS. + var deviceID = device.uuid; + + +### Spécificités iOS + +Le `uuid` sur iOS n'est pas propre à un périphérique, mais varie pour chaque application, pour chaque installation. Elle change si vous supprimez, puis réinstallez l'application, et éventuellement aussi quand vous mettre à jour d'iOS, ou même mettre à jour le soft par version (apparent dans iOS 5.1). Le `uuid` n'est pas une valeur fiable. + +### Notes au sujet de Windows Phone 7 et 8 + +Le `uuid` pour Windows Phone 7 requiert l'autorisation `ID_CAP_IDENTITY_DEVICE` . Microsoft va probablement bientôt obsolète de cette propriété. Si la capacité n'est pas disponible, l'application génère un guid persistant qui est maintenu pendant toute la durée de l'installation de l'application sur le périphérique. + +## device.version + +Téléchargez la version de système d'exploitation. + + var string = device.version; + + +### Plates-formes supportées + + * Android 2.1+ + * BlackBerry 10 + * Navigateur + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + +### Exemple court + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/fr/index.md b/plugins/cordova-plugin-device/doc/fr/index.md new file mode 100644 index 0000000..163e498 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/fr/index.md @@ -0,0 +1,218 @@ + + +# cordova-plugin-device + +Ce plugin définit un global `device` objet qui décrit le matériel et les logiciels de l'appareil. Bien que l'objet est dans la portée globale, il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Installation + + cordova plugin add cordova-plugin-device + + +## Propriétés + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +Retourne la version de Cordova en cours d'exécution sur l'appareil. + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Navigateur +* Firefox OS +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 + +## device.model + +L'objet `device.model` retourne le nom du modèle de l'appareil/produit. Cette valeur est définie par le fabricant du périphérique et peut varier entre les différentes versions d'un même produit. + +### Plates-formes prises en charge + +* Android +* BlackBerry 10 +* Navigateur +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 + +### Petit exemple + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Voir http://theiphonewiki.com/wiki/index.php?title=Models + // + var model = device.model; + + +### Quirks Android + +* Retourne le [nom du produit][1] au lieu du [nom du modèle][2], ce qui équivaut souvent au nom de code de production. Par exemple, `Passion` pour le Nexus One et `voles` pour le Motorola Droid. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### Bizarreries de paciarelli + +* Retourne le modèle du dispositif, assigné par le vendeur, par exemple `TIZEN` + +### Windows Phone 7 et 8 Quirks + +* Retourne le modèle de l'appareil spécifié par le fabricant. Par exemple `SGH-i917` pour le Samsung Focus. + +## device.platform + +Obtenir le nom de système d'exploitation de l'appareil. + + var string = device.platform; + + +### Plates-formes prises en charge + +* Android +* BlackBerry 10 +* Browser4 +* Firefox OS +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 + +### Petit exemple + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 Quirks + +Appareils Windows Phone 7 rapport de la plate-forme comme`WinCE`. + +### Notes au sujet de Windows Phone 8 + +Appareils Windows Phone 8 rapport de la plate-forme comme`Win32NT`. + +## device.uuid + +Obtenir Universally Unique Identifier de l'appareil ([UUID][3]). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### Description + +Les détails de comment un UUID généré sont déterminées par le fabricant du périphérique et sont spécifiques à la plate-forme ou le modèle de l'appareil. + +### Plates-formes prises en charge + +* Android +* BlackBerry 10 +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 + +### Petit exemple + + // Android : retourne un nombre entier 64-bit aléatoire (sous la forme d'une chaîne de caractères, encore !) + // Ce nombre entier est généré lors du premier démarrage de l'appareil + // + // BlackBerry : retourne le numéro PIN de l'appareil + // Il s'agit d'un nombre entier unique à neuf chiffres (sous la forme d'une chaîne de caractères cependant !) + // + // iPhone : (copié depuis la documentation de la classe UIDevice) + // Retourne une chaîne de caractères générée à partir de plusieurs caractéristiques matérielles. + / / Il est garanti pour être unique pour chaque appareil et ne peut pas être lié / / pour le compte d'utilisateur. + // Windows Phone 7 : retourne un hashage généré à partir de appareil+utilisateur actuel, + // si aucun utilisateur n'est défini, un guid est généré persistera jusqu'à ce que l'application soit désinstallée + // Tizen : retourne le numéro IMEI (International Mobile Equipment Identity) de l'appareil, ce numéro est + // unique pour chaque téléphone GSM et UMTS. + var deviceID = device.uuid; + + +### Spécificités iOS + +Le `uuid` sur iOS n'est pas propre à un périphérique, mais varie pour chaque application, pour chaque installation. Elle change si vous supprimez, puis réinstallez l'application, et éventuellement aussi quand vous mettre à jour d'iOS, ou même mettre à jour le soft par version (apparent dans iOS 5.1). Le `uuid` n'est pas une valeur fiable. + +### Windows Phone 7 et 8 Quirks + +Le `uuid` pour Windows Phone 7 requiert l'autorisation `ID_CAP_IDENTITY_DEVICE` . Microsoft va probablement bientôt obsolète de cette propriété. Si la capacité n'est pas disponible, l'application génère un guid persistant qui est maintenu pendant toute la durée de l'installation de l'application sur le périphérique. + +## device.version + +Téléchargez la version de système d'exploitation. + + var string = device.version; + + +### Plates-formes prises en charge + +* Android 2.1+ +* BlackBerry 10 +* Navigateur +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 + +### Petit exemple + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/it/README.md b/plugins/cordova-plugin-device/doc/it/README.md new file mode 100644 index 0000000..7974962 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/it/README.md @@ -0,0 +1,203 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +Questo plugin definisce un global `device` oggetto che descrive il dispositivo hardware e software. Sebbene l'oggetto sia in ambito globale, non è disponibile fino a dopo il `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Installazione + + cordova plugin add cordova-plugin-device + + +## Proprietà + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +Ottenere la versione di Cordova in esecuzione nel dispositivo. + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + +## device.model + +Il `device.model` restituisce il nome del modello del dispositivo o del prodotto. Il valore viene impostato dal produttore del dispositivo e può essere differente tra le versioni dello stesso prodotto. + +### Piattaforme supportate + + * Android + * BlackBerry 10 + * Browser + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + +### Esempio rapido + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Vedi http://theiphonewiki.com/wiki/index.php?title=Models / / modello var = device.model; + + +### Stranezze Android + + * Ottiene il [nome del prodotto](http://developer.android.com/reference/android/os/Build.html#PRODUCT) anziché il [nome del modello](http://developer.android.com/reference/android/os/Build.html#MODEL), che è spesso il nome di codice di produzione. Ad esempio, restituisce il Nexus One `Passion` , e Motorola Droid restituisce`voles`. + +### Tizen stranezze + + * Restituisce il modello di dispositivo assegnato dal fornitore, ad esempio,`TIZEN` + +### Windows Phone 7 e 8 stranezze + + * Restituisce il modello di dispositivo specificato dal produttore. Ad esempio, restituisce il Samsung Focus`SGH-i917`. + +## device.platform + +Ottenere il nome del sistema operativo del dispositivo. + + var string = device.platform; + + +### Piattaforme supportate + + * Android + * BlackBerry 10 + * Browser4 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + +### Esempio rapido + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 capricci + +Windows Phone 7 dispositivi segnalano la piattaforma come`WinCE`. + +### Windows Phone 8 stranezze + +Dispositivi Windows Phone 8 segnalano la piattaforma come`Win32NT`. + +## device.uuid + +Ottenere identificatore del dispositivo univoco universale ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + + var string = device.uuid; + + +### Descrizione + +I dettagli di come viene generato un UUID sono determinati dal produttore del dispositivo e sono specifici per la piattaforma o il modello del dispositivo. + +### Piattaforme supportate + + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + +### Esempio rapido + + / / Android: restituisce un intero casuale di 64 bit (come stringa, ancora una volta!) / / il numero intero è generato al primo avvio del dispositivo / / / / BlackBerry: restituisce il numero PIN del dispositivo / / questo è un valore integer univoco a nove cifre (come stringa, benchè!) / / / / iPhone: (parafrasato dalla documentazione della classe UIDevice) / / restituisce una stringa di valori hash creata dall'hardware più identifica. + / / È garantito per essere unica per ogni dispositivo e non può essere legato / / per l'account utente. + / / Windows Phone 7: restituisce un hash dell'utente corrente, + dispositivo / / se l'utente non è definito, un guid generato e persisterà fino a quando l'applicazione viene disinstallata / / Tizen: restituisce il dispositivo IMEI (International Mobile Equipment Identity o IMEI è un numero / / unico per ogni cellulare GSM e UMTS. + var deviceID = device.uuid; + + +### iOS Quirk + +Il `uuid` su iOS non è univoco per un dispositivo, ma varia per ogni applicazione, per ogni installazione. Cambia se si elimina e re-installare l'app, e possibilmente anche quando aggiornare iOS o anche aggiornare l'app per ogni versione (apparente in iOS 5.1). Il `uuid` non è un valore affidabile. + +### Windows Phone 7 e 8 stranezze + +Il `uuid` per Windows Phone 7 richiede l'autorizzazione `ID_CAP_IDENTITY_DEVICE` . Microsoft probabilmente sarà presto deprecare questa proprietà. Se la funzionalità non è disponibile, l'applicazione genera un guid persistente che viene mantenuto per la durata dell'installazione dell'applicazione sul dispositivo. + +## device.version + +Ottenere la versione del sistema operativo. + + var string = device.version; + + +### Piattaforme supportate + + * Android 2.1 + + * BlackBerry 10 + * Browser + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + +### Esempio rapido + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/it/index.md b/plugins/cordova-plugin-device/doc/it/index.md new file mode 100644 index 0000000..98c6200 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/it/index.md @@ -0,0 +1,206 @@ + + +# cordova-plugin-device + +Questo plugin definisce un global `device` oggetto che descrive il dispositivo hardware e software. Sebbene l'oggetto sia in ambito globale, non è disponibile fino a dopo il `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Installazione + + cordova plugin add cordova-plugin-device + + +## Proprietà + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +Ottenere la versione di Cordova in esecuzione nel dispositivo. + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 + +## device.model + +Il `device.model` restituisce il nome del modello del dispositivo o del prodotto. Il valore viene impostato dal produttore del dispositivo e può essere differente tra le versioni dello stesso prodotto. + +### Piattaforme supportate + +* Android +* BlackBerry 10 +* Browser +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 + +### Esempio rapido + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Vedi http://theiphonewiki.com/wiki/index.php?title=Models / / modello var = device.model; + + +### Stranezze Android + +* Ottiene il [nome del prodotto][1] anziché il [nome del modello][2], che è spesso il nome di codice di produzione. Ad esempio, restituisce il Nexus One `Passion` , e Motorola Droid restituisce`voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### Tizen stranezze + +* Restituisce il modello di dispositivo assegnato dal fornitore, ad esempio,`TIZEN` + +### Windows Phone 7 e 8 stranezze + +* Restituisce il modello di dispositivo specificato dal produttore. Ad esempio, restituisce il Samsung Focus`SGH-i917`. + +## device.platform + +Ottenere il nome del sistema operativo del dispositivo. + + var string = device.platform; + + +### Piattaforme supportate + +* Android +* BlackBerry 10 +* Browser4 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 + +### Esempio rapido + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 capricci + +Windows Phone 7 dispositivi segnalano la piattaforma come`WinCE`. + +### Windows Phone 8 stranezze + +Dispositivi Windows Phone 8 segnalano la piattaforma come`Win32NT`. + +## device.uuid + +Ottenere identificatore del dispositivo univoco universale ([UUID][3]). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### Descrizione + +I dettagli di come viene generato un UUID sono determinati dal produttore del dispositivo e sono specifici per la piattaforma o il modello del dispositivo. + +### Piattaforme supportate + +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 + +### Esempio rapido + + / / Android: restituisce un intero casuale di 64 bit (come stringa, ancora una volta!) / / il numero intero è generato al primo avvio del dispositivo / / / / BlackBerry: restituisce il numero PIN del dispositivo / / questo è un valore integer univoco a nove cifre (come stringa, benchè!) / / / / iPhone: (parafrasato dalla documentazione della classe UIDevice) / / restituisce una stringa di valori hash creata dall'hardware più identifica. + / / È garantito per essere unica per ogni dispositivo e non può essere legato / / per l'account utente. + / / Windows Phone 7: restituisce un hash dell'utente corrente, + dispositivo / / se l'utente non è definito, un guid generato e persisterà fino a quando l'applicazione viene disinstallata / / Tizen: restituisce il dispositivo IMEI (International Mobile Equipment Identity o IMEI è un numero / / unico per ogni cellulare GSM e UMTS. + var deviceID = device.uuid; + + +### iOS Quirk + +Il `uuid` su iOS non è univoco per un dispositivo, ma varia per ogni applicazione, per ogni installazione. Cambia se si elimina e re-installare l'app, e possibilmente anche quando aggiornare iOS o anche aggiornare l'app per ogni versione (apparente in iOS 5.1). Il `uuid` non è un valore affidabile. + +### Windows Phone 7 e 8 stranezze + +Il `uuid` per Windows Phone 7 richiede l'autorizzazione `ID_CAP_IDENTITY_DEVICE` . Microsoft probabilmente sarà presto deprecare questa proprietà. Se la funzionalità non è disponibile, l'applicazione genera un guid persistente che viene mantenuto per la durata dell'installazione dell'applicazione sul dispositivo. + +## device.version + +Ottenere la versione del sistema operativo. + + var string = device.version; + + +### Piattaforme supportate + +* Android 2.1 + +* BlackBerry 10 +* Browser +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 + +### Esempio rapido + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/ja/README.md b/plugins/cordova-plugin-device/doc/ja/README.md new file mode 100644 index 0000000..5a345f8 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/ja/README.md @@ -0,0 +1,203 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +ã“ã®ãƒ—ラグインをグローãƒãƒ«å®šç¾©ã—ã¾ã™ `device` オブジェクトã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¨ã‚½ãƒ•トウェアã«ã¤ã„ã¦èª¬æ˜Žã—ã¾ã™ã€‚ ãれã¯å¾Œã¾ã§åˆ©ç”¨å¯èƒ½ãªã‚ªãƒ–ジェクトãŒã‚°ãƒ­ãƒ¼ãƒãƒ« スコープã§ã¯ã€ `deviceready` イベント。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## インストール + + cordova plugin add cordova-plugin-device + + +## プロパティ + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +デãƒã‚¤ã‚¹ã§å®Ÿè¡Œã•れã¦ã„るコルドãƒã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å–å¾—ã—ã¾ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * ブラウザー + * Firefox ã® OS + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + +## device.model + +`device.model`ã€ãƒ‡ãƒã‚¤ã‚¹ã®ãƒ¢ãƒ‡ãƒ«ã¾ãŸã¯è£½å“ã®åå‰ã‚’è¿”ã—ã¾ã™ã€‚値ã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®è£½é€ å…ƒã«ã‚ˆã£ã¦è¨­å®šã•れã€åŒã˜è£½å“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³é–“ã§ç•°ãªã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * ブラックベリー 10 + * ブラウザー + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + +### ç°¡å˜ãªä¾‹ + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Http://theiphonewiki.com/wiki/index.php?title=Models ã‚’å‚ç…§ã—ã¦ãã ã•ã„//var モデル = device.model; + + +### Android ã®ç™– + + * 生産コードåã¯[モデルå](http://developer.android.com/reference/android/os/Build.html#MODEL)ã®ä»£ã‚りã«[製å“å](http://developer.android.com/reference/android/os/Build.html#PRODUCT)ã‚’å–å¾—ã—ã¾ã™ã€‚ ãŸã¨ãˆã°ã€ãƒã‚¯ã‚µã‚¹ 1 ã¤ã‚’è¿”ã—ã¾ã™ `Passion` ã€Motorola ã®ãƒ‰ãƒ­ã‚¤ãƒ‰ã‚’è¿”ã—ã¾ã™`voles`. + +### Tizen ã®ç™– + + * ãŸã¨ãˆã°ã€ãƒ™ãƒ³ãƒ€ãƒ¼ã«ã‚ˆã£ã¦å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„るデãƒã‚¤ã‚¹ã®ãƒ¢ãƒ‡ãƒ«ã‚’è¿”ã—ã¾ã™`TIZEN` + +### Windows Phone 7 㨠8 ç™– + + * 製造元ã«ã‚ˆã£ã¦æŒ‡å®šã•れãŸãƒ‡ãƒã‚¤ã‚¹ã®ãƒ¢ãƒ‡ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚ãŸã¨ãˆã°ã€ä¸‰æ˜Ÿãƒ•ォーカスを返ã—ã¾ã™`SGH-i917`. + +## device.platform + +デãƒã‚¤ã‚¹ã®ã‚ªãƒšãƒ¬ãƒ¼ãƒ†ã‚£ãƒ³ã‚° システムåã‚’å–å¾—ã—ã¾ã™ã€‚ + + var string = device.platform; + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * ブラックベリー 10 + * Browser4 + * Firefox ã® OS + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + +### ç°¡å˜ãªä¾‹ + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 ã®ç™– + +Windows Phone 7 デãƒã‚¤ã‚¹ã¨ãƒ—ラットフォームを報告ã—ã¾ã™ã€‚`WinCE`. + +### Windows Phone 8 ç™– + +Windows Phone 8 デãƒã‚¤ã‚¹ã¨ãƒ—ラットフォームを報告ã—ã¾ã™ã€‚`Win32NT`. + +## device.uuid + +デãƒã‚¤ã‚¹ã®ãƒ¦ãƒ‹ãƒãƒ¼ã‚µãƒ« ・ ãƒ¦ãƒ‹ãƒ¼ã‚¯è­˜åˆ¥å­ ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)ã‚’å–å¾—ã—ã¾ã™ã€‚). + + var string = device.uuid; + + +### 解説 + +UUID を生æˆã™ã‚‹æ–¹æ³•ã®è©³ç´°ã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®è£½é€ å…ƒã«ã‚ˆã£ã¦æ±ºå®šã•れã€ãƒ‡ãƒã‚¤ã‚¹ã®ãƒ—ラットフォームやモデルã«å›ºæœ‰ã§ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド + * ブラックベリー 10 + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + +### ç°¡å˜ãªä¾‹ + + //アンドロイド: ランダム㪠64 ãƒ“ãƒƒãƒˆã®æ•´æ•° (を文字列ã¨ã—ã¦è¿”ã—ã¾ã™ã€å†ã³ ï¼ï¼‰/デãƒã‚¤ã‚¹ã®æœ€åˆã®èµ·å‹•時ã«ç”Ÿæˆã•れる整数/////ブラックベリー: デãƒã‚¤ã‚¹ã®ãƒ”ン番å·ã‚’è¿”ã—ã¾ã™//ã“れ㯠9 æ¡ã®ä¸€æ„ãªæ•´æ•° (を文字列ã¨ã—ã¦ã‚‚ ï¼)////iPhone: (UIDevice クラスã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‹ã‚‰è¨€ã„æ›ãˆï¼‰//識別複数ã®ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‹ã‚‰ä½œæˆã•れãŸãƒãƒƒã‚·ãƒ¥å€¤ã®æ–‡å­—列を返ã—ã¾ã™ã€‚。 + //ãれã¯ã™ã¹ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ã«å¯¾ã—ã¦ä¸€æ„ã§ã‚ã‚‹ã“ã¨ãŒä¿è¨¼ã•ã‚Œã€æŽ¥ç¶šã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“//ユーザー アカウント。 + //Windows Phone 7: デãƒã‚¤ã‚¹ + ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒãƒƒã‚·ãƒ¥ã‚’è¿”ã—ã¾ã™//ユーザーãŒå®šç¾©ã•れã¦ã„ãªã„å ´åˆ guid ãŒç”Ÿæˆã•れã€ã‚¢ãƒ—リãŒã‚¢ãƒ³ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れるã¾ã§ä¿æŒã•れã¾ã™//Tizen: デãƒã‚¤ã‚¹ã® IMEI ã‚’è¿”ã—ã¾ã™ (国際モãƒã‚¤ãƒ«æ©Ÿå™¨ã‚¢ã‚¤ãƒ‡ãƒ³ãƒ†ã‚£ãƒ†ã‚£ã¾ãŸã¯ IMEI ã¯ç•ªå·ã§ã™//ã™ã¹ã¦ã® GSM ãŠã‚ˆã³ UMTS ã®æºå¸¯é›»è©±ã«å›ºæœ‰ã§ã™ã€‚ + var deviceID = device.uuid; + + +### iOS ã®æ°—ã¾ãれ + +`uuid`IOS ã§ã€ãƒ‡ãƒã‚¤ã‚¹ã«å›ºæœ‰ã§ã¯ãªã„インストールã”ã¨ã€ã‚¢ãƒ—リケーションã”ã¨ã«ç•°ãªã‚Šã¾ã™ã€‚ 削除ã€ã‚¢ãƒ—リをå†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ãŸå ´åˆã«å¤‰æ›´ã¨å¤šåˆ†ã¾ãŸã¨ãアップグレード iOS ã®, ã¾ãŸã¯ã‚‚アップグレードã™ã‚‹ã‚¢ãƒ—リ (iOS ã® 5.1 ã§æ˜Žã‚‰ã‹ã«ï¼‰ ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã”ã¨ã€‚ `uuid`ã¯ä¿¡é ¼æ€§ã®é«˜ã„値ã§ã¯ã‚りã¾ã›ã‚“。 + +### Windows Phone 7 㨠8 ç™– + +`uuid`ã®ãŸã‚ã« Windows Phone 7 ã«ã¯ã€æ¨©é™ãŒå¿…è¦ã§ã™ `ID_CAP_IDENTITY_DEVICE` 。 Microsoft ã¯ã™ãã«ã“ã®ãƒ—ロパティを廃止ã—ã¦å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ 機能ãŒåˆ©ç”¨ã§ããªã„å ´åˆã€ã‚¢ãƒ—リケーションã¯ãƒ‡ãƒã‚¤ã‚¹ã¸ã®ã‚¢ãƒ—リケーションã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã®æŒç¶šæœŸé–“ã®ãŸã‚ã«ä¿æŒã•れã¦ã„る永続的㪠guid を生æˆã—ã¾ã™ã€‚ + +## device.version + +オペレーティング システムã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å–å¾—ã—ã¾ã™ã€‚ + + var string = device.version; + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * アンドロイド 2.1 + + * ブラックベリー 10 + * ブラウザー + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + +### ç°¡å˜ãªä¾‹ + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/ja/index.md b/plugins/cordova-plugin-device/doc/ja/index.md new file mode 100644 index 0000000..b4030fd --- /dev/null +++ b/plugins/cordova-plugin-device/doc/ja/index.md @@ -0,0 +1,206 @@ + + +# cordova-plugin-device + +ã“ã®ãƒ—ラグインをグローãƒãƒ«å®šç¾©ã—ã¾ã™ `device` オブジェクトã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã¨ã‚½ãƒ•トウェアã«ã¤ã„ã¦èª¬æ˜Žã—ã¾ã™ã€‚ ãれã¯å¾Œã¾ã§åˆ©ç”¨å¯èƒ½ãªã‚ªãƒ–ジェクトãŒã‚°ãƒ­ãƒ¼ãƒãƒ« スコープã§ã¯ã€ `deviceready` イベント。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## インストール + + cordova plugin add cordova-plugin-device + + +## プロパティ + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +デãƒã‚¤ã‚¹ã§å®Ÿè¡Œã•れã¦ã„るコルドãƒã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å–å¾—ã—ã¾ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* ブラウザー +* Firefox ã® OS +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 + +## device.model + +`device.model`ã€ãƒ‡ãƒã‚¤ã‚¹ã®ãƒ¢ãƒ‡ãƒ«ã¾ãŸã¯è£½å“ã®åå‰ã‚’è¿”ã—ã¾ã™ã€‚値ã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®è£½é€ å…ƒã«ã‚ˆã£ã¦è¨­å®šã•れã€åŒã˜è£½å“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³é–“ã§ç•°ãªã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* ブラックベリー 10 +* ブラウザー +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 + +### ç°¡å˜ãªä¾‹ + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Http://theiphonewiki.com/wiki/index.php?title=Models ã‚’å‚ç…§ã—ã¦ãã ã•ã„//var モデル = device.model; + + +### Android ã®ç™– + +* 生産コードåã¯[モデルå][1]ã®ä»£ã‚りã«[製å“å][2]ã‚’å–å¾—ã—ã¾ã™ã€‚ ãŸã¨ãˆã°ã€ãƒã‚¯ã‚µã‚¹ 1 ã¤ã‚’è¿”ã—ã¾ã™ `Passion` ã€Motorola ã®ãƒ‰ãƒ­ã‚¤ãƒ‰ã‚’è¿”ã—ã¾ã™`voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#MODEL + [2]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + +### Tizen ã®ç™– + +* ãŸã¨ãˆã°ã€ãƒ™ãƒ³ãƒ€ãƒ¼ã«ã‚ˆã£ã¦å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„るデãƒã‚¤ã‚¹ã®ãƒ¢ãƒ‡ãƒ«ã‚’è¿”ã—ã¾ã™`TIZEN` + +### Windows Phone 7 㨠8 ç™– + +* 製造元ã«ã‚ˆã£ã¦æŒ‡å®šã•れãŸãƒ‡ãƒã‚¤ã‚¹ã®ãƒ¢ãƒ‡ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚ãŸã¨ãˆã°ã€ä¸‰æ˜Ÿãƒ•ォーカスを返ã—ã¾ã™`SGH-i917`. + +## device.platform + +デãƒã‚¤ã‚¹ã®ã‚ªãƒšãƒ¬ãƒ¼ãƒ†ã‚£ãƒ³ã‚° システムåã‚’å–å¾—ã—ã¾ã™ã€‚ + + var string = device.platform; + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* ブラックベリー 10 +* Browser4 +* Firefox ã® OS +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 + +### ç°¡å˜ãªä¾‹ + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 ã®ç™– + +Windows Phone 7 デãƒã‚¤ã‚¹ã¨ãƒ—ラットフォームを報告ã—ã¾ã™ã€‚`WinCE`. + +### Windows Phone 8 ç™– + +Windows Phone 8 デãƒã‚¤ã‚¹ã¨ãƒ—ラットフォームを報告ã—ã¾ã™ã€‚`Win32NT`. + +## device.uuid + +デãƒã‚¤ã‚¹ã®ãƒ¦ãƒ‹ãƒãƒ¼ã‚µãƒ« ・ ãƒ¦ãƒ‹ãƒ¼ã‚¯è­˜åˆ¥å­ ([UUID][3]ã‚’å–å¾—ã—ã¾ã™ã€‚). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### 説明 + +UUID を生æˆã™ã‚‹æ–¹æ³•ã®è©³ç´°ã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®è£½é€ å…ƒã«ã‚ˆã£ã¦æ±ºå®šã•れã€ãƒ‡ãƒã‚¤ã‚¹ã®ãƒ—ラットフォームやモデルã«å›ºæœ‰ã§ã™ã€‚ + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド +* ブラックベリー 10 +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 + +### ç°¡å˜ãªä¾‹ + + //アンドロイド: ランダム㪠64 ãƒ“ãƒƒãƒˆã®æ•´æ•° (を文字列ã¨ã—ã¦è¿”ã—ã¾ã™ã€å†ã³ ï¼ï¼‰/デãƒã‚¤ã‚¹ã®æœ€åˆã®èµ·å‹•時ã«ç”Ÿæˆã•れる整数/////ブラックベリー: デãƒã‚¤ã‚¹ã®ãƒ”ン番å·ã‚’è¿”ã—ã¾ã™//ã“れ㯠9 æ¡ã®ä¸€æ„ãªæ•´æ•° (を文字列ã¨ã—ã¦ã‚‚ ï¼)////iPhone: (UIDevice クラスã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‹ã‚‰è¨€ã„æ›ãˆï¼‰//識別複数ã®ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‹ã‚‰ä½œæˆã•れãŸãƒãƒƒã‚·ãƒ¥å€¤ã®æ–‡å­—列を返ã—ã¾ã™ã€‚。 + //ãれã¯ã™ã¹ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ã«å¯¾ã—ã¦ä¸€æ„ã§ã‚ã‚‹ã“ã¨ãŒä¿è¨¼ã•ã‚Œã€æŽ¥ç¶šã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“//ユーザー アカウント。 + //Windows Phone 7: デãƒã‚¤ã‚¹ + ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒãƒƒã‚·ãƒ¥ã‚’è¿”ã—ã¾ã™//ユーザーãŒå®šç¾©ã•れã¦ã„ãªã„å ´åˆ guid ãŒç”Ÿæˆã•れã€ã‚¢ãƒ—リãŒã‚¢ãƒ³ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れるã¾ã§ä¿æŒã•れã¾ã™//Tizen: デãƒã‚¤ã‚¹ã® IMEI ã‚’è¿”ã—ã¾ã™ (国際モãƒã‚¤ãƒ«æ©Ÿå™¨ã‚¢ã‚¤ãƒ‡ãƒ³ãƒ†ã‚£ãƒ†ã‚£ã¾ãŸã¯ IMEI ã¯ç•ªå·ã§ã™//ã™ã¹ã¦ã® GSM ãŠã‚ˆã³ UMTS ã®æºå¸¯é›»è©±ã«å›ºæœ‰ã§ã™ã€‚ + var deviceID = device.uuid; + + +### iOS ã®æ°—ã¾ãれ + +`uuid`IOS ã§ã€ãƒ‡ãƒã‚¤ã‚¹ã«å›ºæœ‰ã§ã¯ãªã„インストールã”ã¨ã€ã‚¢ãƒ—リケーションã”ã¨ã«ç•°ãªã‚Šã¾ã™ã€‚ 削除ã€ã‚¢ãƒ—リをå†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ãŸå ´åˆã«å¤‰æ›´ã¨å¤šåˆ†ã¾ãŸã¨ãアップグレード iOS ã®, ã¾ãŸã¯ã‚‚アップグレードã™ã‚‹ã‚¢ãƒ—リ (iOS ã® 5.1 ã§æ˜Žã‚‰ã‹ã«ï¼‰ ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã”ã¨ã€‚ `uuid`ã¯ä¿¡é ¼æ€§ã®é«˜ã„値ã§ã¯ã‚りã¾ã›ã‚“。 + +### Windows Phone 7 㨠8 ç™– + +`uuid`ã®ãŸã‚ã« Windows Phone 7 ã«ã¯ã€æ¨©é™ãŒå¿…è¦ã§ã™ `ID_CAP_IDENTITY_DEVICE` 。 Microsoft ã¯ã™ãã«ã“ã®ãƒ—ロパティを廃止ã—ã¦å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ 機能ãŒåˆ©ç”¨ã§ããªã„å ´åˆã€ã‚¢ãƒ—リケーションã¯ãƒ‡ãƒã‚¤ã‚¹ã¸ã®ã‚¢ãƒ—リケーションã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã®æŒç¶šæœŸé–“ã®ãŸã‚ã«ä¿æŒã•れã¦ã„る永続的㪠guid を生æˆã—ã¾ã™ã€‚ + +## device.version + +オペレーティング システムã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å–å¾—ã—ã¾ã™ã€‚ + + var string = device.version; + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* アンドロイド 2.1 + +* ブラックベリー 10 +* ブラウザー +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 + +### ç°¡å˜ãªä¾‹ + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/ko/README.md b/plugins/cordova-plugin-device/doc/ko/README.md new file mode 100644 index 0000000..a818aac --- /dev/null +++ b/plugins/cordova-plugin-device/doc/ko/README.md @@ -0,0 +1,203 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +ì´ í”ŒëŸ¬ê·¸ì¸ ì •ì˜ ì „ì—­ `device` 개체, 디바ì´ìŠ¤ì˜ í•˜ë“œì›¨ì–´ ë° ì†Œí”„íŠ¸ì›¨ì–´ì— ì„¤ëª… 합니다. 개체는 ì „ì—­ 범위ì—서 ë¹„ë¡ ê·¸ê²ƒì€ í›„ê¹Œì§€ 사용할 수 있는 `deviceready` ì´ë²¤íЏ. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## 설치 + + cordova plugin add cordova-plugin-device + + +## ì†ì„± + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +코르ë„바는 장치ì—서 실행 ì¤‘ì¸ ë²„ì „ì„ ì–»ì„. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * 브ë¼ìš°ì € + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + +## device.model + +`device.model`소ìžì˜ ëª¨ë¸ ë˜ëŠ” ì œí’ˆì˜ ì´ë¦„ì„ ë°˜í™˜ 합니다. ê°’ 장치 제조업체ì—서 설정 ë˜ ê³  ë™ì¼ ì œí’ˆì˜ ë²„ì „ ê°„ì— ë‹¤ë¥¼ 수 있습니다. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * 블랙베리 10 + * 브ë¼ìš°ì € + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + +### 빠른 예제 + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Http://theiphonewiki.com/wiki/index.php?title=Models 참조 / / var ëª¨ë¸ = device.model; + + +### 안 드 로ì´ë“œ ë‹¨ì  + + * ì–´ë–¤ì€ ì¢…ì¢… 프로ë•ì…˜ 코드 ì´ë¦„ 대신 [제품 ëª¨ë¸ ì´ë¦„](http://developer.android.com/reference/android/os/Build.html#MODEL), [제품 ì´ë¦„](http://developer.android.com/reference/android/os/Build.html#PRODUCT) ì„ ê°€ì ¸ì˜µë‹ˆë‹¤. 예를 들어 넥서스 하나 반환 합니다 `Passion` , ëª¨í† ë¡œë¼ Droid를 반환 합니다`voles`. + +### Tizen 특수 + + * 예를 들어, 공급 ì—…ì²´ì— ì˜í•´ 할당 ëœ ë””ë°”ì´ìФ 모ë¸ì„ 반환 합니다.`TIZEN` + +### Windows Phone 7, 8 특수 + + * 제조업체ì—서 지정 하는 장치 모ë¸ì„ 반환 합니다. 예를 들어 삼성 í¬ì»¤ìŠ¤ë¥¼ 반환 합니다.`SGH-i917`. + +## device.platform + +ìž¥ì¹˜ì˜ ìš´ì˜ ì²´ì œ ì´ë¦„ì„ ì–»ì„. + + var string = device.platform; + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Browser4 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + +### 빠른 예제 + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 ë‹¨ì  + +Windows Phone 7 장치 ë³´ê³  플랫í¼ìœ¼ë¡œ`WinCE`. + +### Windows Phone 8 ë‹¨ì  + +Windows Phone 8 장치 ë³´ê³  플랫í¼ìœ¼ë¡œ`Win32NT`. + +## device.uuid + +소ìžì˜ 보편ì ìœ¼ë¡œ 고유 ì‹ë³„ìž ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier) 를 ì–»ì„합니다). + + var string = device.uuid; + + +### 설명 + +UUID ìƒì„± ë°©ë²•ì˜ ìžì„¸í•œ ë‚´ìš©ì€ ìž¥ì¹˜ ì œì¡°ì—…ì²´ì— ì˜í•´ ê²°ì • ë©ë‹ˆë‹¤ ë° ì†Œìžì˜ í”Œëž«í¼ ì´ë‚˜ 모ë¸. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ + * 블랙베리 10 + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + +### 빠른 예제 + + / / 안 드 로ì´ë“œ: (문ìžì—´ë¡œ 다시!) ìž„ì˜ì˜ 64 비트 정수를 반환 합니다 / / 정수 ìž¥ì¹˜ì˜ ì²« 번째 부팅ì—서 ìƒì„± / / / / 블랙베리: 디바ì´ìŠ¤ì˜ í•€ 번호를 반환 합니다 / / ì´ê²ƒì€ 9 ìžë¦¬ 고유 정수 (문ìžì—´ë¡œ 비ë¡!) / / / / ì•„ì´í°: (UIDevice í´ëž˜ìФ 설명서ì—서 ìŠ ì—ˆ) / / 문ìžì—´ 여러 하드웨어ì—서 ìƒì„± 하는 해시 ê°’ì„ ì‹ë³„ 하는 반환 합니다. + / ê·¸ê²ƒì€ ëª¨ë“  ìž¥ì¹˜ì— ëŒ€ 한 고유 해야 보장 ë˜ ê³  ë¬¶ì¼ ìˆ˜ 없습니다 / / / ì‚¬ìš©ìž ê³„ì •ì—. + / / Windows Phone 7: 장치 + 현재 사용ìžì˜ 해시를 반환 합니다 / / ì‚¬ìš©ìž ì •ì˜ ë˜ì§€ ì•Šì€ ê²½ìš° guid ìƒì„± ë˜ ê³  ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì œê±°í•  때까지 유지 ë©ë‹ˆë‹¤ / / Tizen: 반환 장치 IMEI (êµ­ì œ ëª¨ë°”ì¼ ê¸°ê¸° ì‹ë³„ ë˜ëŠ” IMEI 숫ìžìž…니다 / / 모든 GSM와 UMTS 휴대 ì „í™” 고유. + var deviceID = device.uuid; + + +### iOS 특질 + +`uuid`ios ìž¥ì¹˜ì— ê³ ìœ  하지 않습니다 하지만 ê° ì„¤ì¹˜ì— ëŒ€ 한 ì‘ìš© 프로그램 마다 다릅니다. ì‚­ì œ 하 ê³  다시 애플 리 ì¼€ì´ ì…˜ì„ ì„¤ì¹˜ 하는 경우 변경 가능 하 게 ë˜í•œ iOS를 업그레ì´ë“œ 하거나 때 버전 (iOS 5.1ì—ì—서 명백한) 당 ì‘ìš© 프로그램 업그레ì´ë“œë„ 하 ê³ . `uuid`ì€ ì‹ ë¢°í•  수 있는 ê°’ì´ ì•„ë‹™ë‹ˆë‹¤. + +### Windows Phone 7, 8 특수 + +`uuid`Windows Phone 7 í•„ìš” 허가 `ID_CAP_IDENTITY_DEVICE` . Microsoft는 ê³§ì´ ì†ì„±ì„ 세ì›ë‹ˆë‹¤ ê°€ëŠ¥ì„±ì´ ê²ƒìž…ë‹ˆë‹¤. ê¸°ëŠ¥ì„ ì‚¬ìš©í•  수 없는 경우 ì‘ìš© 프로그램 ìž¥ì¹˜ì— ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ì„¤ì¹˜ 하는 ë™ì•ˆ 유지 하는 ì˜êµ¬ guid를 ìƒì„± 합니다. + +## device.version + +ìš´ì˜ ì²´ì œ ë²„ì „ì„ ì–»ì„. + + var string = device.version; + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 안 드 로ì´ë“œ 2.1 + + * 블랙베리 10 + * 브ë¼ìš°ì € + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + +### 빠른 예제 + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/ko/index.md b/plugins/cordova-plugin-device/doc/ko/index.md new file mode 100644 index 0000000..0fe38a7 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/ko/index.md @@ -0,0 +1,206 @@ + + +# cordova-plugin-device + +ì´ í”ŒëŸ¬ê·¸ì¸ ì •ì˜ ì „ì—­ `device` 개체, 디바ì´ìŠ¤ì˜ í•˜ë“œì›¨ì–´ ë° ì†Œí”„íŠ¸ì›¨ì–´ì— ì„¤ëª… 합니다. 개체는 ì „ì—­ 범위ì—서 ë¹„ë¡ ê·¸ê²ƒì€ í›„ê¹Œì§€ 사용할 수 있는 `deviceready` ì´ë²¤íЏ. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## 설치 + + cordova plugin add cordova-plugin-device + + +## ì†ì„± + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +코르ë„바는 장치ì—서 실행 ì¤‘ì¸ ë²„ì „ì„ ì–»ì„. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* 브ë¼ìš°ì € +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +## device.model + +`device.model`소ìžì˜ ëª¨ë¸ ë˜ëŠ” ì œí’ˆì˜ ì´ë¦„ì„ ë°˜í™˜ 합니다. ê°’ 장치 제조업체ì—서 설정 ë˜ ê³  ë™ì¼ ì œí’ˆì˜ ë²„ì „ ê°„ì— ë‹¤ë¥¼ 수 있습니다. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* 블랙베리 10 +* 브ë¼ìš°ì € +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +### 빠른 예제 + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Http://theiphonewiki.com/wiki/index.php?title=Models 참조 / / var ëª¨ë¸ = device.model; + + +### 안 드 로ì´ë“œ ë‹¨ì  + +* ì–´ë–¤ì€ ì¢…ì¢… 프로ë•ì…˜ 코드 ì´ë¦„ 대신 [제품 ëª¨ë¸ ì´ë¦„][1], [제품 ì´ë¦„][2] ì„ ê°€ì ¸ì˜µë‹ˆë‹¤. 예를 들어 넥서스 하나 반환 합니다 `Passion` , ëª¨í† ë¡œë¼ Droid를 반환 합니다`voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#MODEL + [2]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + +### Tizen 특수 + +* 예를 들어, 공급 ì—…ì²´ì— ì˜í•´ 할당 ëœ ë””ë°”ì´ìФ 모ë¸ì„ 반환 합니다.`TIZEN` + +### Windows Phone 7, 8 특수 + +* 제조업체ì—서 지정 하는 장치 모ë¸ì„ 반환 합니다. 예를 들어 삼성 í¬ì»¤ìŠ¤ë¥¼ 반환 합니다.`SGH-i917`. + +## device.platform + +ìž¥ì¹˜ì˜ ìš´ì˜ ì²´ì œ ì´ë¦„ì„ ì–»ì„. + + var string = device.platform; + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Browser4 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +### 빠른 예제 + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 ë‹¨ì  + +Windows Phone 7 장치 ë³´ê³  플랫í¼ìœ¼ë¡œ`WinCE`. + +### Windows Phone 8 ë‹¨ì  + +Windows Phone 8 장치 ë³´ê³  플랫í¼ìœ¼ë¡œ`Win32NT`. + +## device.uuid + +소ìžì˜ 보편ì ìœ¼ë¡œ 고유 ì‹ë³„ìž ([UUID][3] 를 ì–»ì„합니다). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### 설명 + +UUID ìƒì„± ë°©ë²•ì˜ ìžì„¸í•œ ë‚´ìš©ì€ ìž¥ì¹˜ ì œì¡°ì—…ì²´ì— ì˜í•´ ê²°ì • ë©ë‹ˆë‹¤ ë° ì†Œìžì˜ í”Œëž«í¼ ì´ë‚˜ 모ë¸. + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ +* 블랙베리 10 +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +### 빠른 예제 + + / / 안 드 로ì´ë“œ: (문ìžì—´ë¡œ 다시!) ìž„ì˜ì˜ 64 비트 정수를 반환 합니다 / / 정수 ìž¥ì¹˜ì˜ ì²« 번째 부팅ì—서 ìƒì„± / / / / 블랙베리: 디바ì´ìŠ¤ì˜ í•€ 번호를 반환 합니다 / / ì´ê²ƒì€ 9 ìžë¦¬ 고유 정수 (문ìžì—´ë¡œ 비ë¡!) / / / / ì•„ì´í°: (UIDevice í´ëž˜ìФ 설명서ì—서 ìŠ ì—ˆ) / / 문ìžì—´ 여러 하드웨어ì—서 ìƒì„± 하는 해시 ê°’ì„ ì‹ë³„ 하는 반환 합니다. + / ê·¸ê²ƒì€ ëª¨ë“  ìž¥ì¹˜ì— ëŒ€ 한 고유 해야 보장 ë˜ ê³  ë¬¶ì¼ ìˆ˜ 없습니다 / / / ì‚¬ìš©ìž ê³„ì •ì—. + / / Windows Phone 7: 장치 + 현재 사용ìžì˜ 해시를 반환 합니다 / / ì‚¬ìš©ìž ì •ì˜ ë˜ì§€ ì•Šì€ ê²½ìš° guid ìƒì„± ë˜ ê³  ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì œê±°í•  때까지 유지 ë©ë‹ˆë‹¤ / / Tizen: 반환 장치 IMEI (êµ­ì œ ëª¨ë°”ì¼ ê¸°ê¸° ì‹ë³„ ë˜ëŠ” IMEI 숫ìžìž…니다 / / 모든 GSM와 UMTS 휴대 ì „í™” 고유. + var deviceID = device.uuid; + + +### iOS 특질 + +`uuid`ios ìž¥ì¹˜ì— ê³ ìœ  하지 않습니다 하지만 ê° ì„¤ì¹˜ì— ëŒ€ 한 ì‘ìš© 프로그램 마다 다릅니다. ì‚­ì œ 하 ê³  다시 애플 리 ì¼€ì´ ì…˜ì„ ì„¤ì¹˜ 하는 경우 변경 가능 하 게 ë˜í•œ iOS를 업그레ì´ë“œ 하거나 때 버전 (iOS 5.1ì—ì—서 명백한) 당 ì‘ìš© 프로그램 업그레ì´ë“œë„ 하 ê³ . `uuid`ì€ ì‹ ë¢°í•  수 있는 ê°’ì´ ì•„ë‹™ë‹ˆë‹¤. + +### Windows Phone 7, 8 특수 + +`uuid`Windows Phone 7 í•„ìš” 허가 `ID_CAP_IDENTITY_DEVICE` . Microsoft는 ê³§ì´ ì†ì„±ì„ 세ì›ë‹ˆë‹¤ ê°€ëŠ¥ì„±ì´ ê²ƒìž…ë‹ˆë‹¤. ê¸°ëŠ¥ì„ ì‚¬ìš©í•  수 없는 경우 ì‘ìš© 프로그램 ìž¥ì¹˜ì— ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ì„¤ì¹˜ 하는 ë™ì•ˆ 유지 하는 ì˜êµ¬ guid를 ìƒì„± 합니다. + +## device.version + +ìš´ì˜ ì²´ì œ ë²„ì „ì„ ì–»ì„. + + var string = device.version; + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 안 드 로ì´ë“œ 2.1 + +* 블랙베리 10 +* 브ë¼ìš°ì € +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +### 빠른 예제 + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/pl/README.md b/plugins/cordova-plugin-device/doc/pl/README.md new file mode 100644 index 0000000..c38832d --- /dev/null +++ b/plugins/cordova-plugin-device/doc/pl/README.md @@ -0,0 +1,214 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +Ten plugin okreÅ›la globalne `device` obiekt, który opisuje urzÄ…dzenia sprzÄ™towe i programowe. Mimo, że obiekt jest w globalnym zasiÄ™gu, nie jest dostÄ™pne dopiero po `deviceready` zdarzenie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Instalacja + + cordova plugin add cordova-plugin-device + + +## WÅ‚aÅ›ciwoÅ›ci + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +Pobierz wersjÄ™ Cordova dziaÅ‚a na urzÄ…dzeniu. + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * PrzeglÄ…darka + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + +## device.model + +`device.model`Zwraca nazwÄ™ modelu lub produktu. Wartość jest zestaw przez producenta urzÄ…dzenia i mogÄ… siÄ™ różnić miÄ™dzy wersjami tego samego produktu. + +### ObsÅ‚ugiwane platformy + + * Android + * BlackBerry 10 + * PrzeglÄ…darka + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + +### Szybki przykÅ‚ad + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Zobacz http://theiphonewiki.com/wiki/index.php?title=Models / / modelu var = device.model; + + +### Dziwactwa Androida + + * Pobiera [nazwÄ™ produktu](http://developer.android.com/reference/android/os/Build.html#PRODUCT) zamiast [nazwy modelu](http://developer.android.com/reference/android/os/Build.html#MODEL), który czÄ™sto jest nazwÄ… kod produkcji. Na przykÅ‚ad, Nexus One zwraca `Passion` , i zwraca Motorola Droid`voles`. + +### Dziwactwa Tizen + + * Zwraca modelu urzÄ…dzenia przypisane przez dostawcÄ™, na przykÅ‚ad,`TIZEN` + +### Windows Phone 7 i 8 dziwactwa + + * Zwraca modelu urzÄ…dzenia, okreÅ›lonej przez producenta. Na przykÅ‚ad Samsung ostroÅ›ci zwraca`SGH-i917`. + +## device.platform + +Uzyskać nazwÄ™ systemu operacyjnego urzÄ…dzenia. + + var string = device.platform; + + +### ObsÅ‚ugiwane platformy + + * Android + * BlackBerry 10 + * Browser4 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + +### Szybki przykÅ‚ad + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Dziwactwa Windows Phone 7 + +UrzÄ…dzenia Windows Phone 7 raport platformy jako`WinCE`. + +### Windows Phone 8 dziwactwa + +UrzÄ…dzenia Windows Phone 8 raport platformy jako`Win32NT`. + +## device.uuid + +SiÄ™ urzÄ…dzenia uniwersalnie unikatowy identyfikator ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + + var string = device.uuid; + + +### Opis + +Szczegóły jak UUID jest generowane sÄ… okreÅ›lane przez producenta urzÄ…dzenia i sÄ… specyficzne dla platformy lub modelu urzÄ…dzenia. + +### ObsÅ‚ugiwane platformy + + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + +### Szybki przykÅ‚ad + + // Android: Returns a random 64-bit integer (as a string, again!) + // The integer is generated on the device's first boot + // + // BlackBerry: Returns the PIN number of the device + // This is a nine-digit unique integer (as a string, though!) + // + // iPhone: (Paraphrased from the UIDevice Class documentation) + // Returns a string of hash values created from multiple hardware identifies. + // It is guaranteed to be unique for every device and can't be tied + // to the user account. + // Windows Phone 7 : Returns a hash of device+current user, + // if the user is not defined, a guid is generated and will persist until the app is uninstalled + // Tizen: returns the device IMEI (International Mobile Equipment Identity or IMEI is a number + // unique to every GSM and UMTS mobile phone. + var deviceID = device.uuid; + + +### iOS dziwactwo + +`uuid`Na iOS nie jest przypisany do urzÄ…dzenia, ale różni siÄ™ dla każdej aplikacji, dla każdej instalacji. Zmienia siÄ™ jeÅ›li możesz usunąć i ponownie zainstalować aplikacjÄ™, a ewentualnie także po aktualizacji iOS czy nawet uaktualnienia aplikacji dla wersji (widoczny w iOS 5.1). `uuid`Jest nie wiarygodne wartoÅ›ci. + +### Windows Phone 7 i 8 dziwactwa + +`uuid`Dla Windows Phone 7 wymaga uprawnieÅ„ `ID_CAP_IDENTITY_DEVICE` . Microsoft bÄ™dzie prawdopodobnie potÄ™piać ten wkrótce. JeÅ›li funkcja nie jest dostÄ™pna, aplikacja generuje trwaÅ‚e identyfikator guid, który jest utrzymywany przez czas trwania instalacji aplikacji na urzÄ…dzeniu. + +## device.version + +Pobierz wersjÄ™ systemu operacyjnego. + + var string = device.version; + + +### ObsÅ‚ugiwane platformy + + * Android 2.1 + + * BlackBerry 10 + * PrzeglÄ…darka + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + +### Szybki przykÅ‚ad + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/pl/index.md b/plugins/cordova-plugin-device/doc/pl/index.md new file mode 100644 index 0000000..acc8f9c --- /dev/null +++ b/plugins/cordova-plugin-device/doc/pl/index.md @@ -0,0 +1,206 @@ + + +# cordova-plugin-device + +Ten plugin okreÅ›la globalne `device` obiekt, który opisuje urzÄ…dzenia sprzÄ™towe i programowe. Mimo, że obiekt jest w globalnym zasiÄ™gu, nie jest dostÄ™pne dopiero po `deviceready` zdarzenie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## Instalacja + + cordova plugin add cordova-plugin-device + + +## WÅ‚aÅ›ciwoÅ›ci + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +Pobierz wersjÄ™ Cordova dziaÅ‚a na urzÄ…dzeniu. + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* PrzeglÄ…darka +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 + +## device.model + +`device.model`Zwraca nazwÄ™ modelu lub produktu. Wartość jest zestaw przez producenta urzÄ…dzenia i mogÄ… siÄ™ różnić miÄ™dzy wersjami tego samego produktu. + +### ObsÅ‚ugiwane platformy + +* Android +* BlackBerry 10 +* PrzeglÄ…darka +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 + +### Szybki przykÅ‚ad + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. Zobacz http://theiphonewiki.com/wiki/index.php?title=Models / / modelu var = device.model; + + +### Dziwactwa Androida + +* Pobiera [nazwÄ™ produktu][1] zamiast [nazwy modelu][2], który czÄ™sto jest nazwÄ… kod produkcji. Na przykÅ‚ad, Nexus One zwraca `Passion` , i zwraca Motorola Droid`voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### Dziwactwa Tizen + +* Zwraca modelu urzÄ…dzenia przypisane przez dostawcÄ™, na przykÅ‚ad,`TIZEN` + +### Windows Phone 7 i 8 dziwactwa + +* Zwraca modelu urzÄ…dzenia, okreÅ›lonej przez producenta. Na przykÅ‚ad Samsung ostroÅ›ci zwraca`SGH-i917`. + +## device.platform + +Uzyskać nazwÄ™ systemu operacyjnego urzÄ…dzenia. + + var string = device.platform; + + +### ObsÅ‚ugiwane platformy + +* Android +* BlackBerry 10 +* Browser4 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 + +### Szybki przykÅ‚ad + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Dziwactwa Windows Phone 7 + +UrzÄ…dzenia Windows Phone 7 raport platformy jako`WinCE`. + +### Windows Phone 8 dziwactwa + +UrzÄ…dzenia Windows Phone 8 raport platformy jako`Win32NT`. + +## device.uuid + +SiÄ™ urzÄ…dzenia uniwersalnie unikatowy identyfikator ([UUID][3]). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### Opis + +Szczegóły jak UUID jest generowane sÄ… okreÅ›lane przez producenta urzÄ…dzenia i sÄ… specyficzne dla platformy lub modelu urzÄ…dzenia. + +### ObsÅ‚ugiwane platformy + +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 + +### Szybki przykÅ‚ad + + / / Android: zwraca losowe 64-bitowa liczba caÅ‚kowita (jako ciÄ…g, znowu!) / / liczba caÅ‚kowita jest generowany na pierwszego uruchomienia urzÄ…dzenia / / / / BlackBerry: zwraca numer PIN urzÄ…dzenia / / to jest unikatowÄ… liczbÄ… caÅ‚kowitÄ… dziewiÄ™ciu cyfr (jako ciÄ…g, choć!) / / / / iPhone: (zacytowana w dokumentacji klasy UIDevice) / / zwraca ciÄ…g wartoÅ›ci mieszania utworzone z wielu sprzÄ™tu identyfikuje. + Zapewniona jest unikatowy dla każdego urzÄ…dzenia i nie może być zwiÄ…zane z / do konta użytkownika. + / / Windows Phone 7: zwraca wartość mieszania urzÄ…dzenia + bieżący użytkownik, / / jeÅ›li nie zdefiniowane przez użytkownika, identyfikator guid jest generowany i bÄ™dzie trwać do czasu odinstalowania aplikacji / / Tizen: zwraca urzÄ…dzenia IMEI (International Mobile Equipment Identity lub IMEI jest liczbÄ… / / unikatowe dla każdego telefonu komórkowego GSM i UMTS. + var deviceID = device.uuid; + + +### iOS dziwactwo + +`uuid`Na iOS nie jest przypisany do urzÄ…dzenia, ale różni siÄ™ dla każdej aplikacji, dla każdej instalacji. Zmienia siÄ™ jeÅ›li możesz usunąć i ponownie zainstalować aplikacjÄ™, a ewentualnie także po aktualizacji iOS czy nawet uaktualnienia aplikacji dla wersji (widoczny w iOS 5.1). `uuid`Jest nie wiarygodne wartoÅ›ci. + +### Windows Phone 7 i 8 dziwactwa + +`uuid`Dla Windows Phone 7 wymaga uprawnieÅ„ `ID_CAP_IDENTITY_DEVICE` . Microsoft bÄ™dzie prawdopodobnie potÄ™piać ten wkrótce. JeÅ›li funkcja nie jest dostÄ™pna, aplikacja generuje trwaÅ‚e identyfikator guid, który jest utrzymywany przez czas trwania instalacji aplikacji na urzÄ…dzeniu. + +## device.version + +Pobierz wersjÄ™ systemu operacyjnego. + + var string = device.version; + + +### ObsÅ‚ugiwane platformy + +* Android 2.1 + +* BlackBerry 10 +* PrzeglÄ…darka +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 + +### Szybki przykÅ‚ad + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/ru/index.md b/plugins/cordova-plugin-device/doc/ru/index.md new file mode 100644 index 0000000..263b1cd --- /dev/null +++ b/plugins/cordova-plugin-device/doc/ru/index.md @@ -0,0 +1,219 @@ + + +# cordova-plugin-device + +Этот плагин определÑет глобальный объект `device`, который опиÑывает оборудование и программное обеÑпечение уÑтройÑтва. ÐеÑÐ¼Ð¾Ñ‚Ñ€Ñ Ð½Ð° то что объект в глобальной облаÑти видимоÑти, он не доÑтупен до того момента пока не произойдет Ñобытие `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## УÑтановка + + cordova plugin add cordova-plugin-device + + +## Параметры + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +Возвращает верÑию Cordova, работающую на уÑтройÑтве. + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Обозреватель +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +## device.model + +СвойÑтво `device.model` возвращает Ð¸Ð¼Ñ ÑƒÑтройÑтва модели или продукта. Значение уÑтанавливаетÑÑ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÐµÐ¼ уÑтройÑтва и могут отличатьÑÑ Ð² разных верÑиÑÑ… одного и того же продукта. + +### Поддерживаемые платформы + +* Android +* BlackBerry 10 +* Обозреватель +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### Краткий пример + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. See http://theiphonewiki.com/wiki/index.php?title=Models + // + var model = device.model; + + +### ОÑобенноÑти Android + +* Возвращает [Ð¸Ð¼Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð°][1] , а не [Ð¸Ð¼Ñ Ð¼Ð¾Ð´ÐµÐ»Ð¸][2], которое чаÑто ÑвлÑетÑÑ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ñтвенным кодом. Ðапример, Nexus One из них возвращает `Passion` , и Motorola Droid возвращает `voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### ОÑобенноÑти Tizen + +* Возвращает модель уÑтройÑтва, назначенного вендором, например,`TIZEN` + +### ОÑобенноÑти Windows Phone 7 и 8 + +* Возвращает модель уÑтройÑтва, указанной заводом-изготовителем. Ðапример Samsung Focus возвращает `SGH-i917`. + +## device.platform + +Получите Ð¸Ð¼Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ ÑиÑтемы уÑтройÑтва. + + var string = device.platform; + + +### Поддерживаемые платформы + +* Android +* BlackBerry 10 +* Браузером4 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### Краткий пример + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### ОÑобенноÑти Windows Phone 7 + +Windows Phone 7 уÑтройÑтва Ñообщают платформу как `WinCE`. + +### ОÑобенноÑти Windows Phone 8 + +УÑтройÑтва Windows Phone 8 Ñообщают платформу как `Win32NT`. + +## device.uuid + +Возвращает универÑальный уникального идентификатора ([UUID][3] уÑтройÑтва). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### ОпиÑание + +ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ том как UUID генерируетÑÑ, определÑÑŽÑ‚ÑÑ Ð¸Ð·Ð³Ð¾Ñ‚Ð¾Ð²Ð¸Ñ‚ÐµÐ»ÐµÐ¼ уÑтройÑтва и ÑвлÑÑŽÑ‚ÑÑ ÑпецифичеÑкими Ð´Ð»Ñ Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ñ‹ или модели уÑтройÑтва. + +### Поддерживаемые платформы + +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### Краткий пример + + // Android: Возвращает Ñлучайное 64-разрÑдное целое чиÑло (в виде Ñтроки, опÑть!) + // целое чиÑло генерируетÑÑ Ð¿Ñ€Ð¸ первой загрузке уÑтройÑтва + // + // BlackBerry: Возвращает номер PIN уÑтройÑтва + // Ñто 9 значный уникальный целочиÑленный (как Ñтрока, хотÑ!) + // + // iPhone: (Перефразировано из документации клаÑÑа UIDevice) + // возвращает Ñтроку Ñ…Ñш-значениÑ, Ñозданные из неÑкольких аппаратных определÑет. + // Это значение гарантированно ÑвлÑетÑÑ ÑƒÐ½Ð¸ÐºÐ°Ð»ÑŒÐ½Ñ‹Ð¼ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ уÑтройÑтва и не может быть привÑзано + // к учетной запиÑи пользователÑ. + // Windows Phone 7: Возвращает Ñ…Ñш уÑтройÑтво + текущего пользователÑ, + // еÑли пользователь не определен, формируетÑÑ guid который и будет ÑохранÑтьÑÑ Ð´Ð¾ тех пор, пока приложение не удалитьÑÑ + // Tizen: возвращает IMEI уÑтройÑтва (Международный идентификатор мобильного Ð¾Ð±Ð¾Ñ€ÑƒÐ´Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ IMEI Ñто чиÑло + // уникальное Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ мобильного телефона GSM и UMTS. + var deviceID = device.uuid; + + +### ОÑобенноÑти iOS + +Ðа iOS `uuid` не ÑвлÑетÑÑ ÑƒÐ½Ð¸ÐºÐ°Ð»ÑŒÐ½Ñ‹Ð¼ Ð´Ð»Ñ ÑƒÑтройÑтва, но варьируетÑÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ приложениÑ, и Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ уÑтановки. Значение менÑетÑÑ, еÑли удалить и повторно уÑтановить приложение, и возможно также когда вы обновите iOS, или даже обновить приложение до Ñледующей верÑии (очевидно в iOS 5.1). Значение `uuid` не ÑвлÑетÑÑ Ð½Ð°Ð´ÐµÐ¶Ð½Ñ‹Ð¼. + +### ОÑобенноÑти Windows Phone 7 и 8 + +Ð”Ð»Ñ Windows Phone 7 `uuid` требует Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ `ID_CAP_IDENTITY_DEVICE` . Microsoft Ñкорее вÑего Ñкоро Ñделает Ñто ÑвойÑтво уÑтаревшим. ЕÑли возможноÑть недоÑтупна, приложение Ñоздает поÑтоÑнные guid, который ÑохранÑетÑÑ Ð½Ð° вÑе Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° уÑтройÑтве. + +## device.version + +Возвращает верÑию операционной ÑиÑтемы. + + var string = device.version; + + +### Поддерживаемые платформы + +* Android 2.1 + +* BlackBerry 10 +* Обозреватель +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### Краткий пример + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/doc/zh/README.md b/plugins/cordova-plugin-device/doc/zh/README.md new file mode 100644 index 0000000..9a18a55 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/zh/README.md @@ -0,0 +1,203 @@ + + +# cordova-plugin-device + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-device.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-device) + +這個外掛程å¼å®šç¾©å…¨çƒ `device` 物件,æè¿°è©²è¨­å‚™çš„硬體和軟體。 雖然物件是在全çƒç¯„åœå…§ï¼Œä½†ä¸æ˜¯å¯ç”¨ï¼Œç›´åˆ°å¾Œ `deviceready` 事件。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-device + + +## 屬性 + + * device.cordova + * device.model + * device.platform + * device.uuid + * device.version + +## device.cordova + +ç²å–科爾多瓦在設備上é‹è¡Œçš„版本。 + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç€è¦½å™¨ + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + +## device.model + +`device.model`返回設備的模型或產å“çš„å稱。值由設備製造商設置和åŒä¸€ç”¢å“çš„ä¸åŒç‰ˆæœ¬å¯èƒ½ä¸åŒã€‚ + +### 支æ´çš„平臺 + + * Android 系統 + * 黑莓 10 + * ç€è¦½å™¨ + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + +### 快速的示例 + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. è«‹åƒé–± HTTP://theiphonewiki.com/wiki/index.php?title=Models / / var 模型 = device.model ï¼› + + +### Android 的怪癖 + + * ç²å–[產å“å稱](http://developer.android.com/reference/android/os/Build.html#PRODUCT)è€Œä¸æ˜¯[產å“型號å稱](http://developer.android.com/reference/android/os/Build.html#MODEL),這往往是生產代碼å稱。 例如,Nexus One 返回 `Passion` ,和摩托羅拉 Droid 返回`voles`. + +### Tizen 怪癖 + + * 例如,返回與供應商指派的設備模型`TIZEN` + +### Windows Phone 7 å’Œ 8 怪癖 + + * 返回由製造商指定的設備模型。例如,三星焦點返回`SGH-i917`. + +## device.platform + +ç²å–設備的作業系統å稱。 + + var string = device.platform; + + +### 支æ´çš„平臺 + + * Android 系統 + * 黑莓 10 + * Browser4 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + +### 快速的示例 + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 的怪癖 + +Windows Phone 7 設備報告作為平臺`WinCE`. + +### Windows Phone 8 怪癖 + +Windows Phone 8 設備報告作為平臺`Win32NT`. + +## device.uuid + +ç²å–設備的通用唯一識別碼 ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + + var string = device.uuid; + + +### 說明 + +如何生æˆä¸€å€‹ UUID 的細節由設備製造商和特定于設備的平臺或模型。 + +### 支æ´çš„平臺 + + * Android 系統 + * 黑莓 10 + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + +### 快速的示例 + + / / Android: 一個隨機的 64 使•´æ•¸ ï¼ˆä½œç‚ºå­—ä¸²è¿”å›žï¼Œå†æ¬¡!) / / 上設備的第一次啟動生æˆçš„æ•´æ•¸ / / / / 黑莓手機: 返回設備的 PIN 號碼 / / 這是ä¹å€‹æ•¸å­—的唯一整數 (作為字串,雖然!) / / / / iPhone: (從 UIDevice 類文檔解釋) / / 返回一個字串的雜湊值創建的多個硬體標識。 + / / 它ä¿è­‰æ˜¯å”¯ä¸€çš„æ¯å€‹è¨­å‚™ä¸¦ä¸èƒ½ç¶ / / 到使用者帳戶。 + / / Windows Phone 7: 返回的雜湊代碼的設備 + ç•¶å‰ä½¿ç”¨è€…,/ / 如果未定義使用者,則一個 guid 生æˆçš„並且將會ä¿ç•™ç›´åˆ°å¸è¼‰è©²æ‡‰ç”¨ç¨‹å¼ / / Tizen: 返回設備 IMEI (國際行動è£ç½®èº«ä»½æˆ– IMEI æ˜¯ä¸€å€‹æ•¸ä½ / / ç¨æœ‰çš„æ¯ä¸€å€‹ UMTS å’Œ GSM 行動電話。 + var deviceID = device.uuid; + + +### iOS 怪癖 + +`uuid`在 iOS 䏿˜¯å”¯ä¸€çš„一種è£ç½®ï¼Œä½†å°æ–¼æ¯å€‹æ‡‰ç”¨ç¨‹å¼ï¼Œç‚ºæ¯å€‹å®‰è£è€Œç•°ã€‚ å¦‚æžœæ‚¨åˆªé™¤ä¸¦é‡æ–°å®‰è£è©²æ‡‰ç”¨ç¨‹å¼ï¼Œå®ƒæ›´æ”¹å’Œå¯èƒ½é‚„ç•¶ä½ å‡ç´š iOS,或甚至å‡ç´šæ¯å€‹ç‰ˆæœ¬ (iOS 5.1 中存在明顯的) 的應用程å¼ã€‚ `uuid`䏿˜¯ä¸€å€‹å¯é çš„值。 + +### Windows Phone 7 å’Œ 8 怪癖 + +`uuid`為 Windows Phone 7 é ˆç¶“è¨±å¯ `ID_CAP_IDENTITY_DEVICE` 。 Microsoft å¯èƒ½æœƒå¾ˆå¿«æ£„用此屬性。 如果沒有å¯ç”¨çš„能力,應用程å¼å°‡ç”Ÿæˆè¨­å‚™ä¸Šæ‡‰ç”¨ç¨‹å¼çš„安è£éŽç¨‹ä¸­ä¿æŒæŒçºŒçš„ guid。 + +## device.version + +ç²å–作業系統版本。 + + var string = device.version; + + +### 支æ´çš„平臺 + + * Android 2.1 + + * 黑莓 10 + * ç€è¦½å™¨ + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + +### 快速的示例 + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; \ No newline at end of file diff --git a/plugins/cordova-plugin-device/doc/zh/index.md b/plugins/cordova-plugin-device/doc/zh/index.md new file mode 100644 index 0000000..5626d69 --- /dev/null +++ b/plugins/cordova-plugin-device/doc/zh/index.md @@ -0,0 +1,206 @@ + + +# cordova-plugin-device + +這個外掛程å¼å®šç¾©å…¨çƒ `device` 物件,æè¿°è©²è¨­å‚™çš„硬體和軟體。 雖然物件是在全çƒç¯„åœå…§ï¼Œä½†ä¸æ˜¯å¯ç”¨ï¼Œç›´åˆ°å¾Œ `deviceready` 事件。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(device.cordova); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-device + + +## 屬性 + +* device.cordova +* device.model +* device.platform +* device.uuid +* device.version + +## device.cordova + +ç²å–科爾多瓦在設備上é‹è¡Œçš„版本。 + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç€è¦½å™¨ +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* æ³° +* Windows Phone 7 å’Œ 8 +* Windows 8 + +## device.model + +`device.model`返回設備的模型或產å“çš„å稱。值由設備製造商設置和åŒä¸€ç”¢å“çš„ä¸åŒç‰ˆæœ¬å¯èƒ½ä¸åŒã€‚ + +### 支æ´çš„平臺 + +* Android 系統 +* 黑莓 10 +* ç€è¦½å™¨ +* iOS +* æ³° +* Windows Phone 7 å’Œ 8 +* Windows 8 + +### 快速的示例 + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // Browser: Google Chrome returns "Chrome" + // Safari returns "Safari" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. è«‹åƒé–± HTTP://theiphonewiki.com/wiki/index.php?title=Models / / var 模型 = device.model ï¼› + + +### Android 的怪癖 + +* ç²å–[產å“å稱][1]è€Œä¸æ˜¯[產å“型號å稱][2],這往往是生產代碼å稱。 例如,Nexus One 返回 `Passion` ,和摩托羅拉 Droid 返回`voles`. + + [1]: http://developer.android.com/reference/android/os/Build.html#PRODUCT + [2]: http://developer.android.com/reference/android/os/Build.html#MODEL + +### Tizen 怪癖 + +* 例如,返回與供應商指派的設備模型`TIZEN` + +### Windows Phone 7 å’Œ 8 怪癖 + +* 返回由製造商指定的設備模型。例如,三星焦點返回`SGH-i917`. + +## device.platform + +ç²å–設備的作業系統å稱。 + + var string = device.platform; + + +### 支æ´çš„平臺 + +* Android 系統 +* 黑莓 10 +* Browser4 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* æ³° +* Windows Phone 7 å’Œ 8 +* Windows 8 + +### 快速的示例 + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry 10" + // - Browser: returns "MacIntel" on Mac + // returns "Win32" on Windows + // - "iOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + + +### Windows Phone 7 的怪癖 + +Windows Phone 7 設備報告作為平臺`WinCE`. + +### Windows Phone 8 怪癖 + +Windows Phone 8 設備報告作為平臺`Win32NT`. + +## device.uuid + +ç²å–設備的通用唯一識別碼 ([UUID][3]). + + [3]: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + var string = device.uuid; + + +### 說明 + +如何生æˆä¸€å€‹ UUID 的細節由設備製造商和特定于設備的平臺或模型。 + +### 支æ´çš„平臺 + +* Android 系統 +* 黑莓 10 +* iOS +* Tizen +* Windows Phone 7 å’Œ 8 +* Windows 8 + +### 快速的示例 + + / / Android: 一個隨機的 64 使•´æ•¸ ï¼ˆä½œç‚ºå­—ä¸²è¿”å›žï¼Œå†æ¬¡!) / / 上設備的第一次啟動生æˆçš„æ•´æ•¸ / / / / 黑莓手機: 返回設備的 PIN 號碼 / / 這是ä¹å€‹æ•¸å­—的唯一整數 (作為字串,雖然!) / / / / iPhone: (從 UIDevice 類文檔解釋) / / 返回一個字串的雜湊值創建的多個硬體標識。 + / / 它ä¿è­‰æ˜¯å”¯ä¸€çš„æ¯å€‹è¨­å‚™ä¸¦ä¸èƒ½ç¶ / / 到使用者帳戶。 + / / Windows Phone 7: 返回的雜湊代碼的設備 + ç•¶å‰ä½¿ç”¨è€…,/ / 如果未定義使用者,則一個 guid 生æˆçš„並且將會ä¿ç•™ç›´åˆ°å¸è¼‰è©²æ‡‰ç”¨ç¨‹å¼ / / Tizen: 返回設備 IMEI (國際行動è£ç½®èº«ä»½æˆ– IMEI æ˜¯ä¸€å€‹æ•¸ä½ / / ç¨æœ‰çš„æ¯ä¸€å€‹ UMTS å’Œ GSM 行動電話。 + var deviceID = device.uuid; + + +### iOS 怪癖 + +`uuid`在 iOS 䏿˜¯å”¯ä¸€çš„一種è£ç½®ï¼Œä½†å°æ–¼æ¯å€‹æ‡‰ç”¨ç¨‹å¼ï¼Œç‚ºæ¯å€‹å®‰è£è€Œç•°ã€‚ å¦‚æžœæ‚¨åˆªé™¤ä¸¦é‡æ–°å®‰è£è©²æ‡‰ç”¨ç¨‹å¼ï¼Œå®ƒæ›´æ”¹å’Œå¯èƒ½é‚„ç•¶ä½ å‡ç´š iOS,或甚至å‡ç´šæ¯å€‹ç‰ˆæœ¬ (iOS 5.1 中存在明顯的) 的應用程å¼ã€‚ `uuid`䏿˜¯ä¸€å€‹å¯é çš„值。 + +### Windows Phone 7 å’Œ 8 怪癖 + +`uuid`為 Windows Phone 7 é ˆç¶“è¨±å¯ `ID_CAP_IDENTITY_DEVICE` 。 Microsoft å¯èƒ½æœƒå¾ˆå¿«æ£„用此屬性。 如果沒有å¯ç”¨çš„能力,應用程å¼å°‡ç”Ÿæˆè¨­å‚™ä¸Šæ‡‰ç”¨ç¨‹å¼çš„安è£éŽç¨‹ä¸­ä¿æŒæŒçºŒçš„ guid。 + +## device.version + +ç²å–作業系統版本。 + + var string = device.version; + + +### 支æ´çš„平臺 + +* Android 2.1 + +* 黑莓 10 +* ç€è¦½å™¨ +* iOS +* æ³° +* Windows Phone 7 å’Œ 8 +* Windows 8 + +### 快速的示例 + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // Browser: Returns version number for the browser + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; diff --git a/plugins/cordova-plugin-device/package.json b/plugins/cordova-plugin-device/package.json new file mode 100644 index 0000000..12c2d1e --- /dev/null +++ b/plugins/cordova-plugin-device/package.json @@ -0,0 +1,61 @@ +{ + "name": "cordova-plugin-device", + "version": "1.1.4", + "description": "Cordova Device Plugin", + "cordova": { + "id": "cordova-plugin-device", + "platforms": [ + "firefoxos", + "tizen", + "android", + "amazon-fireos", + "ubuntu", + "ios", + "blackberry10", + "wp7", + "wp8", + "windows8", + "windows", + "browser", + "osx" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-device" + }, + "keywords": [ + "cordova", + "device", + "ecosystem:cordova", + "cordova-firefoxos", + "cordova-tizen", + "cordova-android", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-ios", + "cordova-blackberry10", + "cordova-wp7", + "cordova-wp8", + "cordova-windows8", + "cordova-windows", + "cordova-browser", + "cordova-osx" + ], + "scripts": { + "test": "npm run jshint", + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "engines": { + "cordovaDependencies": { + "2.0.0": { + "cordova": ">100" + } + } + }, + "devDependencies": { + "jshint": "^2.6.0" + } +} diff --git a/plugins/cordova-plugin-device/plugin.xml b/plugins/cordova-plugin-device/plugin.xml new file mode 100644 index 0000000..ae956f8 --- /dev/null +++ b/plugins/cordova-plugin-device/plugin.xml @@ -0,0 +1,174 @@ + + + + + Device + Cordova Device Plugin + Apache 2.0 + cordova,device + https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git + https://issues.apache.org/jira/browse/CB/component/12320648 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + read_device_identifying_information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-device/src/android/Device.java b/plugins/cordova-plugin-device/src/android/Device.java new file mode 100644 index 0000000..e9efcb4 --- /dev/null +++ b/plugins/cordova-plugin-device/src/android/Device.java @@ -0,0 +1,174 @@ +/* + 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.device; + +import java.util.TimeZone; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaInterface; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.provider.Settings; + +public class Device extends CordovaPlugin { + public static final String TAG = "Device"; + + public static String platform; // Device OS + public static String uuid; // Device UUID + + private static final String ANDROID_PLATFORM = "Android"; + private static final String AMAZON_PLATFORM = "amazon-fireos"; + private static final String AMAZON_DEVICE = "Amazon"; + + /** + * Constructor. + */ + public Device() { + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + Device.uuid = getUuid(); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return True if the action was valid, false if not. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if ("getDeviceInfo".equals(action)) { + JSONObject r = new JSONObject(); + r.put("uuid", Device.uuid); + r.put("version", this.getOSVersion()); + r.put("platform", this.getPlatform()); + r.put("model", this.getModel()); + r.put("manufacturer", this.getManufacturer()); + r.put("isVirtual", this.isVirtual()); + r.put("serial", this.getSerialNumber()); + callbackContext.success(r); + } + else { + return false; + } + return true; + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Get the OS name. + * + * @return + */ + public String getPlatform() { + String platform; + if (isAmazonDevice()) { + platform = AMAZON_PLATFORM; + } else { + platform = ANDROID_PLATFORM; + } + return platform; + } + + /** + * Get the device's Universally Unique Identifier (UUID). + * + * @return + */ + public String getUuid() { + String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); + return uuid; + } + + public String getModel() { + String model = android.os.Build.MODEL; + return model; + } + + public String getProductName() { + String productname = android.os.Build.PRODUCT; + return productname; + } + + public String getManufacturer() { + String manufacturer = android.os.Build.MANUFACTURER; + return manufacturer; + } + + public String getSerialNumber() { + String serial = android.os.Build.SERIAL; + return serial; + } + + /** + * Get the OS version. + * + * @return + */ + public String getOSVersion() { + String osversion = android.os.Build.VERSION.RELEASE; + return osversion; + } + + public String getSDKVersion() { + @SuppressWarnings("deprecation") + String sdkversion = android.os.Build.VERSION.SDK; + return sdkversion; + } + + public String getTimeZoneID() { + TimeZone tz = TimeZone.getDefault(); + return (tz.getID()); + } + + /** + * Function to check if the device is manufactured by Amazon + * + * @return + */ + public boolean isAmazonDevice() { + if (android.os.Build.MANUFACTURER.equals(AMAZON_DEVICE)) { + return true; + } + return false; + } + + public boolean isVirtual() { + return android.os.Build.FINGERPRINT.contains("generic") || + android.os.Build.PRODUCT.contains("sdk"); + } + +} diff --git a/plugins/cordova-plugin-device/src/blackberry10/index.js b/plugins/cordova-plugin-device/src/blackberry10/index.js new file mode 100644 index 0000000..ef0543b --- /dev/null +++ b/plugins/cordova-plugin-device/src/blackberry10/index.js @@ -0,0 +1,71 @@ +/* + * + * 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 PluginResult */ + +function getModelName () { + var modelName = window.qnx.webplatform.device.modelName; + //Pre 10.2 (meaning Z10 or Q10) + if (typeof modelName === "undefined") { + if (window.screen.height === 720 && window.screen.width === 720) { + if ( window.matchMedia("(-blackberry-display-technology: -blackberry-display-oled)").matches) { + modelName = "Q10"; + } else { + modelName = "Q5"; + } + } else if ((window.screen.height === 1280 && window.screen.width === 768) || + (window.screen.height === 768 && window.screen.width === 1280)) { + modelName = "Z10"; + } else { + modelName = window.qnx.webplatform.deviceName; + } + } + + return modelName; +} + +function getUUID () { + var uuid = ""; + try { + //Must surround by try catch because this will throw if the app is missing permissions + uuid = window.qnx.webplatform.device.devicePin; + } catch (e) { + //DO Nothing + } + return uuid; +} + +module.exports = { + getDeviceInfo: function (success, fail, args, env) { + var result = new PluginResult(args, env), + modelName = getModelName(), + uuid = getUUID(), + info = { + manufacturer: 'BlackBerry', + platform: "blackberry10", + version: window.qnx.webplatform.device.scmBundle, + model: modelName, + uuid: uuid + }; + + result.ok(info); + } +}; diff --git a/plugins/cordova-plugin-device/src/browser/DeviceProxy.js b/plugins/cordova-plugin-device/src/browser/DeviceProxy.js new file mode 100644 index 0000000..a863201 --- /dev/null +++ b/plugins/cordova-plugin-device/src/browser/DeviceProxy.js @@ -0,0 +1,82 @@ +/* + * + * 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 browser = require('cordova/platform'); + +function getPlatform() { + return "browser"; +} + +function getModel() { + return getBrowserInfo(true); +} + +function getVersion() { + return getBrowserInfo(false); +} + +function getBrowserInfo(getModel) { + var userAgent = navigator.userAgent; + var returnVal = ''; + var offset; + + if ((offset = userAgent.indexOf('Chrome')) !== -1) { + returnVal = (getModel) ? 'Chrome' : userAgent.substring(offset + 7); + } else if ((offset = userAgent.indexOf('Safari')) !== -1) { + if (getModel) { + returnVal = 'Safari'; + } else { + returnVal = userAgent.substring(offset + 7); + + if ((offset = userAgent.indexOf('Version')) !== -1) { + returnVal = userAgent.substring(offset + 8); + } + } + } else if ((offset = userAgent.indexOf('Firefox')) !== -1) { + returnVal = (getModel) ? 'Firefox' : userAgent.substring(offset + 8); + } else if ((offset = userAgent.indexOf('MSIE')) !== -1) { + returnVal = (getModel) ? 'MSIE' : userAgent.substring(offset + 5); + } else if ((offset = userAgent.indexOf('Trident')) !== -1) { + returnVal = (getModel) ? 'MSIE' : '11'; + } + + if ((offset = returnVal.indexOf(';')) !== -1 || (offset = returnVal.indexOf(' ')) !== -1) { + returnVal = returnVal.substring(0, offset); + } + + return returnVal; +} + + +module.exports = { + getDeviceInfo: function (success, error) { + setTimeout(function () { + success({ + cordova: browser.cordovaVersion, + platform: getPlatform(), + model: getModel(), + version: getVersion(), + uuid: null + }); + }, 0); + } +}; + +require("cordova/exec/proxy").add("Device", module.exports); diff --git a/plugins/cordova-plugin-device/src/firefoxos/DeviceProxy.js b/plugins/cordova-plugin-device/src/firefoxos/DeviceProxy.js new file mode 100644 index 0000000..a05d7ca --- /dev/null +++ b/plugins/cordova-plugin-device/src/firefoxos/DeviceProxy.js @@ -0,0 +1,77 @@ +/* + * + * 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. + * + */ +//example UA String for Firefox OS +//Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0 + +//UA parsing not recommended but currently this is the only way to get the Firefox OS version +//https://developer.mozilla.org/en-US/docs/Gecko_user_agent_string_reference + +//Should be replaced when better conversion to Firefox OS Version is available +function convertVersionNumber(ver) { + var hashVersion = { + '18.0': '1.0.1', + '18.1': '1.1', + '26.0': '1.2', + '28.0': '1.3', + '30.0': '1.4', + '32.0': '2.0' + }; + var rver = ver; + var sStr = ver.substring(0, 4); + if (hashVersion[sStr]) { + rver = hashVersion[sStr]; + } + return (rver); + +} +function getVersion() { + if (navigator.userAgent.match(/(mobile|tablet)/i)) { + var ffVersionArray = (navigator.userAgent.match(/Firefox\/([\d]+\.[\w]?\.?[\w]+)/)); + if (ffVersionArray.length === 2) { + return (convertVersionNumber(ffVersionArray[1])); + } + } + return (null); +} + +function getModel() { + var uaArray = navigator.userAgent.split(/\s*[;)(]\s*/); + if (navigator.userAgent.match(/(mobile|tablet)/i)) { + if (uaArray.length === 5) { + return (uaArray[2]); + } + } + return (null); +} +module.exports = { + getDeviceInfo: function (success, error) { + setTimeout(function () { + success({ + platform: 'firefoxos', + model: getModel(), + version: getVersion(), + uuid: null + }); + }, 0); + } +}; + +require("cordova/exec/proxy").add("Device", module.exports); diff --git a/plugins/cordova-plugin-device/src/ios/CDVDevice.h b/plugins/cordova-plugin-device/src/ios/CDVDevice.h new file mode 100644 index 0000000..a146d88 --- /dev/null +++ b/plugins/cordova-plugin-device/src/ios/CDVDevice.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 +#import + +@interface CDVDevice : CDVPlugin +{} + ++ (NSString*)cordovaVersion; + +- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/cordova-plugin-device/src/ios/CDVDevice.m b/plugins/cordova-plugin-device/src/ios/CDVDevice.m new file mode 100644 index 0000000..4d75a57 --- /dev/null +++ b/plugins/cordova-plugin-device/src/ios/CDVDevice.m @@ -0,0 +1,112 @@ +/* + 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. + */ + +#include +#include +#include "TargetConditionals.h" + +#import +#import "CDVDevice.h" + +@implementation UIDevice (ModelVersion) + +- (NSString*)modelVersion +{ + size_t size; + + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char* machine = malloc(size); + sysctlbyname("hw.machine", machine, &size, NULL, 0); + NSString* platform = [NSString stringWithUTF8String:machine]; + free(machine); + + return platform; +} + +@end + +@interface CDVDevice () {} +@end + +@implementation CDVDevice + +- (NSString*)uniqueAppInstanceIdentifier:(UIDevice*)device +{ + NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; + static NSString* UUID_KEY = @"CDVUUID"; + + // Check user defaults first to maintain backwards compaitibility with previous versions + // which didn't user identifierForVendor + NSString* app_uuid = [userDefaults stringForKey:UUID_KEY]; + if (app_uuid == nil) { + if ([device respondsToSelector:@selector(identifierForVendor)]) { + app_uuid = [[device identifierForVendor] UUIDString]; + } else { + CFUUIDRef uuid = CFUUIDCreate(NULL); + app_uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + } + + [userDefaults setObject:app_uuid forKey:UUID_KEY]; + [userDefaults synchronize]; + } + + return app_uuid; +} + +- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command +{ + NSDictionary* deviceProperties = [self deviceProperties]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceProperties]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSDictionary*)deviceProperties +{ + UIDevice* device = [UIDevice currentDevice]; + + return @{ + @"manufacturer": @"Apple", + @"model": [device modelVersion], + @"platform": @"iOS", + @"version": [device systemVersion], + @"uuid": [self uniqueAppInstanceIdentifier:device], + @"cordova": [[self class] cordovaVersion], + @"isVirtual": @([self isVirtual]) + }; +} + ++ (NSString*)cordovaVersion +{ + return CDV_VERSION; +} + +- (BOOL)isVirtual +{ + #if TARGET_OS_SIMULATOR + return true; + #elif TARGET_IPHONE_SIMULATOR + return true; + #else + return false; + #endif +} + +@end diff --git a/plugins/cordova-plugin-device/src/osx/CDVDevice.h b/plugins/cordova-plugin-device/src/osx/CDVDevice.h new file mode 100644 index 0000000..9def254 --- /dev/null +++ b/plugins/cordova-plugin-device/src/osx/CDVDevice.h @@ -0,0 +1,28 @@ +/* + 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 + +@interface CDVDevice : CDVPlugin + ++ (NSString*) cordovaVersion; + +- (void) getDeviceInfo:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/cordova-plugin-device/src/osx/CDVDevice.m b/plugins/cordova-plugin-device/src/osx/CDVDevice.m new file mode 100644 index 0000000..3a63588 --- /dev/null +++ b/plugins/cordova-plugin-device/src/osx/CDVDevice.m @@ -0,0 +1,113 @@ +/* + 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. + */ + +#include + +#import "CDVDevice.h" + +#define SYSTEM_VERSION_PLIST @"/System/Library/CoreServices/SystemVersion.plist" + +@implementation CDVDevice + +- (NSString*) modelVersion { + size_t size; + + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char* machine = malloc(size); + sysctlbyname("hw.machine", machine, &size, NULL, 0); + NSString* modelVersion = [NSString stringWithUTF8String:machine]; + free(machine); + + return modelVersion; +} + + +- (NSString*) getSerialNr { + NSString* serialNr; + io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); + if (platformExpert) { + CFTypeRef serialNumberAsCFString = + IORegistryEntryCreateCFProperty(platformExpert, + CFSTR(kIOPlatformSerialNumberKey), + kCFAllocatorDefault, 0); + if (serialNumberAsCFString) { + serialNr = (__bridge NSString*) serialNumberAsCFString; + } + IOObjectRelease(platformExpert); + } + return serialNr; +} + +- (NSString*) uniqueAppInstanceIdentifier { + NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; + static NSString* UUID_KEY = @"CDVUUID"; + + NSString* app_uuid = [userDefaults stringForKey:UUID_KEY]; + + if (app_uuid == nil) { + CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef); + + app_uuid = [NSString stringWithString:(__bridge NSString*) uuidString]; + [userDefaults setObject:app_uuid forKey:UUID_KEY]; + [userDefaults synchronize]; + + CFRelease(uuidString); + CFRelease(uuidRef); + } + + return app_uuid; +} + +- (NSString*) platform { + return [NSDictionary dictionaryWithContentsOfFile:SYSTEM_VERSION_PLIST][@"ProductName"]; +} + +- (NSString*) systemVersion { + return [NSDictionary dictionaryWithContentsOfFile:SYSTEM_VERSION_PLIST][@"ProductVersion"]; +} + +- (void) getDeviceInfo:(CDVInvokedUrlCommand*) command { + NSDictionary* deviceProperties = [self deviceProperties]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceProperties]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSDictionary*) deviceProperties { + NSMutableDictionary* devProps = [NSMutableDictionary dictionaryWithCapacity:4]; + + devProps[@"manufacturer"] = @"Apple"; + devProps[@"model"] = [self modelVersion]; + devProps[@"platform"] = [self platform]; + devProps[@"version"] = [self systemVersion]; + devProps[@"uuid"] = [self uniqueAppInstanceIdentifier]; + devProps[@"cordova"] = [[self class] cordovaVersion]; + devProps[@"serial"] = [self getSerialNr]; + devProps[@"isVirtual"] = @NO; + + NSDictionary* devReturn = [NSDictionary dictionaryWithDictionary:devProps]; + return devReturn; +} + ++ (NSString*) cordovaVersion { + return CDV_VERSION; +} + +@end diff --git a/plugins/cordova-plugin-device/src/tizen/DeviceProxy.js b/plugins/cordova-plugin-device/src/tizen/DeviceProxy.js new file mode 100644 index 0000000..973b716 --- /dev/null +++ b/plugins/cordova-plugin-device/src/tizen/DeviceProxy.js @@ -0,0 +1,38 @@ +/* + * + * 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 tizen = require('cordova/platform'); + +module.exports = { + getDeviceInfo: function(success, error) { + setTimeout(function () { + success({ + cordova: tizen.cordovaVersion, + platform: 'tizen', + model: null, + version: null, + uuid: null + }); + }, 0); + } +}; + +require("cordova/tizen/commandProxy").add("Device", module.exports); diff --git a/plugins/cordova-plugin-device/src/ubuntu/device.cpp b/plugins/cordova-plugin-device/src/ubuntu/device.cpp new file mode 100644 index 0000000..eb5a012 --- /dev/null +++ b/plugins/cordova-plugin-device/src/ubuntu/device.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2011 Wolfgang Koller - http://www.gofg.at/ + * + * 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. + */ + +#include +#include + +#include"device.h" + +#define CORDOVA "3.0.0" + +Device::Device(Cordova *cordova) : CPlugin(cordova) { +} + +static QString getOSName() { +#ifdef Q_OS_SYMBIAN + QString platform = "Symbian"; +#endif +#ifdef Q_OS_WIN + QString platform = "Windows"; +#endif +#ifdef Q_OS_WINCE + QString platform = "Windows CE"; +#endif +#ifdef Q_OS_LINUX + QString platform = "Linux"; +#endif + return platform; +} + +void Device::getInfo(int scId, int ecId) { + Q_UNUSED(ecId) + + QDeviceInfo systemDeviceInfo; + QDeviceInfo systemInfo; + + QString platform = getOSName(); + + QString uuid = systemDeviceInfo.uniqueDeviceID(); + if (uuid.isEmpty()) { + QString deviceDescription = systemInfo.imei(0) + ";" + systemInfo.manufacturer() + ";" + systemInfo.model() + ";" + systemInfo.productName() + ";" + platform; + QString user = qgetenv("USER"); + if (user.isEmpty()) { + user = qgetenv("USERNAME"); + if (user.isEmpty()) + user = QDir::homePath(); + } + uuid = QString(QCryptographicHash::hash((deviceDescription + ";" + user).toUtf8(), QCryptographicHash::Md5).toHex()); + } + + this->cb(scId, systemDeviceInfo.model(), CORDOVA, platform, uuid, systemInfo.version(QDeviceInfo::Os)); +} diff --git a/plugins/cordova-plugin-device/src/ubuntu/device.h b/plugins/cordova-plugin-device/src/ubuntu/device.h new file mode 100644 index 0000000..91cb937 --- /dev/null +++ b/plugins/cordova-plugin-device/src/ubuntu/device.h @@ -0,0 +1,47 @@ +/* + * Copyright 2011 Wolfgang Koller - http://www.gofg.at/ + * + * 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. + */ + +#ifndef DEVICE_H_FDSAFAS +#define DEVICE_H_FDSAFAS + +#include + +#include + +class Device: public CPlugin { + Q_OBJECT +public: + explicit Device(Cordova *cordova); + + virtual const QString fullName() override { + return Device::fullID(); + } + + virtual const QString shortName() override { + return "Device"; + } + + static const QString fullID() { + return "com.cordova.Device"; + } + +signals: + +public slots: + void getInfo(int scId, int ecId); +}; + +#endif diff --git a/plugins/cordova-plugin-device/src/ubuntu/device.js b/plugins/cordova-plugin-device/src/ubuntu/device.js new file mode 100644 index 0000000..838586c --- /dev/null +++ b/plugins/cordova-plugin-device/src/ubuntu/device.js @@ -0,0 +1,33 @@ +/* + * + * 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 Cordova */ + +module.exports = { + getInfo:function(win,fail,args) { + Cordova.exec(function (model, cordova, platform, uuid, version) { + win({name: name, model: model, cordova: cordova, + platform: platform, uuid: uuid, version: version}); + }, null, "com.cordova.Device", "getInfo", []); + } +}; + +require("cordova/exec/proxy").add("Device", module.exports); diff --git a/plugins/cordova-plugin-device/src/windows/DeviceProxy.js b/plugins/cordova-plugin-device/src/windows/DeviceProxy.js new file mode 100644 index 0000000..f338ad3 --- /dev/null +++ b/plugins/cordova-plugin-device/src/windows/DeviceProxy.js @@ -0,0 +1,98 @@ +/* + * + * 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, createUUID */ + +var ROOT_CONTAINER = "{00000000-0000-0000-FFFF-FFFFFFFFFFFF}"; +var DEVICE_CLASS_KEY = "{A45C254E-DF1C-4EFD-8020-67D146A850E0},10"; +var DEVICE_CLASS_KEY_NO_SEMICOLON = "{A45C254E-DF1C-4EFD-8020-67D146A850E0}10"; +var ROOT_CONTAINER_QUERY = "System.Devices.ContainerId:=\"" + ROOT_CONTAINER + "\""; +var HAL_DEVICE_CLASS = "4d36e966-e325-11ce-bfc1-08002be10318"; +var DEVICE_DRIVER_VERSION_KEY = "{A8B865DD-2E3D-4094-AD97-E593A70C75D6},3"; + +module.exports = { + + getDeviceInfo:function(win, fail, args) { + + // deviceId aka uuid, stored in Windows.Storage.ApplicationData.current.localSettings.values.deviceId + var deviceId; + // get deviceId, or create and store one + var localSettings = Windows.Storage.ApplicationData.current.localSettings; + if (localSettings.values.deviceId) { + deviceId = localSettings.values.deviceId; + } + else { + // App-specific hardware id could be used as uuid, but it changes if the hardware changes... + try { + var ASHWID = Windows.System.Profile.HardwareIdentification.getPackageSpecificToken(null).id; + deviceId = Windows.Storage.Streams.DataReader.fromBuffer(ASHWID).readGuid(); + } catch (e) { + // Couldn't get the hardware UUID + deviceId = createUUID(); + } + //...so cache it per-install + localSettings.values.deviceId = deviceId; + } + + + var userAgent = window.clientInformation.userAgent; + // this will report "windows" in windows8.1 and windows phone 8.1 apps + // and "windows8" in windows 8.0 apps similar to cordova.js + // See https://github.com/apache/cordova-js/blob/master/src/windows/platform.js#L25 + var devicePlatform = userAgent.indexOf("MSAppHost/1.0") == -1 ? "windows" : "windows8"; + var versionString = userAgent.match(/Windows (?:Phone |NT )?([0-9.]+)/)[1]; + + var deviceInfo = new Windows.Security.ExchangeActiveSyncProvisioning.EasClientDeviceInformation(); + // Running in the Windows Simulator is a remote session. + // Running in the Windows Phone Emulator has the systemProductName set to "Virtual" + var isVirtual = Windows.System.RemoteDesktop.InteractiveSession.isRemote || deviceInfo.systemProductName == "Virtual"; + var manufacturer = deviceInfo.systemManufacturer; + var model = deviceInfo.systemProductName; + + var Pnp = Windows.Devices.Enumeration.Pnp; + + Pnp.PnpObject.findAllAsync(Pnp.PnpObjectType.device, + [DEVICE_DRIVER_VERSION_KEY, DEVICE_CLASS_KEY], + ROOT_CONTAINER_QUERY) + .then(function (rootDevices) { + for (var i = 0; i < rootDevices.length; i++) { + var rootDevice = rootDevices[i]; + if (!rootDevice.properties) continue; + if (rootDevice.properties[DEVICE_CLASS_KEY_NO_SEMICOLON] == HAL_DEVICE_CLASS) { + versionString = rootDevice.properties[DEVICE_DRIVER_VERSION_KEY]; + break; + } + } + + setTimeout(function () { + win({ platform: devicePlatform, + version: versionString, + uuid: deviceId, + isVirtual: isVirtual, + model: model, + manufacturer:manufacturer}); + }, 0); + }); + } + +}; // exports + +require("cordova/exec/proxy").add("Device", module.exports); diff --git a/plugins/cordova-plugin-device/src/wp/Device.cs b/plugins/cordova-plugin-device/src/wp/Device.cs new file mode 100644 index 0000000..76fe8cf --- /dev/null +++ b/plugins/cordova-plugin-device/src/wp/Device.cs @@ -0,0 +1,87 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using Microsoft.Phone.Info; +using System; +using System.IO; +using System.IO.IsolatedStorage; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class Device : BaseCommand + { + public void getDeviceInfo(string notused) + { + string res = String.Format("\"name\":\"{0}\",\"platform\":\"{1}\",\"uuid\":\"{2}\",\"version\":\"{3}\",\"model\":\"{4}\",\"manufacturer\":\"{5}\",\"isVirtual\":{6}", + DeviceStatus.DeviceName, + Environment.OSVersion.Platform.ToString(), + UUID, + Environment.OSVersion.Version.ToString(), + DeviceStatus.DeviceName, + DeviceStatus.DeviceManufacturer, + IsVirtual ? "true" : "false"); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, "{" + res + "}")); + } + + + public bool IsVirtual + { + get + { + return (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Emulator); + } + } + + public string UUID + { + get + { + object id; + + UserExtendedProperties.TryGetValue("ANID", out id); + if (id != null) + { + return id.ToString().Substring(2, 32); + } + + UserExtendedProperties.TryGetValue("ANID2", out id); + if (id != null) + { + return id.ToString(); + } + + string returnVal = "???unknown???"; + + using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication()) + { + try + { + IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage); + + using (StreamReader reader = new StreamReader(fileStream)) + { + returnVal = reader.ReadLine(); + } + } + catch (Exception /*ex*/) + { + + } + } + + return returnVal; + } + } + } +} diff --git a/plugins/cordova-plugin-device/tests/plugin.xml b/plugins/cordova-plugin-device/tests/plugin.xml new file mode 100644 index 0000000..4efeaf6 --- /dev/null +++ b/plugins/cordova-plugin-device/tests/plugin.xml @@ -0,0 +1,31 @@ + + + + + Cordova Device Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-device/tests/tests.js b/plugins/cordova-plugin-device/tests/tests.js new file mode 100644 index 0000000..ac9a281 --- /dev/null +++ b/plugins/cordova-plugin-device/tests/tests.js @@ -0,0 +1,113 @@ +/* + * + * 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. + * +*/ + +/* jshint jasmine: true */ + +exports.defineAutoTests = function() { + describe('Device Information (window.device)', function () { + it("should exist", function() { + expect(window.device).toBeDefined(); + }); + + it("should contain a platform specification that is a string", function() { + expect(window.device.platform).toBeDefined(); + expect((String(window.device.platform)).length > 0).toBe(true); + }); + + it("should contain a version specification that is a string", function() { + expect(window.device.version).toBeDefined(); + expect((String(window.device.version)).length > 0).toBe(true); + }); + + it("should contain a UUID specification that is a string or a number", function() { + expect(window.device.uuid).toBeDefined(); + if (typeof window.device.uuid == 'string' || typeof window.device.uuid == 'object') { + expect((String(window.device.uuid)).length > 0).toBe(true); + } else { + expect(window.device.uuid > 0).toBe(true); + } + }); + + it("should contain a cordova specification that is a string", function() { + expect(window.device.cordova).toBeDefined(); + expect((String(window.device.cordova)).length > 0).toBe(true); + }); + + it("should depend on the presence of cordova.version string", function() { + expect(window.cordova.version).toBeDefined(); + expect((String(window.cordova.version)).length > 0).toBe(true); + }); + + it("should contain device.cordova equal to cordova.version", function() { + expect(window.device.cordova).toBe(window.cordova.version); + }); + + it("should contain a model specification that is a string", function() { + expect(window.device.model).toBeDefined(); + expect((String(window.device.model)).length > 0).toBe(true); + }); + + it("should contain a manufacturer property that is a string", function() { + expect(window.device.manufacturer).toBeDefined(); + expect((String(window.device.manufacturer)).length > 0).toBe(true); + }); + + it("should contain an isVirtual property that is a boolean", function() { + expect(window.device.isVirtual).toBeDefined(); + expect(typeof window.device.isVirtual).toBe("boolean"); + }); + + it("should contain a serial number specification that is a string", function() { + expect(window.device.serial).toBeDefined(); + expect((String(window.device.serial)).length > 0).toBe(true); + + }); + + }); +}; + +exports.defineManualTests = function(contentEl, createActionButton) { + var logMessage = function (message, color) { + var log = document.getElementById('info'); + var logLine = document.createElement('div'); + if (color) { + logLine.style.color = color; + } + logLine.innerHTML = message; + log.appendChild(logLine); + }; + + var clearLog = function () { + var log = document.getElementById('info'); + log.innerHTML = ''; + }; + + var device_tests = '

Press Dump Device button to get device information

' + + '
' + + 'Expected result: Status box will get updated with device info. (i.e. platform, version, uuid, model, etc)'; + + contentEl.innerHTML = '
' + device_tests; + + createActionButton('Dump device', function() { + clearLog(); + logMessage(JSON.stringify(window.device, null, '\t')); + }, "dump_device"); +}; diff --git a/plugins/cordova-plugin-device/www/device.js b/plugins/cordova-plugin-device/www/device.js new file mode 100644 index 0000000..f7ed19f --- /dev/null +++ b/plugins/cordova-plugin-device/www/device.js @@ -0,0 +1,83 @@ +/* + * + * 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'), + channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + cordova = require('cordova'); + +channel.createSticky('onCordovaInfoReady'); +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.uuid = null; + this.cordova = null; + this.model = null; + this.manufacturer = null; + this.isVirtual = null; + this.serial = null; + + var me = this; + + channel.onCordovaReady.subscribe(function() { + me.getInfo(function(info) { + //ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js + //TODO: CB-5105 native implementations should not return info.cordova + var buildLabel = cordova.version; + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.uuid = info.uuid; + me.cordova = buildLabel; + me.model = info.model; + me.isVirtual = info.isVirtual; + me.manufacturer = info.manufacturer || 'unknown'; + me.serial = info.serial || 'unknown'; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + argscheck.checkArgs('fF', 'Device.getInfo', arguments); + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); diff --git a/plugins/cordova-plugin-dialogs/CONTRIBUTING.md b/plugins/cordova-plugin-dialogs/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-dialogs/LICENSE b/plugins/cordova-plugin-dialogs/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-dialogs/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/plugins/cordova-plugin-dialogs/NOTICE b/plugins/cordova-plugin-dialogs/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/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/plugins/cordova-plugin-dialogs/README.md b/plugins/cordova-plugin-dialogs/README.md new file mode 100644 index 0000000..5598ab3 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/README.md @@ -0,0 +1,276 @@ + + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +# cordova-plugin-dialogs + +This plugin provides access to some native dialog UI elements +via a global `navigator.notification` object. + +Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + +Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Dialogs%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + + cordova plugin add cordova-plugin-dialogs + +## Methods + +- `navigator.notification.alert` +- `navigator.notification.confirm` +- `navigator.notification.prompt` +- `navigator.notification.beep` + +## navigator.notification.alert + +Shows a custom alert or dialog box. Most Cordova implementations use a native +dialog box for this feature, but some platforms use the browser's `alert` +function, which is typically less customizable. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + +- __message__: Dialog message. _(String)_ + +- __alertCallback__: Callback to invoke when alert dialog is dismissed. _(Function)_ + +- __title__: Dialog title. _(String)_ (Optional, defaults to `Alert`) + +- __buttonName__: Button name. _(String)_ (Optional, defaults to `OK`) + + +### Example + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows 8 +- Windows + +### Windows Phone 7 and 8 Quirks + +- There is no built-in browser alert, but you can bind one as follows to call `alert()` in the global scope: + + window.alert = navigator.notification.alert; + +- Both `alert` and `confirm` are non-blocking calls, results of which are only available asynchronously. + +### Firefox OS Quirks: + +Both native-blocking `window.alert()` and non-blocking `navigator.notification.alert()` are available. + +### BlackBerry 10 Quirks +`navigator.notification.alert('text', callback, 'title', 'text')` callback parameter is passed the number 1. + +## navigator.notification.confirm + +Displays a customizable confirmation dialog box. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + +- __message__: Dialog message. _(String)_ + +- __confirmCallback__: Callback to invoke with index of button pressed (1, 2, or 3) or when the dialog is dismissed without a button press (0). _(Function)_ + +- __title__: Dialog title. _(String)_ (Optional, defaults to `Confirm`) + +- __buttonLabels__: Array of strings specifying button labels. _(Array)_ (Optional, defaults to [`OK,Cancel`]) + + +### confirmCallback + +The `confirmCallback` executes when the user presses one of the +buttons in the confirmation dialog box. + +The callback takes the argument `buttonIndex` _(Number)_, which is the +index of the pressed button. Note that the index uses one-based +indexing, so the value is `1`, `2`, `3`, etc. + +### Example + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows 8 +- Windows + +### Windows Phone 7 and 8 Quirks + +- There is no built-in browser function for `window.confirm`, but you can bind it by assigning: + + window.confirm = navigator.notification.confirm; + +- Calls to `alert` and `confirm` are non-blocking, so the result is only available asynchronously. + +### Windows Quirks + +- On Windows8/8.1 it is not possible to add more than three buttons to MessageDialog instance. + +- On Windows Phone 8.1 it's not possible to show dialog with more than two buttons. + +### Firefox OS Quirks: + +Both native-blocking `window.confirm()` and non-blocking `navigator.notification.confirm()` are available. + +## navigator.notification.prompt + +Displays a native dialog box that is more customizable than the browser's `prompt` function. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + +- __message__: Dialog message. _(String)_ + +- __promptCallback__: Callback to invoke with index of button pressed (1, 2, or 3) or when the dialog is dismissed without a button press (0). _(Function)_ + +- __title__: Dialog title _(String)_ (Optional, defaults to `Prompt`) + +- __buttonLabels__: Array of strings specifying button labels _(Array)_ (Optional, defaults to `["OK","Cancel"]`) + +- __defaultText__: Default textbox input value (`String`) (Optional, Default: empty string) + +### promptCallback + +The `promptCallback` executes when the user presses one of the buttons +in the prompt dialog box. The `results` object passed to the callback +contains the following properties: + +- __buttonIndex__: The index of the pressed button. _(Number)_ Note that the index uses one-based indexing, so the value is `1`, `2`, `3`, etc. + + + +- __input1__: The text entered in the prompt dialog box. _(String)_ + +### Example + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + +### Supported Platforms + +- Amazon Fire OS +- Android +- Firefox OS +- iOS +- Windows Phone 7 and 8 +- Windows 8 +- Windows + +### Android Quirks + +- Android supports a maximum of three buttons, and ignores any more than that. + +- On Android 3.0 and later, buttons are displayed in reverse order for devices that use the Holo theme. + +### Windows Quirks + +- On Windows prompt dialog is html-based due to lack of such native api. + +### Firefox OS Quirks: + +Both native-blocking `window.prompt()` and non-blocking `navigator.notification.prompt()` are available. + +## navigator.notification.beep + +The device plays a beep sound. + + navigator.notification.beep(times); + +- __times__: The number of times to repeat the beep. _(Number)_ + +### Example + + // Beep twice! + navigator.notification.beep(2); + +### Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- iOS +- Tizen +- Windows Phone 7 and 8 +- Windows 8 + +### Amazon Fire OS Quirks + +- Amazon Fire OS plays the default __Notification Sound__ specified under the __Settings/Display & Sound__ panel. + +### Android Quirks + +- Android plays the default __Notification ringtone__ specified under the __Settings/Sound & Display__ panel. + +### Windows Phone 7 and 8 Quirks + +- Relies on a generic beep file from the Cordova distribution. + +### Tizen Quirks + +- Tizen implements beeps by playing an audio file via the media API. + +- The beep file must be short, must be located in a `sounds` subdirectory of the application's root directory, and must be named `beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/RELEASENOTES.md b/plugins/cordova-plugin-dialogs/RELEASENOTES.md new file mode 100644 index 0000000..20e114f --- /dev/null +++ b/plugins/cordova-plugin-dialogs/RELEASENOTES.md @@ -0,0 +1,151 @@ + +# Release Notes + +### 1.2.1 (Apr 15, 2016) +* CB-10097 dialog doesn't show on **iOS** when called from a select list `onChange` event +* Remove `warning` emoji, as it doesn't correctly display in the docs website: http://cordova.apache.org/docs/en/dev/cordova-plugin-dialogs/index.html +* CB-10727 Dialogs plugin has warnings on **iOS** +* CB-10636 Add `JSHint` for plugins + +### 1.2.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* [CB-8549](https://issues.apache.org/jira/browse/CB-8549) Updated source to pass `Fortify` scan. +* Fixing contribute link. +* add `CSS class` to prompt `div` for **Windows** platform +* [CB-9347](https://issues.apache.org/jira/browse/CB-9347) - fix to allow to stack multiple `UIAlertControllers` + +### 1.1.1 (Jun 17, 2015) +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-dialogs documentation translation: cordova-plugin-dialogs +* fix npm md + +### 1.1.0 (May 06, 2015) +* [CB-8928](https://issues.apache.org/jira/browse/CB-8928): Removed direct call to `toStaticHTML`, only call it if we are sure it's present. This closes #52 +* [CB-7734](https://issues.apache.org/jira/browse/CB-7734) - `navigator.notification.alert` or `navigator.notification.confirm` seem have a "many words" issue. (closes #39) + +### 1.0.0 (Apr 15, 2015) +* [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) updated wp and bb specific references of old id to new id +* [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 +* [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 deprecated headers +* [CB-8565](https://issues.apache.org/jira/browse/CB-8565) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-dialogs documentation translation: cordova-plugin-dialogs +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8367](https://issues.apache.org/jira/browse/CB-8367) [org.apache.cordova.dialogs] Add Prompt support on Windows + +### 0.3.0 (Feb 04, 2015) +* Correct way to specify Windows platform in config.xml +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension +* [CB-7955](https://issues.apache.org/jira/browse/CB-7955) Add support "browser" platform + +### 0.2.11 (Dec 02, 2014) +* [CB-7737](https://issues.apache.org/jira/browse/CB-7737) lower min height for alert +* [CB-8038](https://issues.apache.org/jira/browse/CB-8038) backslash getting escaped twice in **BB10** +* [CB-8029](https://issues.apache.org/jira/browse/CB-8029) test 1-based indexing for confirm +* [CB-7639](https://issues.apache.org/jira/browse/CB-7639) Update docs + manual tests +* [CB-7639](https://issues.apache.org/jira/browse/CB-7639) Revert back `isAlertShowing` flag in case of exception to prevent queuing of future dialogs. +* [CB-7639](https://issues.apache.org/jira/browse/CB-7639) Handle button labels as array on windows +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* Check for `setTextDirection` API level +* **Android** Make spinner dialog to use `THEME_DEVICE_DEFAULT_LIGHT` (same as the other dialogs) +* **Android** Unbreak `API` level < `14` +* [CB-7414](https://issues.apache.org/jira/browse/CB-7414) **BB10** Document callback parameter for `navigator.notification.alert` +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-dialogs documentation translation: cordova-plugin-dialogs +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin + +### 0.2.10 (Sep 17, 2014) +* [CB-7538](https://issues.apache.org/jira/browse/CB-7538) Android beep thread fix Beep now executes in it's own thread. It was previously executing in the main UI thread which was causing the application to lock up will the beep was occurring. Closing pull request +* Set dialog text dir to locale +* Renamed test dir, added nested plugin.xml +* added documentation for manual tests +* [CB-6965](https://issues.apache.org/jira/browse/CB-6965) Added manual tests +* [CB-6965](https://issues.apache.org/jira/browse/CB-6965) Port notification tests to test-framework + +### 0.2.9 (Aug 06, 2014) +* ubuntu: pass proper arguments to prompt callback +* ubuntu: use TextField instead of TextInput +* ubuntu: proper message escaping before passing to qml +* **FFOS** update notification.js +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* android: Explicitly apply default theme to dialogs +* Fix Beep exception on Android when no argument passed + +### 0.2.8 (Jun 05, 2014) +* [CB-6801](https://issues.apache.org/jira/browse/CB-6801) Add license +* running original windows.open, inAppBrowser is overriding it no need to place CSS in every page anymore +* [CB-5945](https://issues.apache.org/jira/browse/CB-5945) [Windows8] do not call success callbacks until dialog is dismissed +* [CB-4616](https://issues.apache.org/jira/browse/CB-4616) Returned index 0 was not documented for notification.prompt +* update docs to state that prompt is supported on windowsphone +* [CB-6528](https://issues.apache.org/jira/browse/CB-6528) allow scroll on alert message content +* [CB-6628][amazon-fireos]dialogs plugin's confirm and prompt methods dont work confirm() method was missing amazon-fireos platform check. added that. prompt() method had bug. It is executed in a worker thread that does not have a message queue(or Looper object) associated with it and hence "can't create a handler" exception is thrown. To fix this issue, we need to create the EditText widget from within the UI thread. This was fixed sometime ago when we added fireos platform but commit got lost somewhere. So fixing it again now. +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* Added check for isFinishing() on the parent activity to prevent crashes when trying to display dialogs when activity is in this phase of it's lifecycle +* [CB-4966](https://issues.apache.org/jira/browse/CB-4966) Dialogs are in window now No need to add anything to manifest or index.html +* Removing FirefoxOS Quirks * no need to add special permission (it's different API with the same name) * notification.css is added automatically + +### 0.2.7 (Apr 17, 2014) +* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit +* [CB-6411](https://issues.apache.org/jira/browse/CB-6411): [BlackBerry10] Work around Audio playback issue +* [CB-6411](https://issues.apache.org/jira/browse/CB-6411): [BlackBerry10] Updates to beep +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* Add NOTICE file + +### 0.2.6 (Feb 05, 2014) +* no need to recreate the manifest.webapp file after each `cordova prepare` for FFOS +* FFOS description added + +### 0.2.5 (Jan 02, 2014) +* [CB-4696](https://issues.apache.org/jira/browse/CB-4696) Fix compile error for Xcode 4.5. +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Dialogs plugin +* [CB-3762](https://issues.apache.org/jira/browse/CB-3762) Change prompt default to empty string +* Move images from css to img + +### 0.2.4 (Dec 4, 2013) +* add ubuntu platform +* 1. Added amazon-fireos platform. 2. Change to use amazon-fireos as a platform if user agent string contains 'cordova-amazon-fireos'. +* added beep funtionality using ms-winsoundevent:Notfication.Default + +### 0.2.3 (Oct 28, 2013) +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for dialogs plugin +* new plugin execute arguments supported +* new plugin style +* smaller fonts styling input +* img files copied inside plugin +* style added +* prompt added +* styling from James +* fixed "exec" calls addedd css, but not working yet +* first (blind) try +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.2.2 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [windows8] commandProxy was moved +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming reference in Notification.cs +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.dialogs to org.apache.cordova.dialogs +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4592](https://issues.apache.org/jira/browse/CB-4592) [Blackberry10] Added beep support +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. diff --git a/plugins/cordova-plugin-dialogs/doc/de/README.md b/plugins/cordova-plugin-dialogs/doc/de/README.md new file mode 100644 index 0000000..355cd4d --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/de/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +Dieses Plugin ermöglicht den Zugriff auf einige native Dialog-UI-Elemente über eine globale `navigator.notification`-Objekt. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Installation + + cordova plugin add cordova-plugin-dialogs + + +## Methoden + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +Zeigt eine benutzerdefinierte Warnung oder Dialogfeld Feld. Die meisten Implementierungen von Cordova ein native Dialogfeld für dieses Feature verwenden, jedoch einige Plattformen des Browsers-`alert`-Funktion, die in der Regel weniger anpassbar ist. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **Nachricht**: Dialogfeld Nachricht. *(String)* + + * **AlertCallback**: Callback aufgerufen wird, wenn Warnungs-Dialogfeld geschlossen wird. *(Funktion)* + + * **Titel**: Dialog "Titel". *(String)* (Optional, Standard ist`Alert`) + + * **ButtonName**: Name der Schaltfläche. *(String)* (Optional, Standard ist`OK`) + +### Beispiel + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Windows Phone 7 und 8 Eigenarten + + * Es gibt keine eingebaute Datenbanksuchroutine-Warnung, aber Sie können binden, wie folgt zu nennen `alert()` im globalen Gültigkeitsbereich: + + window.alert = navigator.notification.alert; + + + * Beide `alert` und `confirm` sind nicht blockierende Aufrufe, die Ergebnisse davon nur asynchron sind. + +### Firefox OS Macken: + +Native blockierenden `window.alert()` und nicht-blockierende `navigator.notification.alert()` zur Verfügung. + +### BlackBerry 10 Macken + +`navigator.notification.alert ('Text', Rückruf, 'Titel', 'Text')` Callback-Parameter wird die Zahl 1 übergeben. + +## navigator.notification.confirm + +Zeigt das Dialogfeld anpassbare Bestätigung. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **Nachricht**: Dialogfeld Nachricht. *(String)* + + * **ConfirmCallback**: Callback aufgerufen wird, mit Index gedrückt (1, 2 oder 3) oder wenn das Dialogfeld geschlossen wird, ohne einen Tastendruck (0). *(Funktion)* + + * **Titel**: Dialog "Titel". *(String)* (Optional, Standard ist`Confirm`) + + * **ButtonLabels**: Array von Zeichenfolgen, die Schaltflächenbezeichnungen angeben. *(Array)* (Optional, Standard ist [ `OK,Cancel` ]) + +### confirmCallback + +Die `confirmCallback` wird ausgeführt, wenn der Benutzer eine der Schaltflächen im Dialogfeld zur Bestätigung drückt. + +Der Rückruf dauert das Argument `buttonIndex` *(Anzahl)*, die der Index der Schaltfläche gedrückt ist. Beachten Sie, dass der Index 1-basierte Indizierung, sodass der Wert `1`, `2`, `3` usw. ist. + +### Beispiel + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Windows Phone 7 und 8 Eigenarten + + * Es gibt keine integrierte Browser-Funktion für `window.confirm` , aber Sie können es binden, indem Sie zuweisen: + + window.confirm = navigator.notification.confirm; + + + * Aufrufe von `alert` und `confirm` sind nicht blockierend, so dass das Ergebnis nur asynchron zur Verfügung steht. + +### Windows-Eigenheiten + + * Auf Windows8/8.1 kann nicht mehr als drei Schaltflächen MessageDialog-Instanz hinzu. + + * Auf Windows Phone 8.1 ist es nicht möglich, Dialog mit mehr als zwei Knöpfen zeigen. + +### Firefox OS Macken: + +Native blockierenden `window.confirm()` und nicht-blockierende `navigator.notification.confirm()` zur Verfügung. + +## navigator.notification.prompt + +Zeigt eine native Dialogfeld, das mehr als `Prompt`-Funktion des Browsers anpassbar ist. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **Nachricht**: Dialogfeld Nachricht. *(String)* + + * **promptCallback**: Callback aufgerufen wird, mit Index gedrückt (1, 2 oder 3) oder wenn das Dialogfeld geschlossen wird, ohne einen Tastendruck (0). *(Funktion)* + + * **title**: Dialog Title *(String)* (Optional, Standard ist `Prompt`) + + * **buttonLabels**: Array von Zeichenfolgen angeben Schaltfläche Etiketten *(Array)* (Optional, Standard ist `["OK", "Abbrechen"]`) + + * **defaultText**: Standard-Textbox Eingabewert (`String`) (Optional, Standard: leere Zeichenfolge) + +### promptCallback + +Die `promptCallback` wird ausgeführt, wenn der Benutzer eine der Schaltflächen im Eingabedialogfeld drückt. `Des Objekts an den Rückruf übergeben` enthält die folgenden Eigenschaften: + + * **buttonIndex**: der Index der Schaltfläche gedrückt. *(Anzahl)* Beachten Sie, dass der Index 1-basierte Indizierung, sodass der Wert `1`, `2`, `3` usw. ist. + + * **input1**: in Eingabedialogfeld eingegebenen Text. *(String)* + +### Beispiel + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * Firefox OS + * iOS + * Windows Phone 7 und 8 + * Windows 8 + * Windows + +### Android Eigenarten + + * Android unterstützt maximal drei Schaltflächen und mehr als das ignoriert. + + * Auf Android 3.0 und höher, werden die Schaltflächen in umgekehrter Reihenfolge für Geräte angezeigt, die das Holo-Design verwenden. + +### Windows-Eigenheiten + + * Unter Windows ist Prompt Dialogfeld html-basierten mangels solcher native api. + +### Firefox OS Macken: + +Native blockierenden `window.prompt()` und nicht-blockierende `navigator.notification.prompt()` zur Verfügung. + +## navigator.notification.beep + +Das Gerät spielt einen Signalton sound. + + navigator.notification.beep(times); + + + * **times**: die Anzahl der Wiederholungen des Signaltons. *(Anzahl)* + +### Beispiel + + // Beep twice! + navigator.notification.beep(2); + + +### Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 und 8 + * Windows 8 + +### Amazon Fire OS Macken + + * Amazon Fire OS spielt die Standardeinstellung **Akustische Benachrichtigung** unter **Einstellungen/Display & Sound** Bereich angegeben. + +### Android Eigenarten + + * Android spielt die Standardeinstellung **Benachrichtigung Klingelton** unter **Einstellungen/Sound & Display**-Panel angegeben. + +### Windows Phone 7 und 8 Eigenarten + + * Stützt sich auf eine generische Piepton-Datei aus der Cordova-Distribution. + +### Tizen Macken + + * Tizen implementiert Signaltöne durch Abspielen einer Audiodatei über die Medien API. + + * Die Beep-Datei muss kurz sein, in einem `sounds`-Unterverzeichnis des Stammverzeichnisses der Anwendung befinden muss und muss den Namen `beep.wav`. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/de/index.md b/plugins/cordova-plugin-dialogs/doc/de/index.md new file mode 100644 index 0000000..c003d40 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/de/index.md @@ -0,0 +1,273 @@ + + +# cordova-plugin-dialogs + +Dieses Plugin ermöglicht den Zugriff auf einige native Dialog-UI-Elemente über eine globale `navigator.notification`-Objekt. + +Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist, steht es nicht bis nach dem `Deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Installation + + cordova plugin add cordova-plugin-dialogs + + +## Methoden + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +Zeigt eine benutzerdefinierte Warnung oder Dialogfeld Feld. Die meisten Implementierungen von Cordova ein native Dialogfeld für dieses Feature verwenden, jedoch einige Plattformen des Browsers-`alert`-Funktion, die in der Regel weniger anpassbar ist. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + +* **Nachricht**: Dialogfeld Nachricht. *(String)* + +* **AlertCallback**: Callback aufgerufen wird, wenn Warnungs-Dialogfeld geschlossen wird. *(Funktion)* + +* **Titel**: Dialog "Titel". *(String)* (Optional, Standard ist`Alert`) + +* **ButtonName**: Name der Schaltfläche. *(String)* (Optional, Standard ist`OK`) + +### Beispiel + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Windows Phone 7 und 8 Eigenarten + +* Es gibt keine eingebaute Datenbanksuchroutine-Warnung, aber Sie können binden, wie folgt zu nennen `alert()` im globalen Gültigkeitsbereich: + + window.alert = navigator.notification.alert; + + +* Beide `alert` und `confirm` sind nicht blockierende Aufrufe, die Ergebnisse davon nur asynchron sind. + +### Firefox OS Macken: + +Native blockierenden `window.alert()` und nicht-blockierende `navigator.notification.alert()` zur Verfügung. + +### BlackBerry 10 Macken + +`navigator.notification.alert ('Text', Rückruf, 'Titel', 'Text')` Callback-Parameter wird die Zahl 1 übergeben. + +## navigator.notification.confirm + +Zeigt das Dialogfeld anpassbare Bestätigung. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **Nachricht**: Dialogfeld Nachricht. *(String)* + +* **ConfirmCallback**: Callback aufgerufen wird, mit Index gedrückt (1, 2 oder 3) oder wenn das Dialogfeld geschlossen wird, ohne einen Tastendruck (0). *(Funktion)* + +* **Titel**: Dialog "Titel". *(String)* (Optional, Standard ist`Confirm`) + +* **ButtonLabels**: Array von Zeichenfolgen, die Schaltflächenbezeichnungen angeben. *(Array)* (Optional, Standard ist [ `OK,Cancel` ]) + +### confirmCallback + +Die `confirmCallback` wird ausgeführt, wenn der Benutzer eine der Schaltflächen im Dialogfeld zur Bestätigung drückt. + +Der Rückruf dauert das Argument `buttonIndex` *(Anzahl)*, die der Index der Schaltfläche gedrückt ist. Beachten Sie, dass der Index 1-basierte Indizierung, sodass der Wert `1`, `2`, `3` usw. ist. + +### Beispiel + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Windows Phone 7 und 8 Eigenarten + +* Es gibt keine integrierte Browser-Funktion für `window.confirm` , aber Sie können es binden, indem Sie zuweisen: + + window.confirm = navigator.notification.confirm; + + +* Aufrufe von `alert` und `confirm` sind nicht blockierend, so dass das Ergebnis nur asynchron zur Verfügung steht. + +### Windows-Eigenheiten + +* Auf Windows8/8.1 kann nicht mehr als drei Schaltflächen MessageDialog-Instanz hinzu. + +* Auf Windows Phone 8.1 ist es nicht möglich, Dialog mit mehr als zwei Knöpfen zeigen. + +### Firefox OS Macken: + +Native blockierenden `window.confirm()` und nicht-blockierende `navigator.notification.confirm()` zur Verfügung. + +## navigator.notification.prompt + +Zeigt eine native Dialogfeld, das mehr als `Prompt`-Funktion des Browsers anpassbar ist. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **Nachricht**: Dialogfeld Nachricht. *(String)* + +* **promptCallback**: Callback aufgerufen wird, mit Index gedrückt (1, 2 oder 3) oder wenn das Dialogfeld geschlossen wird, ohne einen Tastendruck (0). *(Funktion)* + +* **title**: Dialog Title *(String)* (Optional, Standard ist `Prompt`) + +* **buttonLabels**: Array von Zeichenfolgen angeben Schaltfläche Etiketten *(Array)* (Optional, Standard ist `["OK", "Abbrechen"]`) + +* **defaultText**: Standard-Textbox Eingabewert (`String`) (Optional, Standard: leere Zeichenfolge) + +### promptCallback + +Die `promptCallback` wird ausgeführt, wenn der Benutzer eine der Schaltflächen im Eingabedialogfeld drückt. `Des Objekts an den Rückruf übergeben` enthält die folgenden Eigenschaften: + +* **buttonIndex**: der Index der Schaltfläche gedrückt. *(Anzahl)* Beachten Sie, dass der Index 1-basierte Indizierung, sodass der Wert `1`, `2`, `3` usw. ist. + +* **input1**: in Eingabedialogfeld eingegebenen Text. *(String)* + +### Beispiel + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* Firefox OS +* iOS +* Windows Phone 7 und 8 +* Windows 8 +* Windows + +### Android Eigenarten + +* Android unterstützt maximal drei Schaltflächen und mehr als das ignoriert. + +* Auf Android 3.0 und höher, werden die Schaltflächen in umgekehrter Reihenfolge für Geräte angezeigt, die das Holo-Design verwenden. + +### Windows-Eigenheiten + +* Unter Windows ist Prompt Dialogfeld html-basierten mangels solcher native api. + +### Firefox OS Macken: + +Native blockierenden `window.prompt()` und nicht-blockierende `navigator.notification.prompt()` zur Verfügung. + +## navigator.notification.beep + +Das Gerät spielt einen Signalton sound. + + navigator.notification.beep(times); + + +* **times**: die Anzahl der Wiederholungen des Signaltons. *(Anzahl)* + +### Beispiel + + // Beep twice! + navigator.notification.beep(2); + + +### Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 und 8 +* Windows 8 + +### Amazon Fire OS Macken + +* Amazon Fire OS spielt die Standardeinstellung **Akustische Benachrichtigung** unter **Einstellungen/Display & Sound** Bereich angegeben. + +### Android Eigenarten + +* Android spielt die Standardeinstellung **Benachrichtigung Klingelton** unter **Einstellungen/Sound & Display**-Panel angegeben. + +### Windows Phone 7 und 8 Eigenarten + +* Stützt sich auf eine generische Piepton-Datei aus der Cordova-Distribution. + +### Tizen Macken + +* Tizen implementiert Signaltöne durch Abspielen einer Audiodatei über die Medien API. + +* Die Beep-Datei muss kurz sein, in einem `sounds`-Unterverzeichnis des Stammverzeichnisses der Anwendung befinden muss und muss den Namen `beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/doc/es/README.md b/plugins/cordova-plugin-dialogs/doc/es/README.md new file mode 100644 index 0000000..e7df5fe --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/es/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +Este plugin permite acceder a algunos elementos de interfaz de usuario nativa diálogo vía global `navigator.notification` objeto. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Instalación + + cordova plugin add cordova-plugin-dialogs + + +## Métodos + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +Muestra un cuadro de alerta o cuadro de diálogo personalizado. La mayoría de las implementaciones de Cordova utilizan un cuadro de diálogo nativa para esta característica, pero algunas plataformas utilizan el navegador `alert` la función, que es típicamente menos personalizable. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **message**: mensaje de diálogo. *(String)* + + * **alertCallback**: Callback para invocar al diálogo de alerta es desestimada. *(Función)* + + * **title**: título de diálogo. *(String)* (Opcional, el valor predeterminado de `Alert`) + + * **buttonName**: nombre del botón. *(String)* (Opcional, por defecto `Aceptar`) + +### Ejemplo + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Windows Phone 7 y 8 rarezas + + * No hay ninguna alerta del navegador integrado, pero puede enlazar uno proceda a llamar `alert()` en el ámbito global: + + window.alert = navigator.notification.alert; + + + * `alert` y `confirm` son non-blocking llamadas, cuyos resultados sólo están disponibles de forma asincrónica. + +### Firefox OS rarezas: + +Dos nativos de bloqueo `window.alert()` y no-bloqueo `navigator.notification.alert()` están disponibles. + +### BlackBerry 10 rarezas + +`navigator.notification.alert('text', callback, 'title', 'text')`parámetro de devolución de llamada se pasa el número 1. + +## navigator.notification.confirm + +Muestra un cuadro de diálogo de confirmación personalizables. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **message**: mensaje de diálogo. *(String)* + + * **confirmCallback**: Callback para invocar con índice de botón pulsado (1, 2 o 3) o cuando el diálogo es despedido sin la presión del botón (0). *(Función)* + + * **title**: título de diálogo. *(String)* (Opcional, por defecto a `confirmar`) + + * **buttonLabels**: matriz de cadenas especificando las etiquetas de botón. *(Matriz)* (Opcional, por defecto [`OK, cancelar`]) + +### confirmCallback + +El `confirmCallback` se ejecuta cuando el usuario presiona uno de los botones en el cuadro de diálogo de confirmación. + +La devolución de llamada toma el argumento `buttonIndex` *(número)*, que es el índice del botón presionado. Observe que el índice utiliza indexación basada en uno, entonces el valor es `1` , `2` , `3` , etc.. + +### Ejemplo + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Windows Phone 7 y 8 rarezas + + * No hay ninguna función de navegador incorporado para `window.confirm`, pero lo puede enlazar mediante la asignación: + + window.confirm = navigator.notification.confirm; + + + * Llamadas de `alert` y `confirm` son non-blocking, así que el resultado sólo está disponible de forma asincrónica. + +### Windows rarezas + + * Sobre Windows8/8.1 no es posible agregar más de tres botones a instancia de MessageDialog. + + * En Windows Phone 8.1 no es posible Mostrar cuadro de diálogo con más de dos botones. + +### Firefox OS rarezas: + +Dos nativos de bloqueo `window.confirm()` y no-bloqueo `navigator.notification.confirm()` están disponibles. + +## navigator.notification.prompt + +Muestra un cuadro de diálogo nativa que es más personalizable que del navegador `prompt` función. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **message**: mensaje de diálogo. *(String)* + + * **promptCallback**: Callback para invocar con índice del botón pulsado (1, 2 ó 3) o cuando el cuadro de diálogo es despedido sin la presión del botón (0). *(Función)* + + * **título**: título *(String)* (opcional, por defecto de diálogo`Prompt`) + + * **buttonLabels**: matriz de cadenas especificando botón etiquetas *(Array)* (opcional, por defecto`["OK","Cancel"]`) + + * **defaultText**: valor de la entrada predeterminada textbox ( `String` ) (opcional, por defecto: cadena vacía) + +### promptCallback + +El `promptCallback` se ejecuta cuando el usuario presiona uno de los botones del cuadro de diálogo pronto. El `results` objeto que se pasa a la devolución de llamada contiene las siguientes propiedades: + + * **buttonIndex**: el índice del botón presionado. *(Número)* Observe que el índice utiliza indexación basada en uno, entonces el valor es `1` , `2` , `3` , etc.. + + * **INPUT1**: el texto introducido en el cuadro de diálogo pronto. *(String)* + +### Ejemplo + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### Plataformas soportadas + + * Amazon fire OS + * Android + * Firefox OS + * iOS + * Windows Phone 7 y 8 + * Windows 8 + * Windows + +### Rarezas Android + + * Android soporta un máximo de tres botones e ignora nada más. + + * En Android 3.0 y posteriores, los botones aparecen en orden inverso para dispositivos que utilizan el tema Holo. + +### Windows rarezas + + * En Windows pronto diálogo está basado en html debido a falta de tal api nativa. + +### Firefox OS rarezas: + +Dos nativos de bloqueo `window.prompt()` y no-bloqueo `navigator.notification.prompt()` están disponibles. + +## navigator.notification.beep + +El aparato reproduce un sonido sonido. + + navigator.notification.beep(times); + + + * **tiempos**: el número de veces a repetir la señal. *(Número)* + +### Ejemplo + + // Beep twice! + navigator.notification.beep(2); + + +### Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 y 8 + * Windows 8 + +### Amazon fuego OS rarezas + + * Amazon fuego OS reproduce el **Sonido de notificación** especificados en el panel de **configuración/pantalla y sonido** por defecto. + +### Rarezas Android + + * Androide reproduce el **tono de notificación** especificados en el panel **ajustes de sonido y visualización** por defecto. + +### Windows Phone 7 y 8 rarezas + + * Se basa en un archivo de sonido genérico de la distribución de Córdoba. + +### Rarezas Tizen + + * Tizen implementa pitidos por reproducir un archivo de audio a través de los medios de comunicación API. + + * El archivo de sonido debe ser corto, debe estar ubicado en un `sounds` subdirectorio del directorio raíz de la aplicación y deben ser nombrados`beep.wav`. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/es/index.md b/plugins/cordova-plugin-dialogs/doc/es/index.md new file mode 100644 index 0000000..9ff4251 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/es/index.md @@ -0,0 +1,247 @@ + + +# cordova-plugin-dialogs + +Este plugin permite acceder a algunos elementos de interfaz de usuario nativa diálogo vía global `navigator.notification` objeto. + +Aunque el objeto está unido al ámbito global `navigator` , no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(navigator.notification)}; + + +## Instalación + + Cordova plugin agregar cordova-plugin-dialogs + + +## Métodos + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +Muestra un cuadro de alerta o cuadro de diálogo personalizado. La mayoría de las implementaciones de Cordova utilizan un cuadro de diálogo nativa para esta característica, pero algunas plataformas utilizan el navegador `alert` la función, que es típicamente menos personalizable. + + Navigator.Notification.alert (mensaje, alertCallback, [title], [buttonName]) + + +* **message**: mensaje de diálogo. *(String)* + +* **alertCallback**: Callback para invocar al diálogo de alerta es desestimada. *(Función)* + +* **title**: título de diálogo. *(String)* (Opcional, el valor predeterminado de `Alert`) + +* **buttonName**: nombre del botón. *(String)* (Opcional, por defecto `Aceptar`) + +### Ejemplo + + function alertDismissed() {/ / hacer algo} navigator.notification.alert ('Tú eres el ganador!', / / mensaje alertDismissed, / / callback 'Game Over', / / título 'hecho' / / buttonName); + + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Windows Phone 7 y 8 rarezas + +* No hay ninguna alerta del navegador integrado, pero puede enlazar uno proceda a llamar `alert()` en el ámbito global: + + window.alert = navigator.notification.alert; + + +* `alert` y `confirm` son non-blocking llamadas, cuyos resultados sólo están disponibles de forma asincrónica. + +### Firefox OS rarezas: + +Dos nativos de bloqueo `window.alert()` y no-bloqueo `navigator.notification.alert()` están disponibles. + +### BlackBerry 10 rarezas + +`navigator.notification.alert('text', callback, 'title', 'text')`parámetro de devolución de llamada se pasa el número 1. + +## navigator.notification.confirm + +Muestra un cuadro de diálogo de confirmación personalizables. + + Navigator.Notification.CONFIRM (mensaje, confirmCallback, [title], [buttonLabels]) + + +* **message**: mensaje de diálogo. *(String)* + +* **confirmCallback**: Callback para invocar con índice de botón pulsado (1, 2 o 3) o cuando el diálogo es despedido sin la presión del botón (0). *(Función)* + +* **title**: título de diálogo. *(String)* (Opcional, por defecto a `confirmar`) + +* **buttonLabels**: matriz de cadenas especificando las etiquetas de botón. *(Matriz)* (Opcional, por defecto [`OK, cancelar`]) + +### confirmCallback + +El `confirmCallback` se ejecuta cuando el usuario presiona uno de los botones en el cuadro de diálogo de confirmación. + +La devolución de llamada toma el argumento `buttonIndex` *(número)*, que es el índice del botón presionado. Observe que el índice utiliza indexación basada en uno, entonces el valor es `1` , `2` , `3` , etc.. + +### Ejemplo + + function onConfirm(buttonIndex) {alert ('Tu botón seleccionado' + buttonIndex);} + + Navigator.Notification.CONFIRM ('Tú eres el ganador!', / / mensaje onConfirm, / callback para invocar con índice del botón pulsado 'Game Over', / / / título ['reiniciar', 'Exit'] / / buttonLabels); + + +### Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Windows Phone 7 y 8 rarezas + +* No hay ninguna función de navegador incorporado para `window.confirm`, pero lo puede enlazar mediante la asignación: + + window.confirm = navigator.notification.confirm; + + +* Llamadas de `alert` y `confirm` son non-blocking, así que el resultado sólo está disponible de forma asincrónica. + +### Windows rarezas + +* Sobre Windows8/8.1 no es posible agregar más de tres botones a instancia de MessageDialog. + +* En Windows Phone 8.1 no es posible Mostrar cuadro de diálogo con más de dos botones. + +### Firefox OS rarezas: + +Dos nativos de bloqueo `window.confirm()` y no-bloqueo `navigator.notification.confirm()` están disponibles. + +## navigator.notification.prompt + +Muestra un cuadro de diálogo nativa que es más personalizable que del navegador `prompt` función. + + Navigator.Notification.prompt (mensaje, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **mensaje**: mensaje de diálogo. *(String)* + +* **promptCallback**: Callback para invocar con índice del botón pulsado (1, 2 ó 3) o cuando el cuadro de diálogo es despedido sin la presión del botón (0). *(Función)* + +* **título**: título *(String)* (opcional, por defecto de diálogo`Prompt`) + +* **buttonLabels**: matriz de cadenas especificando botón etiquetas *(Array)* (opcional, por defecto`["OK","Cancel"]`) + +* **defaultText**: valor de la entrada predeterminada textbox ( `String` ) (opcional, por defecto: cadena vacía) + +### promptCallback + +El `promptCallback` se ejecuta cuando el usuario presiona uno de los botones del cuadro de diálogo pronto. El `results` objeto que se pasa a la devolución de llamada contiene las siguientes propiedades: + +* **buttonIndex**: el índice del botón presionado. *(Número)* Observe que el índice utiliza indexación basada en uno, entonces el valor es `1` , `2` , `3` , etc.. + +* **INPUT1**: el texto introducido en el cuadro de diálogo pronto. *(String)* + +### Ejemplo + + function onPrompt(results) {alert ("seleccionaron botón número" + results.buttonIndex + "y entró en" + results.input1);} + + Navigator.Notification.prompt ('Por favor introduce tu nombre', / / mensaje onPrompt, / / callback para invocar 'Registro', / / título ['Ok', 'Exit'], / / buttonLabels 'Jane Doe' / / defaultText); + + +### Plataformas soportadas + +* Amazon fuego OS +* Android +* Firefox OS +* iOS +* Windows Phone 7 y 8 +* Windows 8 +* Windows + +### Rarezas Android + +* Android soporta un máximo de tres botones e ignora nada más. + +* En Android 3.0 y posteriores, los botones aparecen en orden inverso para dispositivos que utilizan el tema Holo. + +### Windows rarezas + +* En Windows pronto diálogo está basado en html debido a falta de tal api nativa. + +### Firefox OS rarezas: + +Dos nativos de bloqueo `window.prompt()` y no-bloqueo `navigator.notification.prompt()` están disponibles. + +## navigator.notification.beep + +El aparato reproduce un sonido sonido. + + Navigator.Notification.Beep(Times); + + +* **tiempos**: el número de veces a repetir la señal. *(Número)* + +### Ejemplo + + Dos pitidos. + Navigator.Notification.Beep(2); + + +### Plataformas soportadas + +* Amazon fuego OS +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 y 8 +* Windows 8 + +### Amazon fuego OS rarezas + +* Amazon fuego OS reproduce el **Sonido de notificación** especificados en el panel de **configuración/pantalla y sonido** por defecto. + +### Rarezas Android + +* Androide reproduce el **tono de notificación** especificados en el panel **ajustes de sonido y visualización** por defecto. + +### Windows Phone 7 y 8 rarezas + +* Se basa en un archivo de sonido genérico de la distribución de Córdoba. + +### Rarezas Tizen + +* Tizen implementa pitidos por reproducir un archivo de audio a través de los medios de comunicación API. + +* El archivo de sonido debe ser corto, debe estar ubicado en un `sounds` subdirectorio del directorio raíz de la aplicación y deben ser nombrados`beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/doc/fr/README.md b/plugins/cordova-plugin-dialogs/doc/fr/README.md new file mode 100644 index 0000000..994c826 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/fr/README.md @@ -0,0 +1,249 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +Ce plugin permet d'accéder à certains éléments d'interface utilisateur native de dialogue via un global `navigator.notification` objet. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.notification);} + + +## Installation + + cordova plugin add cordova-plugin-dialogs + + +## Méthodes + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +Affiche une boîte de dialogue ou d'alerte personnalisé. La plupart des implémentations de Cordova utilisent une boîte de dialogue natives pour cette fonctionnalité, mais certaines plates-formes du navigateur `alert` fonction, qui est généralement moins personnalisable. + + Navigator.notification.Alert (message, alertCallback, [title], [buttonName]) + + + * **message**: message de la boîte de dialogue. *(String)* + + * **alertCallback**: callback à appeler lorsque la boîte de dialogue d'alerte est rejetée. *(Fonction)* + + * **titre**: titre de la boîte de dialogue. *(String)* (Facultatif, par défaut`Alert`) + + * **buttonName**: nom du bouton. *(String)* (Facultatif, par défaut`OK`) + +### Exemple + + function alertDismissed() {/ / faire quelque chose} navigator.notification.alert ('Vous êtes le gagnant!', / / message alertDismissed, / / rappel « Game Over », / / titre « Done » / / buttonName) ; + + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Notes au sujet de Windows Phone 7 et 8 + + * Il n'y a aucune boîte de dialogue d'alerte intégrée au navigateur, mais vous pouvez en lier une pour appeler `alert()` dans le scope global: + + window.alert = navigator.notification.alert; + + + * Les deux appels `alert` et `confirm` sont non-blocants, leurs résultats ne sont disponibles que de façon asynchrone. + +### Firefox OS Quirks : + +Les deux indigènes bloquant `window.alert()` et non-bloquante `navigator.notification.alert()` sont disponibles. + +### BlackBerry 10 Quirks + +`navigator.notification.alert('text', callback, 'title', 'text')`paramètre callback est passé numéro 1. + +## navigator.notification.confirm + +Affiche une boîte de dialogue de confirmation personnalisable. + + Navigator.notification.Confirm (message, confirmCallback, [title], [buttonLabels]) + + + * **message**: message de la boîte de dialogue. *(String)* + + * **confirmCallback**: callback à appeler avec l'index du bouton pressé (1, 2 ou 3) ou lorsque la boîte de dialogue est fermée sans qu'un bouton ne soit pressé (0). *(Fonction)* + + * **titre**: titre de dialogue. *(String)* (Facultatif, par défaut`Confirm`) + + * **buttonLabels**: tableau de chaînes spécifiant les étiquettes des boutons. *(Array)* (Optionnel, par défaut, [ `OK,Cancel` ]) + +### confirmCallback + +Le `confirmCallback` s'exécute lorsque l'utilisateur appuie sur un bouton dans la boîte de dialogue de confirmation. + +Le rappel prend l'argument `buttonIndex` *(nombre)*, qui est l'index du bouton activé. Notez que l'index utilise base d'indexation, la valeur est `1` , `2` , `3` , etc.. + +### Exemple + + function onConfirm(buttonIndex) {alert (« Vous bouton sélectionné » + buttonIndex);} + + Navigator.notification.Confirm ('Vous êtes le gagnant!', / / message onConfirm, / / rappel d'invoquer avec l'index du bouton enfoncé « Game Over », / / title ['redémarrer', « Exit »] / / buttonLabels) ; + + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Notes au sujet de Windows Phone 7 et 8 + + * Il n'y a aucune fonction intégrée au navigateur pour `window.confirm`, mais vous pouvez en lier une en affectant: + + window.confirm = navigator.notification.confirm ; + + + * Les appels à `alert` et `confirm` sont non-bloquants, donc le résultat est seulement disponible de façon asynchrone. + +### Bizarreries de Windows + + * Sur Windows8/8.1, il n'est pas possible d'ajouter plus de trois boutons à MessageDialog instance. + + * Sur Windows Phone 8.1, il n'est pas possible d'établir le dialogue avec plus de deux boutons. + +### Firefox OS Quirks : + +Les deux indigènes bloquant `window.confirm()` et non-bloquante `navigator.notification.confirm()` sont disponibles. + +## navigator.notification.prompt + +Affiche une boîte de dialogue natif qui est plus personnalisable que le navigateur `prompt` fonction. + + Navigator.notification.prompt (message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **message**: message de la boîte de dialogue. *(String)* + + * **promptCallback**: rappel d'invoquer avec l'index du bouton pressé (1, 2 ou 3) ou lorsque la boîte de dialogue est fermée sans une presse de bouton (0). *(Fonction)* + + * **titre**: titre *(String)* (facultatif, la valeur par défaut de dialogue`Prompt`) + + * **buttonLabels**: tableau de chaînes spécifiant les bouton *(Array)* (facultatif, par défaut, les étiquettes`["OK","Cancel"]`) + + * **defaultText**: zone de texte par défaut entrée valeur ( `String` ) (en option, par défaut : chaîne vide) + +### promptCallback + +Le `promptCallback` s'exécute lorsque l'utilisateur appuie sur un bouton dans la boîte de dialogue d'invite. Le `results` objet passé au rappel contient les propriétés suivantes : + + * **buttonIndex**: l'index du bouton activé. *(Nombre)* Notez que l'index utilise base d'indexation, la valeur est `1` , `2` , `3` , etc.. + + * **entrée 1**: le texte entré dans la boîte de dialogue d'invite. *(String)* + +### Exemple + + function onPrompt(results) {alert (« Vous avez sélectionné le numéro du bouton » + results.buttonIndex + « et saisi » + results.input1);} + + Navigator.notification.prompt ('Veuillez saisir votre nom', / / message onPrompt, / / rappel à appeler « Registration », / / title ['Ok', 'Exit'], / / buttonLabels « Jane Doe » / / defaultText) ; + + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * Firefox OS + * iOS + * Windows Phone 7 et 8 + * Windows 8 + * Windows + +### Quirks Android + + * Android prend en charge un maximum de trois boutons et ignore plus que cela. + + * Sur Android 3.0 et versions ultérieures, les boutons sont affichés dans l'ordre inverse pour les appareils qui utilisent le thème Holo. + +### Bizarreries de Windows + + * Sous Windows, dialogue d'invite est basé sur html en raison de l'absence de ces api native. + +### Firefox OS Quirks : + +Les deux indigènes bloquant `window.prompt()` et non-bloquante `navigator.notification.prompt()` sont disponibles. + +## navigator.notification.beep + +Le dispositif joue un bip sonore. + + Navigator.notification.Beep(Times) ; + + + * **temps**: le nombre de fois répéter le bip. *(Nombre)* + +### Exemple + + Deux bips ! + Navigator.notification.Beep(2) ; + + +### Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * iOS + * Paciarelli + * Windows Phone 7 et 8 + * Windows 8 + +### Amazon Fire OS Quirks + + * Amazon Fire OS joue la valeur par défaut le **Son de Notification** spécifié sous le panneau **d'affichage des réglages/& Sound** . + +### Quirks Android + + * Android joue la **sonnerie de Notification** spécifié sous le panneau des **réglages/son et affichage** de valeur par défaut. + +### Notes au sujet de Windows Phone 7 et 8 + + * S'appuie sur un fichier générique bip de la distribution de Cordova. + +### Bizarreries de paciarelli + + * Paciarelli implémente les bips en lisant un fichier audio via les médias API. + + * Le fichier sonore doit être court, doit se trouver dans un `sounds` sous-répertoire du répertoire racine de l'application et doit être nommé`beep.wav`. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/fr/index.md b/plugins/cordova-plugin-dialogs/doc/fr/index.md new file mode 100644 index 0000000..fec0939 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/fr/index.md @@ -0,0 +1,247 @@ + + +# cordova-plugin-dialogs + +Ce plugin permet d'accéder à certains éléments d'interface utilisateur native de dialogue via un global `navigator.notification` objet. + +Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(navigator.notification);} + + +## Installation + + Cordova plugin ajouter cordova-plugin-dialogs + + +## Méthodes + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +Affiche une boîte de dialogue ou d'alerte personnalisé. La plupart des implémentations de Cordova utilisent une boîte de dialogue natives pour cette fonctionnalité, mais certaines plates-formes du navigateur `alert` fonction, qui est généralement moins personnalisable. + + Navigator.notification.Alert (message, alertCallback, [title], [buttonName]) + + +* **message**: message de la boîte de dialogue. *(String)* + +* **alertCallback**: callback à appeler lorsque la boîte de dialogue d'alerte est rejetée. *(Fonction)* + +* **titre**: titre de la boîte de dialogue. *(String)* (Facultatif, par défaut`Alert`) + +* **buttonName**: nom du bouton. *(String)* (Facultatif, par défaut`OK`) + +### Exemple + + function alertDismissed() {/ / faire quelque chose} navigator.notification.alert ('Vous êtes le gagnant!', / / message alertDismissed, / / rappel « Game Over », / / titre « Done » / / buttonName) ; + + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Windows Phone 7 et 8 Quirks + +* Il n'y a aucune boîte de dialogue d'alerte intégrée au navigateur, mais vous pouvez en lier une pour appeler `alert()` dans le scope global: + + window.alert = navigator.notification.alert; + + +* Les deux appels `alert` et `confirm` sont non-blocants, leurs résultats ne sont disponibles que de façon asynchrone. + +### Firefox OS Quirks : + +Les deux indigènes bloquant `window.alert()` et non-bloquante `navigator.notification.alert()` sont disponibles. + +### BlackBerry 10 Quirks + +`navigator.notification.alert('text', callback, 'title', 'text')`paramètre callback est passé numéro 1. + +## navigator.notification.confirm + +Affiche une boîte de dialogue de confirmation personnalisable. + + Navigator.notification.Confirm (message, confirmCallback, [title], [buttonLabels]) + + +* **message**: message de la boîte de dialogue. *(String)* + +* **confirmCallback**: callback à appeler avec l'index du bouton pressé (1, 2 ou 3) ou lorsque la boîte de dialogue est fermée sans qu'un bouton ne soit pressé (0). *(Fonction)* + +* **titre**: titre de dialogue. *(String)* (Facultatif, par défaut`Confirm`) + +* **buttonLabels**: tableau de chaînes spécifiant les étiquettes des boutons. *(Array)* (Optionnel, par défaut, [ `OK,Cancel` ]) + +### confirmCallback + +Le `confirmCallback` s'exécute lorsque l'utilisateur appuie sur un bouton dans la boîte de dialogue de confirmation. + +Le rappel prend l'argument `buttonIndex` *(nombre)*, qui est l'index du bouton activé. Notez que l'index utilise base d'indexation, la valeur est `1` , `2` , `3` , etc.. + +### Exemple + + function onConfirm(buttonIndex) {alert (« Vous bouton sélectionné » + buttonIndex);} + + Navigator.notification.Confirm ('Vous êtes le gagnant!', / / message onConfirm, / / rappel d'invoquer avec l'index du bouton enfoncé « Game Over », / / title ['redémarrer', « Exit »] / / buttonLabels) ; + + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Windows Phone 7 et 8 Quirks + +* Il n'y a aucune fonction intégrée au navigateur pour `window.confirm`, mais vous pouvez en lier une en affectant: + + window.confirm = navigator.notification.confirm ; + + +* Les appels à `alert` et `confirm` sont non-bloquants, donc le résultat est seulement disponible de façon asynchrone. + +### Bizarreries de Windows + +* Sur Windows8/8.1, il n'est pas possible d'ajouter plus de trois boutons à MessageDialog instance. + +* Sur Windows Phone 8.1, il n'est pas possible d'établir le dialogue avec plus de deux boutons. + +### Firefox OS Quirks : + +Les deux indigènes bloquant `window.confirm()` et non-bloquante `navigator.notification.confirm()` sont disponibles. + +## navigator.notification.prompt + +Affiche une boîte de dialogue natif qui est plus personnalisable que le navigateur `prompt` fonction. + + Navigator.notification.prompt (message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **message**: message de la boîte de dialogue. *(String)* + +* **promptCallback**: rappel d'invoquer avec l'index du bouton pressé (1, 2 ou 3) ou lorsque la boîte de dialogue est fermée sans une presse de bouton (0). *(Fonction)* + +* **titre**: titre *(String)* (facultatif, la valeur par défaut de dialogue`Prompt`) + +* **buttonLabels**: tableau de chaînes spécifiant les bouton *(Array)* (facultatif, par défaut, les étiquettes`["OK","Cancel"]`) + +* **defaultText**: zone de texte par défaut entrée valeur ( `String` ) (en option, par défaut : chaîne vide) + +### promptCallback + +Le `promptCallback` s'exécute lorsque l'utilisateur appuie sur un bouton dans la boîte de dialogue d'invite. Le `results` objet passé au rappel contient les propriétés suivantes : + +* **buttonIndex**: l'index du bouton activé. *(Nombre)* Notez que l'index utilise base d'indexation, la valeur est `1` , `2` , `3` , etc.. + +* **entrée 1**: le texte entré dans la boîte de dialogue d'invite. *(String)* + +### Exemple + + function onPrompt(results) {alert (« Vous avez sélectionné le numéro du bouton » + results.buttonIndex + « et saisi » + results.input1);} + + Navigator.notification.prompt ('Veuillez saisir votre nom', / / message onPrompt, / / rappel à appeler « Registration », / / title ['Ok', 'Exit'], / / buttonLabels « Jane Doe » / / defaultText) ; + + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* Firefox OS +* iOS +* Windows Phone 7 et 8 +* Windows 8 +* Windows + +### Quirks Android + +* Android prend en charge un maximum de trois boutons et ignore plus que cela. + +* Sur Android 3.0 et versions ultérieures, les boutons sont affichés dans l'ordre inverse pour les appareils qui utilisent le thème Holo. + +### Bizarreries de Windows + +* Sous Windows, dialogue d'invite est basé sur html en raison de l'absence de ces api native. + +### Firefox OS Quirks : + +Les deux indigènes bloquant `window.prompt()` et non-bloquante `navigator.notification.prompt()` sont disponibles. + +## navigator.notification.beep + +Le dispositif joue un bip sonore. + + Navigator.notification.Beep(Times) ; + + +* **temps**: le nombre de fois répéter le bip. *(Nombre)* + +### Exemple + + Deux bips ! + Navigator.notification.Beep(2) ; + + +### Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Paciarelli +* Windows Phone 7 et 8 +* Windows 8 + +### Amazon Fire OS Quirks + +* Amazon Fire OS joue la valeur par défaut le **Son de Notification** spécifié sous le panneau **d'affichage des réglages/& Sound** . + +### Quirks Android + +* Android joue la **sonnerie de Notification** spécifié sous le panneau des **réglages/son et affichage** de valeur par défaut. + +### Windows Phone 7 et 8 Quirks + +* S'appuie sur un fichier générique bip de la distribution de Cordova. + +### Bizarreries de paciarelli + +* Paciarelli implémente les bips en lisant un fichier audio via les médias API. + +* Le fichier sonore doit être court, doit se trouver dans un `sounds` sous-répertoire du répertoire racine de l'application et doit être nommé`beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/doc/it/README.md b/plugins/cordova-plugin-dialogs/doc/it/README.md new file mode 100644 index 0000000..8a72905 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/it/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +Questo plugin consente di accedere ad alcuni elementi di interfaccia utente nativa dialogo tramite un oggetto globale `navigator.notification`. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Installazione + + cordova plugin add cordova-plugin-dialogs + + +## Metodi + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +Mostra una finestra di avviso o la finestra di dialogo personalizzata. La maggior parte delle implementazioni di Cordova utilizzano una finestra di dialogo nativa per questa caratteristica, ma alcune piattaforme utilizzano la funzione di `alert` del browser, che è in genere meno personalizzabile. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **message**: messaggio finestra di dialogo. *(String)* + + * **alertCallback**: Callback da richiamare quando viene chiusa la finestra di avviso. *(Funzione)* + + * **title**: titolo di dialogo. *(String)* (Opzionale, default è `Alert`) + + * **buttonName**: nome del pulsante. *(String)* (Opzionale, default è `OK`) + +### Esempio + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Windows Phone 7 e 8 stranezze + + * Non non c'è nessun avviso del browser integrato, ma è possibile associare uno come segue per chiamare `alert()` in ambito globale: + + window.alert = navigator.notification.alert; + + + * Entrambi `alert` e `confirm` sono non di blocco chiamate, risultati di cui sono disponibili solo in modo asincrono. + +### Firefox OS Stranezze: + +Nativo di blocco `window.alert()` blocco `navigator.notification.alert()` sono disponibili sia. + +### BlackBerry 10 capricci + +parametro di callback `navigator.notification.alert ('text' callback, 'title' 'text')` viene passato il numero 1. + +## navigator.notification.confirm + +Visualizza una finestra di dialogo conferma personalizzabile. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **message**: messaggio finestra di dialogo. *(String)* + + * **confirmCallback**: Callback da richiamare con l'indice del pulsante premuto (1, 2 o 3) o quando la finestra di dialogo viene chiusa senza una pressione del pulsante (0). *(Funzione)* + + * **titolo**: titolo di dialogo. *(String)* (Opzionale, default è`Confirm`) + + * **buttonLabels**: matrice di stringhe che specificano le etichette dei pulsanti. *(Matrice)* (Opzionale, default è [ `OK,Cancel` ]) + +### confirmCallback + +Il `confirmCallback` viene eseguito quando l'utente preme uno dei pulsanti nella finestra di dialogo conferma. + +Il callback accetta l'argomento `buttonIndex` *(numero)*, che è l'indice del pulsante premuto. Si noti che l'indice utilizza l'indicizzazione base uno, quindi il valore è `1`, `2`, `3`, ecc. + +### Esempio + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Windows Phone 7 e 8 stranezze + + * Non non c'è nessuna funzione browser incorporato per `window.confirm` , ma è possibile associare assegnando: + + window.confirm = navigator.notification.confirm; + + + * Chiama al `alert` e `confirm` sono non bloccante, quindi il risultato è disponibile solo in modo asincrono. + +### Stranezze di Windows + + * Su Windows8/8.1 non è possibile aggiungere più di tre pulsanti a MessageDialog istanza. + + * Su Windows Phone 8.1 non è possibile mostrare la finestra di dialogo con più di due pulsanti. + +### Firefox OS Stranezze: + +Nativo di blocco `window.confirm()` blocco `navigator.notification.confirm()` sono disponibili sia. + +## navigator.notification.prompt + +Visualizza una finestra di dialogo nativa che è più personalizzabile di funzione `prompt` del browser. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **message**: messaggio finestra di dialogo. *(String)* + + * **promptCallback**: Callback da richiamare con l'indice del pulsante premuto (1, 2 o 3) o quando la finestra di dialogo viene chiusa senza una pressione del pulsante (0). *(Funzione)* + + * **title**: dialogo titolo *(String)* (opzionale, default è `Prompt`) + + * **buttonLabels**: matrice di stringhe specificando il pulsante etichette *(Array)* (opzionale, default è `["OK", "Cancel"]`) + + * **defaultText**: valore (`String`) di input predefinito textbox (opzionale, Default: empty string) + +### promptCallback + +Il `promptCallback` viene eseguito quando l'utente preme uno dei pulsanti nella finestra di dialogo richiesta. `results` oggetto passato al metodo di callback contiene le seguenti proprietà: + + * **buttonIndex**: l'indice del pulsante premuto. *(Numero)* Si noti che l'indice utilizza l'indicizzazione base uno, quindi il valore è `1`, `2`, `3`, ecc. + + * **input1**: il testo immesso nella finestra di dialogo richiesta. *(String)* + +### Esempio + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * Firefox OS + * iOS + * Windows Phone 7 e 8 + * Windows 8 + * Windows + +### Stranezze Android + + * Android supporta un massimo di tre pulsanti e ignora di più di quello. + + * Su Android 3.0 e versioni successive, i pulsanti vengono visualizzati in ordine inverso per dispositivi che utilizzano il tema Holo. + +### Stranezze di Windows + + * Su Windows finestra di dialogo richiesta è a causa di mancanza di tali api nativa basata su html. + +### Firefox OS Stranezze: + +Nativo di blocco `window.prompt()` blocco `navigator.notification.prompt()` sono disponibili sia. + +## navigator.notification.beep + +Il dispositivo riproduce un bip sonoro. + + navigator.notification.beep(times); + + + * **times**: il numero di volte per ripetere il segnale acustico. *(Numero)* + +### Esempio + + // Beep twice! + navigator.notification.beep(2); + + +### Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 e 8 + * Windows 8 + +### Amazon fuoco OS stranezze + + * Amazon fuoco OS riproduce il **Suono di notifica** specificato sotto il pannello **Impostazioni/Display e il suono** predefinito. + +### Stranezze Android + + * Android giochi default **Notification ringtone** specificato sotto il pannello **impostazioni/audio e Display**. + +### Windows Phone 7 e 8 stranezze + + * Si basa su un file generico bip dalla distribuzione di Cordova. + +### Tizen stranezze + + * Tizen implementa bip di riproduzione di un file audio tramite i media API. + + * Il file beep deve essere breve, deve trovarsi in una sottodirectory di `sounds` della directory principale dell'applicazione e deve essere denominato `beep.wav`. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/it/index.md b/plugins/cordova-plugin-dialogs/doc/it/index.md new file mode 100644 index 0000000..e8e02c7 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/it/index.md @@ -0,0 +1,273 @@ + + +# cordova-plugin-dialogs + +Questo plugin consente di accedere ad alcuni elementi di interfaccia utente nativa dialogo tramite un oggetto globale `navigator.notification`. + +Anche se l'oggetto è associato con ambito globale del `navigator`, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Installazione + + cordova plugin add cordova-plugin-dialogs + + +## Metodi + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +Mostra una finestra di avviso o la finestra di dialogo personalizzata. La maggior parte delle implementazioni di Cordova utilizzano una finestra di dialogo nativa per questa caratteristica, ma alcune piattaforme utilizzano la funzione di `alert` del browser, che è in genere meno personalizzabile. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + +* **message**: messaggio finestra di dialogo. *(String)* + +* **alertCallback**: Callback da richiamare quando viene chiusa la finestra di avviso. *(Funzione)* + +* **title**: titolo di dialogo. *(String)* (Opzionale, default è `Alert`) + +* **buttonName**: nome del pulsante. *(String)* (Opzionale, default è `OK`) + +### Esempio + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Windows Phone 7 e 8 stranezze + +* Non non c'è nessun avviso del browser integrato, ma è possibile associare uno come segue per chiamare `alert()` in ambito globale: + + window.alert = navigator.notification.alert; + + +* Entrambi `alert` e `confirm` sono non di blocco chiamate, risultati di cui sono disponibili solo in modo asincrono. + +### Firefox OS Stranezze: + +Nativo di blocco `window.alert()` blocco `navigator.notification.alert()` sono disponibili sia. + +### BlackBerry 10 capricci + +parametro di callback `navigator.notification.alert ('text' callback, 'title' 'text')` viene passato il numero 1. + +## navigator.notification.confirm + +Visualizza una finestra di dialogo conferma personalizzabile. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **messaggio**: messaggio finestra di dialogo. *(String)* + +* **confirmCallback**: Callback da richiamare con l'indice del pulsante premuto (1, 2 o 3) o quando la finestra di dialogo viene chiusa senza una pressione del pulsante (0). *(Funzione)* + +* **titolo**: titolo di dialogo. *(String)* (Opzionale, default è`Confirm`) + +* **buttonLabels**: matrice di stringhe che specificano le etichette dei pulsanti. *(Matrice)* (Opzionale, default è [ `OK,Cancel` ]) + +### confirmCallback + +Il `confirmCallback` viene eseguito quando l'utente preme uno dei pulsanti nella finestra di dialogo conferma. + +Il callback accetta l'argomento `buttonIndex` *(numero)*, che è l'indice del pulsante premuto. Si noti che l'indice utilizza l'indicizzazione base uno, quindi il valore è `1`, `2`, `3`, ecc. + +### Esempio + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Windows Phone 7 e 8 stranezze + +* Non non c'è nessuna funzione browser incorporato per `window.confirm` , ma è possibile associare assegnando: + + window.confirm = navigator.notification.confirm; + + +* Chiama al `alert` e `confirm` sono non bloccante, quindi il risultato è disponibile solo in modo asincrono. + +### Stranezze di Windows + +* Su Windows8/8.1 non è possibile aggiungere più di tre pulsanti a MessageDialog istanza. + +* Su Windows Phone 8.1 non è possibile mostrare la finestra di dialogo con più di due pulsanti. + +### Firefox OS Stranezze: + +Nativo di blocco `window.confirm()` blocco `navigator.notification.confirm()` sono disponibili sia. + +## navigator.notification.prompt + +Visualizza una finestra di dialogo nativa che è più personalizzabile di funzione `prompt` del browser. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **message**: messaggio finestra di dialogo. *(String)* + +* **promptCallback**: Callback da richiamare con l'indice del pulsante premuto (1, 2 o 3) o quando la finestra di dialogo viene chiusa senza una pressione del pulsante (0). *(Funzione)* + +* **title**: dialogo titolo *(String)* (opzionale, default è `Prompt`) + +* **buttonLabels**: matrice di stringhe specificando il pulsante etichette *(Array)* (opzionale, default è `["OK", "Cancel"]`) + +* **defaultText**: valore (`String`) di input predefinito textbox (opzionale, Default: empty string) + +### promptCallback + +Il `promptCallback` viene eseguito quando l'utente preme uno dei pulsanti nella finestra di dialogo richiesta. `results` oggetto passato al metodo di callback contiene le seguenti proprietà: + +* **buttonIndex**: l'indice del pulsante premuto. *(Numero)* Si noti che l'indice utilizza l'indicizzazione base uno, quindi il valore è `1`, `2`, `3`, ecc. + +* **input1**: il testo immesso nella finestra di dialogo richiesta. *(String)* + +### Esempio + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* Firefox OS +* iOS +* Windows Phone 7 e 8 +* Windows 8 +* Windows + +### Stranezze Android + +* Android supporta un massimo di tre pulsanti e ignora di più di quello. + +* Su Android 3.0 e versioni successive, i pulsanti vengono visualizzati in ordine inverso per dispositivi che utilizzano il tema Holo. + +### Stranezze di Windows + +* Su Windows finestra di dialogo richiesta è a causa di mancanza di tali api nativa basata su html. + +### Firefox OS Stranezze: + +Nativo di blocco `window.prompt()` blocco `navigator.notification.prompt()` sono disponibili sia. + +## navigator.notification.beep + +Il dispositivo riproduce un bip sonoro. + + navigator.notification.beep(times); + + +* **times**: il numero di volte per ripetere il segnale acustico. *(Numero)* + +### Esempio + + // Beep twice! + navigator.notification.beep(2); + + +### Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 e 8 +* Windows 8 + +### Amazon fuoco OS stranezze + +* Amazon fuoco OS riproduce il **Suono di notifica** specificato sotto il pannello **Impostazioni/Display e il suono** predefinito. + +### Stranezze Android + +* Android giochi default **Notification ringtone** specificato sotto il pannello **impostazioni/audio e Display**. + +### Windows Phone 7 e 8 stranezze + +* Si basa su un file generico bip dalla distribuzione di Cordova. + +### Tizen stranezze + +* Tizen implementa bip di riproduzione di un file audio tramite i media API. + +* Il file beep deve essere breve, deve trovarsi in una sottodirectory di `sounds` della directory principale dell'applicazione e deve essere denominato `beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/doc/ja/README.md b/plugins/cordova-plugin-dialogs/doc/ja/README.md new file mode 100644 index 0000000..0722658 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/ja/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +ã“ã®ãƒ—ラグインã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.notification` オブジェクトを介ã—ã¦ã„ãã¤ã‹ãƒã‚¤ãƒ†ã‚£ãƒ– ダイアログ㮠UI è¦ç´ ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã—ã¾ã™ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## インストール + + cordova plugin add cordova-plugin-dialogs + + +## メソッド + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +カスタムã®è­¦å‘Šã¾ãŸã¯ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスãŒè¡¨ç¤ºã•れã¾ã™ã€‚ ã»ã¨ã‚“ã©ã‚³ãƒ«ãƒ‰ãƒ ãƒã‚¤ãƒ†ã‚£ãƒ–] ダイアログ ボックスã®ä½¿ç”¨ã“ã®æ©Ÿèƒ½ãŒã„ãã¤ã‹ã®ãƒ—ラットフォームã¯é€šå¸¸å°ã•ã„カスタマイズå¯èƒ½ãªãƒ–ラウザー㮠`警告` 機能を使用ã—ã¾ã™ã€‚ + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **メッセージ**: ダイアログ メッセージ。*(文字列)* + + * **alertCallback**: 警告ダイアログãŒé–‰ã˜ã‚‰ã‚ŒãŸã¨ãã«å‘¼ã³å‡ºã™ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã€‚*(機能)* + + * **タイトル**: ダイアログã®ã‚¿ã‚¤ãƒˆãƒ«ã€‚*(文字列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯`Alert`) + + * **buttonName**: ボタンã®åå‰ã€‚*(文字列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯`OK`) + +### 例 + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### Windows Phone 7 㨠8 ç™– + + * 組ã¿è¾¼ã¿ã®ãƒ–ラウザー警告ãŒãªã„呼ã³å‡ºã—を次ã®ã‚ˆã†ã« 1 ã¤ã‚’ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ `alert()` ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープã§ã€‚ + + window.alert = navigator.notification.alert; + + + * 両方㮠`alert` 㨠`confirm` ã¯éžãƒ–ロッキング呼ã³å‡ºã—ã€çµæžœã¯éžåŒæœŸçš„ã«ã®ã¿åˆ©ç”¨ã§ãã¾ã™ã€‚ + +### Firefox OS 互æ›: + +ãƒã‚¤ãƒ†ã‚£ãƒ– ブロック `window.alert()` ã¨éžãƒ–ロッキング `navigator.notification.alert()` ãŒã‚りã¾ã™ã€‚ + +### ブラックベリー 10 ç™– + +`navigator.notification.alert ('text' コールãƒãƒƒã‚¯ 'title'ã€'text')` コールãƒãƒƒã‚¯ ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã¯æ•° 1 ã«æ¸¡ã•れã¾ã™ã€‚ + +## navigator.notification.confirm + +カスタマイズå¯èƒ½ãªç¢ºèªã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **メッセージ**: ダイアログ メッセージ。*(文字列)* + + * **confirmCallback**: インデックス (1ã€2ã€ã¾ãŸã¯ 3) を押ã•れãŸãƒœã‚¿ãƒ³ã¾ãŸã¯ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスã¯ã€ãƒœã‚¿ãƒ³ã‚’押㙠(0) ãªã—ã«è§£é›‡ã•れãŸã¨ãã«å‘¼ã³å‡ºã™ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã€‚*(機能)* + + * **タイトル**: ダイアログã®ã‚¿ã‚¤ãƒˆãƒ«ã€‚*(文字列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯`Confirm`) + + * **buttonLabels**: ボタンã®ãƒ©ãƒ™ãƒ«ã‚’指定ã™ã‚‹æ–‡å­—列ã®é…列。*(é…列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯ [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback` ã¯ã€ã„ãšã‚Œã‹ã®ç¢ºèªãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスã§ãƒœã‚¿ãƒ³ã‚’押ã—ãŸã¨ãã«å®Ÿè¡Œã—ã¾ã™ã€‚ + +コールãƒãƒƒã‚¯ã¯ã€å¼•æ•° `buttonIndex` *(番å·ï¼‰* ã¯ã€æŠ¼ã•れãŸãƒœã‚¿ãƒ³ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã€‚ インデックスãŒã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ 1 ベースã®ã§ã€å€¤ã¯ `1`ã€`2`ã€`3` ãªã©ã«æ³¨æ„ã—ã¦ãã ã•ã„。 + +### 例 + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### Windows Phone 7 㨠8 ç™– + + * 組ã¿è¾¼ã¿ãƒ–ãƒ©ã‚¦ã‚¶ãƒ¼ã®æ©Ÿèƒ½ã¯ã‚りã¾ã›ã‚“ `window.confirm` ãŒå‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã«ã‚ˆã£ã¦ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + + window.confirm = navigator.notification.confirm; + + + * 呼ã³å‡ºã—ã‚’ `alert` 㨠`confirm` ã§ã¯éžãƒ–ロッキングã€çµæžœã¯éžåŒæœŸçš„ã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚ + +### Windows ã®ç™– + + * Windows8/8.1 ã® MessageDialog インスタンスを 3 ã¤ä»¥ä¸Šã®ãƒœã‚¿ãƒ³ã‚’追加ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + + * Windows Phone 8.1 ã« 2 ã¤ä»¥ä¸Šã®ãƒœã‚¿ãƒ³ã‚’æŒã¤ãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã‚’表示ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + +### Firefox OS 互æ›: + +ãƒã‚¤ãƒ†ã‚£ãƒ– ブロック `window.confirm()` ã¨éžãƒ–ロッキング `navigator.notification.confirm()` ãŒã‚りã¾ã™ã€‚ + +## navigator.notification.prompt + +ブラウザー㮠`プロンプト` 機能より詳細ã«ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºã¯ãƒã‚¤ãƒ†ã‚£ãƒ–ã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **メッセージ**: ダイアログ メッセージ。*(文字列)* + + * **promptCallback**: インデックス (1ã€2ã€ã¾ãŸã¯ 3) を押ã•れãŸãƒœã‚¿ãƒ³ã¾ãŸã¯ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスã¯ã€ãƒœã‚¿ãƒ³ã‚’押㙠(0) ãªã—ã«è§£é›‡ã•れãŸã¨ãã«å‘¼ã³å‡ºã™ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã€‚*(機能)* + + * **title**: タイトル *(String)* (çœç•¥å¯èƒ½ã€æ—¢å®š `プロンプト` ダイアログ) + + * **buttonLabels**: ボタンã®ãƒ©ãƒ™ãƒ« *(é…列)* (çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ `["OK"ã€ã€Œã‚­ãƒ£ãƒ³ã‚»ãƒ«ã€]` を指定ã™ã‚‹æ–‡å­—列ã®é…列) + + * **defaultText**: 既定テキスト ボックスã®å…¥åЛ値 (`文字列`) (çœç•¥å¯èƒ½ã€æ—¢å®š: ç©ºã®æ–‡å­—列) + +### promptCallback + +`promptCallback` ã¯ã€ãƒ—ロンプト ダイアログ ボックス内ã®ãƒœã‚¿ãƒ³ã®ã„ãšã‚Œã‹ã‚’押ã—ãŸã¨ãã«å®Ÿè¡Œã—ã¾ã™ã€‚コールãƒãƒƒã‚¯ã«æ¸¡ã•れる `results` オブジェクトã«ã¯ã€æ¬¡ã®ãƒ—ロパティãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ + + * **buttonIndex**: 押ã•れãŸãƒœã‚¿ãƒ³ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã€‚*(æ•°)*インデックスãŒã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ 1 ベースã®ã§ã€å€¤ã¯ `1`ã€`2`ã€`3` ãªã©ã«æ³¨æ„ã—ã¦ãã ã•ã„。 + + * **input1**: プロンプト ダイアログ ボックスã«å…¥åŠ›ã—ãŸãƒ†ã‚­ã‚¹ãƒˆã€‚*(文字列)* + +### 例 + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + +### Android ã®ç™– + + * Android ã¯æœ€å¤§ 3 ã¤ã®ãƒœã‚¿ãƒ³ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã‚‹ã—ã€ãれ以上無視ã—ã¾ã™ã€‚ + + * アンドロイド 3.0 ã¨å¾Œã€ãƒ›ãƒ­ã®ãƒ†ãƒ¼ãƒžã‚’使用ã™ã‚‹ãƒ‡ãƒã‚¤ã‚¹ã‚’逆ã®é †åºã§ãƒœã‚¿ãƒ³ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + +### Windows ã®ç™– + + * Windows プロンプト ダイアログ㯠html ベースã®ã‚ˆã†ãªãƒã‚¤ãƒ†ã‚£ãƒ– api ã®ä¸è¶³ã®ãŸã‚ã§ã™ã€‚ + +### Firefox OS 互æ›: + +ãƒã‚¤ãƒ†ã‚£ãƒ– ブロック `window.prompt()` ã¨éžãƒ–ロッキング `navigator.notification.prompt()` ãŒã‚りã¾ã™ã€‚ + +## navigator.notification.beep + +デãƒã‚¤ã‚¹ サウンドをビープ音をå†ç”Ÿã—ã¾ã™ã€‚ + + navigator.notification.beep(times); + + + * **times**: ビープ音を繰り返ã™å›žæ•°ã€‚*(æ•°)* + +### 例 + + // Beep twice! + navigator.notification.beep(2); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * iOS + * Tizen + * Windows Phone 7 㨠8 + * Windows 8 + +### ã‚¢ãƒžã‚¾ãƒ³ç« OS ç™– + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS デフォルト **設定/表示 & サウンド** パãƒãƒ«ã®ä¸‹ã«æŒ‡å®šã—㟠**通知音** ã‚’æžœãŸã—ã¦ã„ã¾ã™ã€‚ + +### Android ã®ç™– + + * アンドロイド デフォルト **通知ç€ä¿¡éŸ³** **設定/サウンド & ディスプレイ** パãƒãƒ«ã®ä¸‹ã«æŒ‡å®šã‚’æžœãŸã—ã¦ã„ã¾ã™ã€‚ + +### Windows Phone 7 㨠8 ç™– + + * コルドãƒåˆ†å¸ƒã‹ã‚‰ã‚¸ã‚§ãƒãƒªãƒƒã‚¯ ビープ音ファイルã«ä¾å­˜ã—ã¾ã™ã€‚ + +### Tizen ã®ç™– + + * Tizen ã¯ã€ãƒ¡ãƒ‡ã‚£ã‚¢ API 経由ã§ã‚ªãƒ¼ãƒ‡ã‚£ã‚ª ファイルをå†ç”Ÿã—ã¦ãƒ“ープ音を実装ã—ã¾ã™ã€‚ + + * ビープ音ファイル短ã„ã€`sounds` アプリケーションã®ãƒ«ãƒ¼ãƒˆ ディレクトリã®ã‚µãƒ–ディレクトリã§ã‚ã‚‹å¿…è¦ãŒã‚り。ã€`beep.wav` ã¨ã„ã†åå‰ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/ja/index.md b/plugins/cordova-plugin-dialogs/doc/ja/index.md new file mode 100644 index 0000000..b530860 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/ja/index.md @@ -0,0 +1,273 @@ + + +# cordova-plugin-dialogs + +ã“ã®ãƒ—ラグインã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `navigator.notification` オブジェクトを介ã—ã¦ã„ãã¤ã‹ãƒã‚¤ãƒ†ã‚£ãƒ– ダイアログ㮠UI è¦ç´ ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æä¾›ã—ã¾ã™ã€‚ + +オブジェクトã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープ㮠`ナビゲーター` ã«æ·»ä»˜ã€ãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## インストール + + cordova plugin add cordova-plugin-dialogs + + +## メソッド + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +カスタムã®è­¦å‘Šã¾ãŸã¯ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスãŒè¡¨ç¤ºã•れã¾ã™ã€‚ ã»ã¨ã‚“ã©ã‚³ãƒ«ãƒ‰ãƒ ãƒã‚¤ãƒ†ã‚£ãƒ–] ダイアログ ボックスã®ä½¿ç”¨ã“ã®æ©Ÿèƒ½ãŒã„ãã¤ã‹ã®ãƒ—ラットフォームã¯é€šå¸¸å°ã•ã„カスタマイズå¯èƒ½ãªãƒ–ラウザー㮠`警告` 機能を使用ã—ã¾ã™ã€‚ + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + +* **メッセージ**: ダイアログ メッセージ。*(文字列)* + +* **alertCallback**: 警告ダイアログãŒé–‰ã˜ã‚‰ã‚ŒãŸã¨ãã«å‘¼ã³å‡ºã™ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã€‚*(機能)* + +* **タイトル**: ダイアログã®ã‚¿ã‚¤ãƒˆãƒ«ã€‚*(文字列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯`Alert`) + +* **buttonName**: ボタンã®åå‰ã€‚*(文字列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯`OK`) + +### 例 + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### Windows Phone 7 㨠8 ç™– + +* 組ã¿è¾¼ã¿ã®ãƒ–ラウザー警告ãŒãªã„呼ã³å‡ºã—を次ã®ã‚ˆã†ã« 1 ã¤ã‚’ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ `alert()` ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« スコープã§ã€‚ + + window.alert = navigator.notification.alert; + + +* 両方㮠`alert` 㨠`confirm` ã¯éžãƒ–ロッキング呼ã³å‡ºã—ã€çµæžœã¯éžåŒæœŸçš„ã«ã®ã¿åˆ©ç”¨ã§ãã¾ã™ã€‚ + +### Firefox OS 互æ›: + +ãƒã‚¤ãƒ†ã‚£ãƒ– ブロック `window.alert()` ã¨éžãƒ–ロッキング `navigator.notification.alert()` ãŒã‚りã¾ã™ã€‚ + +### ブラックベリー 10 ç™– + +`navigator.notification.alert ('text' コールãƒãƒƒã‚¯ 'title'ã€'text')` コールãƒãƒƒã‚¯ ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã¯æ•° 1 ã«æ¸¡ã•れã¾ã™ã€‚ + +## navigator.notification.confirm + +カスタマイズå¯èƒ½ãªç¢ºèªã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **メッセージ**: ダイアログ メッセージ。*(文字列)* + +* **confirmCallback**: インデックス (1ã€2ã€ã¾ãŸã¯ 3) を押ã•れãŸãƒœã‚¿ãƒ³ã¾ãŸã¯ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスã¯ã€ãƒœã‚¿ãƒ³ã‚’押㙠(0) ãªã—ã«è§£é›‡ã•れãŸã¨ãã«å‘¼ã³å‡ºã™ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã€‚*(機能)* + +* **タイトル**: ダイアログã®ã‚¿ã‚¤ãƒˆãƒ«ã€‚*(文字列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯`Confirm`) + +* **buttonLabels**: ボタンã®ãƒ©ãƒ™ãƒ«ã‚’指定ã™ã‚‹æ–‡å­—列ã®é…列。*(é…列)*(çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ã¯ [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback` ã¯ã€ã„ãšã‚Œã‹ã®ç¢ºèªãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスã§ãƒœã‚¿ãƒ³ã‚’押ã—ãŸã¨ãã«å®Ÿè¡Œã—ã¾ã™ã€‚ + +コールãƒãƒƒã‚¯ã¯ã€å¼•æ•° `buttonIndex` *(番å·ï¼‰* ã¯ã€æŠ¼ã•れãŸãƒœã‚¿ãƒ³ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã€‚ インデックスãŒã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ 1 ベースã®ã§ã€å€¤ã¯ `1`ã€`2`ã€`3` ãªã©ã«æ³¨æ„ã—ã¦ãã ã•ã„。 + +### 例 + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### Windows Phone 7 㨠8 ç™– + +* 組ã¿è¾¼ã¿ãƒ–ãƒ©ã‚¦ã‚¶ãƒ¼ã®æ©Ÿèƒ½ã¯ã‚りã¾ã›ã‚“ `window.confirm` ãŒå‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã«ã‚ˆã£ã¦ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + + window.confirm = navigator.notification.confirm; + + +* 呼ã³å‡ºã—ã‚’ `alert` 㨠`confirm` ã§ã¯éžãƒ–ロッキングã€çµæžœã¯éžåŒæœŸçš„ã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚ + +### Windows ã®ç™– + +* Windows8/8.1 ã® MessageDialog インスタンスを 3 ã¤ä»¥ä¸Šã®ãƒœã‚¿ãƒ³ã‚’追加ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + +* Windows Phone 8.1 ã« 2 ã¤ä»¥ä¸Šã®ãƒœã‚¿ãƒ³ã‚’æŒã¤ãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã‚’表示ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + +### Firefox OS 互æ›: + +ãƒã‚¤ãƒ†ã‚£ãƒ– ブロック `window.confirm()` ã¨éžãƒ–ロッキング `navigator.notification.confirm()` ãŒã‚りã¾ã™ã€‚ + +## navigator.notification.prompt + +ブラウザー㮠`プロンプト` 機能より詳細ã«ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºã¯ãƒã‚¤ãƒ†ã‚£ãƒ–ã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **message**: ダイアログ メッセージ。*(文字列)* + +* **promptCallback**: インデックス (1ã€2ã€ã¾ãŸã¯ 3) を押ã•れãŸãƒœã‚¿ãƒ³ã¾ãŸã¯ãƒ€ã‚¤ã‚¢ãƒ­ã‚° ボックスã¯ã€ãƒœã‚¿ãƒ³ã‚’押㙠(0) ãªã—ã«è§£é›‡ã•れãŸã¨ãã«å‘¼ã³å‡ºã™ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã€‚*(機能)* + +* **title**: タイトル *(String)* (çœç•¥å¯èƒ½ã€æ—¢å®š `プロンプト` ダイアログ) + +* **buttonLabels**: ボタンã®ãƒ©ãƒ™ãƒ« *(é…列)* (çœç•¥å¯èƒ½ã€æ—¢å®šå€¤ `["OK"ã€ã€Œã‚­ãƒ£ãƒ³ã‚»ãƒ«ã€]` を指定ã™ã‚‹æ–‡å­—列ã®é…列) + +* **defaultText**: 既定テキスト ボックスã®å…¥åЛ値 (`文字列`) (çœç•¥å¯èƒ½ã€æ—¢å®š: ç©ºã®æ–‡å­—列) + +### promptCallback + +`promptCallback` ã¯ã€ãƒ—ロンプト ダイアログ ボックス内ã®ãƒœã‚¿ãƒ³ã®ã„ãšã‚Œã‹ã‚’押ã—ãŸã¨ãã«å®Ÿè¡Œã—ã¾ã™ã€‚コールãƒãƒƒã‚¯ã«æ¸¡ã•れる `results` オブジェクトã«ã¯ã€æ¬¡ã®ãƒ—ロパティãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ + +* **buttonIndex**: 押ã•れãŸãƒœã‚¿ãƒ³ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã€‚*(æ•°)*インデックスãŒã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ 1 ベースã®ã§ã€å€¤ã¯ `1`ã€`2`ã€`3` ãªã©ã«æ³¨æ„ã—ã¦ãã ã•ã„。 + +* **input1**: プロンプト ダイアログ ボックスã«å…¥åŠ›ã—ãŸãƒ†ã‚­ã‚¹ãƒˆã€‚*(文字列)* + +### 例 + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 +* Windows 8 +* Windows + +### Android ã®ç™– + +* Android ã¯æœ€å¤§ 3 ã¤ã®ãƒœã‚¿ãƒ³ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã‚‹ã—ã€ãれ以上無視ã—ã¾ã™ã€‚ + +* アンドロイド 3.0 ã¨å¾Œã€ãƒ›ãƒ­ã®ãƒ†ãƒ¼ãƒžã‚’使用ã™ã‚‹ãƒ‡ãƒã‚¤ã‚¹ã‚’逆ã®é †åºã§ãƒœã‚¿ãƒ³ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + +### Windows ã®ç™– + +* Windows プロンプト ダイアログ㯠html ベースã®ã‚ˆã†ãªãƒã‚¤ãƒ†ã‚£ãƒ– api ã®ä¸è¶³ã®ãŸã‚ã§ã™ã€‚ + +### Firefox OS 互æ›: + +ãƒã‚¤ãƒ†ã‚£ãƒ– ブロック `window.prompt()` ã¨éžãƒ–ロッキング `navigator.notification.prompt()` ãŒã‚りã¾ã™ã€‚ + +## navigator.notification.beep + +デãƒã‚¤ã‚¹ サウンドをビープ音をå†ç”Ÿã—ã¾ã™ã€‚ + + navigator.notification.beep(times); + + +* **times**: ビープ音を繰り返ã™å›žæ•°ã€‚*(æ•°)* + +### 例 + + // Beep twice! + navigator.notification.beep(2); + + +### サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* iOS +* Tizen +* Windows Phone 7 㨠8 +* Windows 8 + +### ã‚¢ãƒžã‚¾ãƒ³ç« OS ç™– + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS デフォルト **設定/表示 & サウンド** パãƒãƒ«ã®ä¸‹ã«æŒ‡å®šã—㟠**通知音** ã‚’æžœãŸã—ã¦ã„ã¾ã™ã€‚ + +### Android ã®ç™– + +* アンドロイド デフォルト **通知ç€ä¿¡éŸ³** **設定/サウンド & ディスプレイ** パãƒãƒ«ã®ä¸‹ã«æŒ‡å®šã‚’æžœãŸã—ã¦ã„ã¾ã™ã€‚ + +### Windows Phone 7 㨠8 ç™– + +* コルドãƒåˆ†å¸ƒã‹ã‚‰ã‚¸ã‚§ãƒãƒªãƒƒã‚¯ ビープ音ファイルã«ä¾å­˜ã—ã¾ã™ã€‚ + +### Tizen ã®ç™– + +* Tizen ã¯ã€ãƒ¡ãƒ‡ã‚£ã‚¢ API 経由ã§ã‚ªãƒ¼ãƒ‡ã‚£ã‚ª ファイルをå†ç”Ÿã—ã¦ãƒ“ープ音を実装ã—ã¾ã™ã€‚ + +* ビープ音ファイル短ã„ã€`sounds` アプリケーションã®ãƒ«ãƒ¼ãƒˆ ディレクトリã®ã‚µãƒ–ディレクトリã§ã‚ã‚‹å¿…è¦ãŒã‚り。ã€`beep.wav` ã¨ã„ã†åå‰ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚. diff --git a/plugins/cordova-plugin-dialogs/doc/ko/README.md b/plugins/cordova-plugin-dialogs/doc/ko/README.md new file mode 100644 index 0000000..04532da --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/ko/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `navigator.notification` 개체를 통해 몇 가지 기본 대화 ìƒìž UI ìš”ì†Œì— ì•¡ì„¸ìŠ¤í•  수 있습니다. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## 설치 + + cordova plugin add cordova-plugin-dialogs + + +## 메서드 + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +ì‚¬ìš©ìž ì§€ì • 경고 ë˜ëŠ” 대화 ìƒìžë¥¼ ë³´ì—¬ ì¤ë‹ˆë‹¤. ì´ ê¸°ëŠ¥ì— ëŒ€ 한 기본 대화 ìƒìžë¥¼ 사용 하는 ëŒ€ë¶€ë¶„ì˜ ì½”ë¥´ë„ë°” 구현 하지만 ì¼ë¶€ 플랫í¼ì€ ì¼ë°˜ì ìœ¼ë¡œ ëœ ì‚¬ìš©ìž ì •ì˜í•  수 있는 브ë¼ìš°ì €ì˜ `alert` ê¸°ëŠ¥ì„ ì‚¬ìš© 합니다. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **message**: 대화 메시지. *(문ìžì—´)* + + * **alertCallback**: ì½œë°±ì„ í˜¸ì¶œí•  때 경고 대화 기 ê°. *(기능)* + + * **title**: 제목 대화 ìƒìž. *(문ìžì—´)* (옵션, 기본값:`Alert`) + + * **buttonName**: 단추 ì´ë¦„. *(문ìžì—´)* (옵션, 기본값:`OK`) + +### 예를 들어 + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### Windows Phone 7, 8 특수 + + * 아니 내장 브ë¼ìš°ì € 경고 하지만 다ìŒê³¼ ê°™ì´ ì „í™”ë¥¼ ë°”ì¸ë”©í•  수 있습니다 `alert()` ì „ì—­ 범위ì—서: + + window.alert = navigator.notification.alert; + + + * 둘 다 `alert` 와 `confirm` 는 비차단 호출, ê²°ê³¼ 비ë™ê¸°ì ìœ¼ë¡œ 사용할 수 있습니다. + +### 파ì´ì–´ í­ìФ OS 단ì : + +기본 차단 `window.alert()` ë° ì°¨ë‹¨ ë˜ì§€ ì•Šì€ `navigator.notification.alert()` 사용할 수 있습니다. + +### 블랙베리 10 ë‹¨ì  + +`navigator.notification.alert ('í…스트', 콜백, '제목', 'í…스트')` 콜백 매개 변수 1 ë²ˆì„ ì „ë‹¬ ë©ë‹ˆë‹¤. + +## navigator.notification.confirm + +ì‚¬ìš©ìž ì •ì˜ í™•ì¸ ëŒ€í™” ìƒìžê°€ 표시 ë©ë‹ˆë‹¤. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **message**: 대화 메시지. *(문ìžì—´)* + + * **confirmCallback**: ì¸ë±ìФ 버튼 (1, 2 ë˜ëŠ” 3) ë˜ëŠ” 대화 ìƒìž ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ (0) ì—†ì´ ê¸° ê° ë  ë•Œ 호출할 콜백 합니다. *(기능)* + + * **title**: 제목 대화 ìƒìž. *(문ìžì—´)* (옵션, 기본값:`Confirm`) + + * **buttonLabels**: 단추 ë ˆì´ë¸”ì„ ì§€ì • 하는 문ìžì—´ 배열입니다. *(ë°°ì—´)* (옵션, ê¸°ë³¸ê°’ì€ [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback`는 사용ìžê°€ í™•ì¸ ëŒ€í™” ìƒìžì—서 단추 중 하나를 누를 때 실행 합니다. + +ì½œë°±ì´ ê±¸ë¦½ë‹ˆë‹¤ ì¸ìˆ˜ `buttonIndex` *(번호)를* ëˆ„ë¥´ë©´ëœ ë²„íŠ¼ì˜ ì¸ë±ìŠ¤ìž…ë‹ˆë‹¤. Note ì¸ë±ìФì—서는 ì¸ë±ì‹± 1 시작 ê°’ì€ `1`, `2`, `3`, 등등. + +### 예를 들어 + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### Windows Phone 7, 8 특수 + + * ì— ëŒ€ 한 기본 제공 브ë¼ìš°ì € 함수가 `window.confirm` , 그러나 할당 하 ì—¬ ë°”ì¸ë”©í•  수 있습니다: + + window.confirm = navigator.notification.confirm; + + + * 호출 `alert` ë° `confirm` ë˜ë¯€ë¡œ 차단 ë˜ì§€ ì•Šì€ ê²°ê³¼ë§Œ 비ë™ê¸°ì ìœ¼ë¡œ 사용할 수 있습니다. + +### 윈ë„ìš° 특수 + + * Windows8/8.1ì— 3 ê°œ ì´ìƒ 단추 MessageDialog ì¸ìŠ¤í„´ìŠ¤ë¥¼ 추가할 수는 없습니다. + + * Windows Phone 8.1ì— ë‘ ê°œ ì´ìƒì˜ 단추와 대화 ìƒìž 표시 수는 없습니다. + +### 파ì´ì–´ í­ìФ OS 단ì : + +기본 차단 `window.confirm()` ë° ì°¨ë‹¨ ë˜ì§€ ì•Šì€ `navigator.notification.confirm()` 사용할 수 있습니다. + +## navigator.notification.prompt + +브ë¼ìš°ì €ì˜ `프롬프트` 함수 보다 ë” ë§Žì€ ì‚¬ìš©ìž ì •ì˜ ê¸°ë³¸ 대화 ìƒìžê°€ 표시 ë©ë‹ˆë‹¤. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **message**: 대화 메시지. *(문ìžì—´)* + + * **promptCallback**: ì¸ë±ìФ 버튼 (1, 2 ë˜ëŠ” 3) ë˜ëŠ” 대화 ìƒìž ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ (0) ì—†ì´ ê¸° ê° ë  ë•Œ 호출할 콜백 합니다. *(기능)* + + * **title**: 제목 대화 ìƒìž. *(문ìžì—´)* (옵션, 기본값:`Prompt`) + + * **buttonLabels**: 버튼 ë ˆì´ë¸” *(ë°°ì—´)* (옵션, 기본값 `["확ì¸", "취소"]ì„` 지정 하는 문ìžì—´ì˜ ë°°ì—´) + + * **defaultText**: 기본 í…스트 ìƒìžì— ê°’ (`문ìžì—´`) ìž…ë ¥ (옵션, 기본값: 빈 문ìžì—´) + +### promptCallback + +`promptCallback`는 사용ìžê°€ 프롬프트 대화 ìƒìžì—서 단추 중 하나를 누를 때 실행 합니다. ì½œë°±ì— ì „ë‹¬ ëœ `results` 개체ì—는 ë‹¤ìŒ ì†ì„±ì´ í¬í•¨ ë˜ì–´ 있습니다. + + * **buttonIndex**: ëˆŒë ¤ì§„ëœ ë²„íŠ¼ì˜ ì¸ë±ìФ. *(수)* Note ì¸ë±ìФì—서는 ì¸ë±ì‹± 1 시작 ê°’ì€ `1`, `2`, `3`, 등등. + + * **input1**: 프롬프트 대화 ìƒìžì— ìž…ë ¥ 한 í…스트. *(문ìžì—´)* + +### 예를 들어 + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + +### 안 드 로ì´ë“œ ë‹¨ì  + + * 안 드 로ì´ë“œ 최대 3 ê°œì˜ ë‹¨ì¶”ë¥¼ ì§€ì› í•˜ ê³  그것 보다는 ë” ì´ìƒ 무시 합니다. + + * 안 드 로ì´ë“œ 3.0 ë° ë‚˜ì¤‘ì—, 단추는 홀로 테마를 사용 하는 ìž¥ì¹˜ì— ëŒ€ 한 반대 순서로 표시 ë©ë‹ˆë‹¤. + +### 윈ë„ìš° 특수 + + * 윈ë„ìš°ì—서 프롬프트 대화 ê°™ì€ ë„¤ì´í‹°ë¸Œ apiì˜ ë¶€ì¡±ìœ¼ë¡œ ì¸í•´ html 기반 ì´ë‹¤. + +### 파ì´ì–´ í­ìФ OS 단ì : + +기본 차단 `window.prompt()` ë° ì°¨ë‹¨ ë˜ì§€ ì•Šì€ `navigator.notification.prompt()` 사용할 수 있습니다. + +## navigator.notification.beep + +장치는 ê²½ê³ ìŒ ì†Œë¦¬ë¥¼ ìž¬ìƒ í•©ë‹ˆë‹¤. + + navigator.notification.beep(times); + + + * **times**: 경고ìŒì„ 반복 하는 횟수. *(수)* + +### 예를 들어 + + // Beep twice! + navigator.notification.beep(2); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * iOS + * Tizen + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + +### 아마존 화재 OS ë‹¨ì  + + * 아마존 화재 ìš´ì˜ ì²´ì œ 기본 **설정/ë””ìŠ¤í”Œë ˆì´ ë° ì‚¬ìš´ë“œ** 패ë„ì— ì§€ì • ëœ **알림 소리** ìž¬ìƒ ë©ë‹ˆë‹¤. + +### 안 드 로ì´ë“œ ë‹¨ì  + + * 안 드 로ì´ë“œ 기본 **알림 벨소리** **설정/사운드 ë° ë””ìŠ¤í”Œë ˆì´** 패ë„ì—서 지정 합니다. + +### Windows Phone 7, 8 특수 + + * 코르 ë„ìš° ë°” ë¶„í¬ì—서 ì¼ë°˜ ê²½ê³ ìŒ íŒŒì¼ì— ì˜ì¡´í•©ë‹ˆë‹¤. + +### Tizen 특수 + + * Tizenì€ ë¯¸ë””ì–´ API 통해 오디오 파ì¼ì„ ìž¬ìƒ í•˜ ì—¬ 경고ìŒì„ 구현 합니다. + + * ê²½ê³ ìŒ íŒŒì¼ ì§§ì€ ë˜ì–´ì•¼ 합니다, ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë£¨íŠ¸ ë””ë ‰í„°ë¦¬ì˜ `소리` 하위 ë””ë ‰í„°ë¦¬ì— ìœ„ì¹˜ 해야 합니다 ë° `beep.wav`는 명명 ëœ. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/ko/index.md b/plugins/cordova-plugin-dialogs/doc/ko/index.md new file mode 100644 index 0000000..8216d8c --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/ko/index.md @@ -0,0 +1,273 @@ + + +# cordova-plugin-dialogs + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `navigator.notification` 개체를 통해 몇 가지 기본 대화 ìƒìž UI ìš”ì†Œì— ì•¡ì„¸ìŠ¤í•  수 있습니다. + +개체 `navigator` 글로벌 범위 첨부 아니ì—ìš” 때까지 사용할 수 있는 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## 설치 + + cordova plugin add cordova-plugin-dialogs + + +## 메서드 + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +ì‚¬ìš©ìž ì§€ì • 경고 ë˜ëŠ” 대화 ìƒìžë¥¼ ë³´ì—¬ ì¤ë‹ˆë‹¤. ì´ ê¸°ëŠ¥ì— ëŒ€ 한 기본 대화 ìƒìžë¥¼ 사용 하는 ëŒ€ë¶€ë¶„ì˜ ì½”ë¥´ë„ë°” 구현 하지만 ì¼ë¶€ 플랫í¼ì€ ì¼ë°˜ì ìœ¼ë¡œ ëœ ì‚¬ìš©ìž ì •ì˜í•  수 있는 브ë¼ìš°ì €ì˜ `alert` ê¸°ëŠ¥ì„ ì‚¬ìš© 합니다. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + +* **message**: 대화 메시지. *(문ìžì—´)* + +* **alertCallback**: ì½œë°±ì„ í˜¸ì¶œí•  때 경고 대화 기 ê°. *(기능)* + +* **title**: 제목 대화 ìƒìž. *(문ìžì—´)* (옵션, 기본값:`Alert`) + +* **buttonName**: 단추 ì´ë¦„. *(문ìžì—´)* (옵션, 기본값:`OK`) + +### 예를 들어 + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### Windows Phone 7, 8 특수 + +* 아니 내장 브ë¼ìš°ì € 경고 하지만 다ìŒê³¼ ê°™ì´ ì „í™”ë¥¼ ë°”ì¸ë”©í•  수 있습니다 `alert()` ì „ì—­ 범위ì—서: + + window.alert = navigator.notification.alert; + + +* 둘 다 `alert` 와 `confirm` 는 비차단 호출, ê²°ê³¼ 비ë™ê¸°ì ìœ¼ë¡œ 사용할 수 있습니다. + +### 파ì´ì–´ í­ìФ OS 단ì : + +기본 차단 `window.alert()` ë° ì°¨ë‹¨ ë˜ì§€ ì•Šì€ `navigator.notification.alert()` 사용할 수 있습니다. + +### 블랙베리 10 ë‹¨ì  + +`navigator.notification.alert ('í…스트', 콜백, '제목', 'í…스트')` 콜백 매개 변수 1 ë²ˆì„ ì „ë‹¬ ë©ë‹ˆë‹¤. + +## navigator.notification.confirm + +ì‚¬ìš©ìž ì •ì˜ í™•ì¸ ëŒ€í™” ìƒìžê°€ 표시 ë©ë‹ˆë‹¤. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **message**: 대화 메시지. *(문ìžì—´)* + +* **confirmCallback**: ì¸ë±ìФ 버튼 (1, 2 ë˜ëŠ” 3) ë˜ëŠ” 대화 ìƒìž ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ (0) ì—†ì´ ê¸° ê° ë  ë•Œ 호출할 콜백 합니다. *(기능)* + +* **title**: 제목 대화 ìƒìž. *(문ìžì—´)* (옵션, 기본값:`Confirm`) + +* **buttonLabels**: 단추 ë ˆì´ë¸”ì„ ì§€ì • 하는 문ìžì—´ 배열입니다. *(ë°°ì—´)* (옵션, ê¸°ë³¸ê°’ì€ [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback`는 사용ìžê°€ í™•ì¸ ëŒ€í™” ìƒìžì—서 단추 중 하나를 누를 때 실행 합니다. + +ì½œë°±ì´ ê±¸ë¦½ë‹ˆë‹¤ ì¸ìˆ˜ `buttonIndex` *(번호)를* ëˆ„ë¥´ë©´ëœ ë²„íŠ¼ì˜ ì¸ë±ìŠ¤ìž…ë‹ˆë‹¤. Note ì¸ë±ìФì—서는 ì¸ë±ì‹± 1 시작 ê°’ì€ `1`, `2`, `3`, 등등. + +### 예를 들어 + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### Windows Phone 7, 8 특수 + +* ì— ëŒ€ 한 기본 제공 브ë¼ìš°ì € 함수가 `window.confirm` , 그러나 할당 하 ì—¬ ë°”ì¸ë”©í•  수 있습니다: + + window.confirm = navigator.notification.confirm; + + +* 호출 `alert` ë° `confirm` ë˜ë¯€ë¡œ 차단 ë˜ì§€ ì•Šì€ ê²°ê³¼ë§Œ 비ë™ê¸°ì ìœ¼ë¡œ 사용할 수 있습니다. + +### 윈ë„ìš° 특수 + +* Windows8/8.1ì— 3 ê°œ ì´ìƒ 단추 MessageDialog ì¸ìŠ¤í„´ìŠ¤ë¥¼ 추가할 수는 없습니다. + +* Windows Phone 8.1ì— ë‘ ê°œ ì´ìƒì˜ 단추와 대화 ìƒìž 표시 수는 없습니다. + +### 파ì´ì–´ í­ìФ OS 단ì : + +기본 차단 `window.confirm()` ë° ì°¨ë‹¨ ë˜ì§€ ì•Šì€ `navigator.notification.confirm()` 사용할 수 있습니다. + +## navigator.notification.prompt + +브ë¼ìš°ì €ì˜ `프롬프트` 함수 보다 ë” ë§Žì€ ì‚¬ìš©ìž ì •ì˜ ê¸°ë³¸ 대화 ìƒìžê°€ 표시 ë©ë‹ˆë‹¤. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **message**: 대화 메시지. *(문ìžì—´)* + +* **promptCallback**: ì¸ë±ìФ 버튼 (1, 2 ë˜ëŠ” 3) ë˜ëŠ” 대화 ìƒìž ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ (0) ì—†ì´ ê¸° ê° ë  ë•Œ 호출할 콜백 합니다. *(기능)* + +* **title**: 제목 대화 ìƒìž. *(문ìžì—´)* (옵션, 기본값:`Prompt`) + +* **buttonLabels**: 버튼 ë ˆì´ë¸” *(ë°°ì—´)* (옵션, 기본값 `["확ì¸", "취소"]ì„` 지정 하는 문ìžì—´ì˜ ë°°ì—´) + +* **defaultText**: 기본 í…스트 ìƒìžì— ê°’ (`문ìžì—´`) ìž…ë ¥ (옵션, 기본값: 빈 문ìžì—´) + +### promptCallback + +`promptCallback`는 사용ìžê°€ 프롬프트 대화 ìƒìžì—서 단추 중 하나를 누를 때 실행 합니다. ì½œë°±ì— ì „ë‹¬ ëœ `results` 개체ì—는 ë‹¤ìŒ ì†ì„±ì´ í¬í•¨ ë˜ì–´ 있습니다. + +* **buttonIndex**: ëˆŒë ¤ì§„ëœ ë²„íŠ¼ì˜ ì¸ë±ìФ. *(수)* Note ì¸ë±ìФì—서는 ì¸ë±ì‹± 1 시작 ê°’ì€ `1`, `2`, `3`, 등등. + +* **input1**: 프롬프트 대화 ìƒìžì— ìž…ë ¥ 한 í…스트. *(문ìžì—´)* + +### 예를 들어 + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 +* 윈ë„ìš° + +### 안 드 로ì´ë“œ ë‹¨ì  + +* 안 드 로ì´ë“œ 최대 3 ê°œì˜ ë‹¨ì¶”ë¥¼ ì§€ì› í•˜ ê³  그것 보다는 ë” ì´ìƒ 무시 합니다. + +* 안 드 로ì´ë“œ 3.0 ë° ë‚˜ì¤‘ì—, 단추는 홀로 테마를 사용 하는 ìž¥ì¹˜ì— ëŒ€ 한 반대 순서로 표시 ë©ë‹ˆë‹¤. + +### 윈ë„ìš° 특수 + +* 윈ë„ìš°ì—서 프롬프트 대화 ê°™ì€ ë„¤ì´í‹°ë¸Œ apiì˜ ë¶€ì¡±ìœ¼ë¡œ ì¸í•´ html 기반 ì´ë‹¤. + +### 파ì´ì–´ í­ìФ OS 단ì : + +기본 차단 `window.prompt()` ë° ì°¨ë‹¨ ë˜ì§€ ì•Šì€ `navigator.notification.prompt()` 사용할 수 있습니다. + +## navigator.notification.beep + +장치는 ê²½ê³ ìŒ ì†Œë¦¬ë¥¼ ìž¬ìƒ í•©ë‹ˆë‹¤. + + navigator.notification.beep(times); + + +* **times**: 경고ìŒì„ 반복 하는 횟수. *(수)* + +### 예를 들어 + + // Beep twice! + navigator.notification.beep(2); + + +### ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* iOS +* Tizen +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +### 아마존 화재 OS ë‹¨ì  + +* 아마존 화재 ìš´ì˜ ì²´ì œ 기본 **설정/ë””ìŠ¤í”Œë ˆì´ ë° ì‚¬ìš´ë“œ** 패ë„ì— ì§€ì • ëœ **알림 소리** ìž¬ìƒ ë©ë‹ˆë‹¤. + +### 안 드 로ì´ë“œ ë‹¨ì  + +* 안 드 로ì´ë“œ 기본 **알림 벨소리** **설정/사운드 ë° ë””ìŠ¤í”Œë ˆì´** 패ë„ì—서 지정 합니다. + +### Windows Phone 7, 8 특수 + +* 코르 ë„ìš° ë°” ë¶„í¬ì—서 ì¼ë°˜ ê²½ê³ ìŒ íŒŒì¼ì— ì˜ì¡´í•©ë‹ˆë‹¤. + +### Tizen 특수 + +* Tizenì€ ë¯¸ë””ì–´ API 통해 오디오 파ì¼ì„ ìž¬ìƒ í•˜ ì—¬ 경고ìŒì„ 구현 합니다. + +* ê²½ê³ ìŒ íŒŒì¼ ì§§ì€ ë˜ì–´ì•¼ 합니다, ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë£¨íŠ¸ ë””ë ‰í„°ë¦¬ì˜ `소리` 하위 ë””ë ‰í„°ë¦¬ì— ìœ„ì¹˜ 해야 합니다 ë° `beep.wav`는 명명 ëœ. diff --git a/plugins/cordova-plugin-dialogs/doc/pl/README.md b/plugins/cordova-plugin-dialogs/doc/pl/README.md new file mode 100644 index 0000000..45fa937 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/pl/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +Ten plugin umożliwia dostÄ™p do niektórych rodzimych okna dialogowego elementy interfejsu użytkownika za poÅ›rednictwem obiektu globalnego `navigator.notification`. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Instalacja + + cordova plugin add cordova-plugin-dialogs + + +## Metody + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +Pokazuje niestandardowe wpisu lub okno dialogowe. WiÄ™kszość implementacji Cordova używać rodzimych okno dialogowe dla tej funkcji, ale niektóre platformy używać przeglÄ…darki `alert` funkcji, która jest zazwyczaj mniej konfigurowalny. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **wiadomość**: komunikat okna dialogowego. *(String)* + + * **alertCallback**: wywoÅ‚anie zwrotne do wywoÅ‚ania, gdy okno dialogowe alert jest oddalona. *(Funkcja)* + + * **tytuÅ‚**: okno tytuÅ‚. *(String)* (Opcjonalna, domyÅ›lnie`Alert`) + + * **buttonName**: Nazwa przycisku. *(String)* (Opcjonalna, domyÅ›lnie`OK`) + +### PrzykÅ‚ad + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### Windows Phone 7 i 8 dziwactwa + + * Istnieje wpis nie wbudowana przeglÄ…darka, ale można powiÄ…zać w nastÄ™pujÄ…cy sposób na wywoÅ‚anie `alert()` w globalnym zasiÄ™gu: + + window.alert = navigator.notification.alert; + + + * Zarówno `alert` i `confirm` sÄ… bez blokowania połączeÅ„, których wyniki sÄ… tylko dostÄ™pne asynchronicznie. + +### Firefox OS dziwactwa: + +DostÄ™pne sÄ… zarówno rodzimych blokuje `window.alert()` i bez blokowania `navigator.notification.alert()`. + +### Jeżyna 10 dziwactwa + +parametr wywoÅ‚ania zwrotnego `Navigator.Notification.alert ("tekst", wywoÅ‚anie zwrotne, 'tytuÅ‚', 'tekst')` jest przekazywana numer 1. + +## navigator.notification.confirm + +WyÅ›wietla okno dialogowe potwierdzenia konfigurowalny. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **wiadomość**: komunikat okna dialogowego. *(String)* + + * **confirmCallback**: wywoÅ‚anie zwrotne do wywoÅ‚ania z indeksu z przycisku (1, 2 lub 3), lub gdy okno jest zwolniony bez naciÅ›nij przycisk (0). *(Funkcja)* + + * **tytuÅ‚**: okno tytuÅ‚. *(String)* (Opcjonalna, domyÅ›lnie`Confirm`) + + * **buttonLabels**: tablica ciÄ…gów, okreÅ›lajÄ…c etykiety przycisków. *(Tablica)* (Opcjonalna, domyÅ›lnie [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback` wykonuje, gdy użytkownik naciÅ›nie klawisz jeden z przycisków w oknie dialogowym potwierdzenia. + +WywoÅ‚anie zwrotne wymaga argumentu `buttonIndex` *(numer)*, który jest indeksem wciÅ›niÄ™ty przycisk. Należy zauważyć, że indeks używa, na podstawie jednego indeksowania, wiÄ™c wartoÅ›ciÄ… jest `1`, `2`, `3` itd. + +### PrzykÅ‚ad + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### Windows Phone 7 i 8 dziwactwa + + * Istnieje funkcja wbudowana przeglÄ…darka nie `window.confirm` , ale można go powiÄ…zać przypisujÄ…c: + + window.confirm = navigator.notification.confirm; + + + * Wzywa do `alert` i `confirm` sÄ… bez blokowania, wiÄ™c wynik jest tylko dostÄ™pnych asynchronicznie. + +### Windows dziwactwa + + * Na Windows8/8.1 to nie można dodać wiÄ™cej niż trzy przyciski do instancji MessageDialog. + + * Na Windows Phone 8.1 nie jest możliwe wyÅ›wietlić okno dialogowe z wiÄ™cej niż dwoma przyciskami. + +### Firefox OS dziwactwa: + +DostÄ™pne sÄ… zarówno rodzimych blokuje `window.confirm()` i bez blokowania `navigator.notification.confirm()`. + +## navigator.notification.prompt + +WyÅ›wietla okno dialogowe macierzystego, który bardziej niż przeglÄ…darki `prompt` funkcja. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **wiadomość**: komunikat okna dialogowego. *(String)* + + * **promptCallback**: wywoÅ‚anie zwrotne do wywoÅ‚ania z indeksu z przycisku (1, 2 lub 3), lub gdy okno jest zwolniony bez naciÅ›nij przycisk (0). *(Funkcja)* + + * **title**: okno tytuÅ‚ *(String)* (opcjonalna, domyÅ›lnie `polecenia`) + + * **buttonLabels**: tablica ciÄ…gów, okreÅ›lajÄ…c przycisk etykiety *(tablica)* (opcjonalna, domyÅ›lnie `["OK", "Anuluj"]`) + + * **defaultText**: domyÅ›lnie pole tekstowe wprowadzania wartoÅ›ci (`String`) (opcjonalna, domyÅ›lnie: pusty ciÄ…g) + +### promptCallback + +`promptCallback` wykonuje, gdy użytkownik naciÅ›nie klawisz jeden z przycisków w oknie dialogowym polecenia. Obiektu `results` przekazane do wywoÅ‚ania zwrotnego zawiera nastÄ™pujÄ…ce wÅ‚aÅ›ciwoÅ›ci: + + * **buttonIndex**: indeks wciÅ›niÄ™ty przycisk. *(Liczba)* Należy zauważyć, że indeks używa, na podstawie jednego indeksowania, wiÄ™c wartoÅ›ciÄ… jest `1`, `2`, `3` itd. + + * **input1**: Tekst wprowadzony w oknie polecenia. *(String)* + +### PrzykÅ‚ad + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * Firefox OS + * iOS + * Windows Phone 7 i 8 + * Windows 8 + * Windows + +### Dziwactwa Androida + + * Android obsÅ‚uguje maksymalnie trzy przyciski i wiÄ™cej niż to ignoruje. + + * Android 3.0 i nowszych przyciski sÄ… wyÅ›wietlane w kolejnoÅ›ci odwrotnej do urzÄ…dzenia, które używajÄ… tematu Holo. + +### Windows dziwactwa + + * W systemie Windows wierzyciel okno jest oparte na jÄ™zyku html, ze wzglÄ™du na brak takich natywnego api. + +### Firefox OS dziwactwa: + +DostÄ™pne sÄ… zarówno rodzimych blokuje `window.prompt()` i bez blokowania `navigator.notification.prompt()`. + +## navigator.notification.beep + +UrzÄ…dzenie odtwarza sygnaÅ‚ ciÄ…gÅ‚y dźwiÄ™k. + + navigator.notification.beep(times); + + + * **times**: liczba powtórzeÅ„ po sygnale. *(Liczba)* + +### PrzykÅ‚ad + + // Beep twice! + navigator.notification.beep(2); + + +### ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * iOS + * Tizen + * Windows Phone 7 i 8 + * Windows 8 + +### Amazon ogieÅ„ OS dziwactwa + + * Amazon ogieÅ„ OS gra domyÅ›lny **DźwiÄ™k powiadomienia** okreÅ›lone w panelu **ekranu/ustawienia i dźwiÄ™k**. + +### Dziwactwa Androida + + * Android gra domyÅ›lnie **dzwonek powiadomienia** okreÅ›lone w panelu **ustawieÅ„/dźwiÄ™k i wyÅ›wietlacz**. + +### Windows Phone 7 i 8 dziwactwa + + * Opiera siÄ™ na pliku rodzajowego sygnaÅ‚ z rozkÅ‚adu Cordova. + +### Dziwactwa Tizen + + * Tizen implementuje dźwiÄ™ków przez odtwarzania pliku audio za poÅ›rednictwem mediów API. + + * Plik dźwiÄ™kowy muszÄ… być krótkie, musi znajdować siÄ™ w podkatalogu `dźwiÄ™ki` w katalogu głównym aplikacji i musi być o nazwie `beep.wav`. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/pl/index.md b/plugins/cordova-plugin-dialogs/doc/pl/index.md new file mode 100644 index 0000000..462d5ac --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/pl/index.md @@ -0,0 +1,273 @@ + + +# cordova-plugin-dialogs + +Ten plugin umożliwia dostÄ™p do niektórych rodzimych okna dialogowego elementy interfejsu użytkownika za poÅ›rednictwem obiektu globalnego `navigator.notification`. + +Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dostÄ™pne dopiero po zdarzeniu `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## Instalacja + + cordova plugin add cordova-plugin-dialogs + + +## Metody + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +Pokazuje niestandardowe wpisu lub okno dialogowe. WiÄ™kszość implementacji Cordova używać rodzimych okno dialogowe dla tej funkcji, ale niektóre platformy używać przeglÄ…darki `alert` funkcji, która jest zazwyczaj mniej konfigurowalny. + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + +* **wiadomość**: komunikat okna dialogowego. *(String)* + +* **alertCallback**: wywoÅ‚anie zwrotne do wywoÅ‚ania, gdy okno dialogowe alert jest oddalona. *(Funkcja)* + +* **tytuÅ‚**: okno tytuÅ‚. *(String)* (Opcjonalna, domyÅ›lnie`Alert`) + +* **buttonName**: Nazwa przycisku. *(String)* (Opcjonalna, domyÅ›lnie`OK`) + +### PrzykÅ‚ad + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### Windows Phone 7 i 8 dziwactwa + +* Istnieje wpis nie wbudowana przeglÄ…darka, ale można powiÄ…zać w nastÄ™pujÄ…cy sposób na wywoÅ‚anie `alert()` w globalnym zasiÄ™gu: + + window.alert = navigator.notification.alert; + + +* Zarówno `alert` i `confirm` sÄ… bez blokowania połączeÅ„, których wyniki sÄ… tylko dostÄ™pne asynchronicznie. + +### Firefox OS dziwactwa: + +DostÄ™pne sÄ… zarówno rodzimych blokuje `window.alert()` i bez blokowania `navigator.notification.alert()`. + +### Jeżyna 10 dziwactwa + +parametr wywoÅ‚ania zwrotnego `Navigator.Notification.alert ("tekst", wywoÅ‚anie zwrotne, 'tytuÅ‚', 'tekst')` jest przekazywana numer 1. + +## navigator.notification.confirm + +WyÅ›wietla okno dialogowe potwierdzenia konfigurowalny. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **wiadomość**: komunikat okna dialogowego. *(String)* + +* **confirmCallback**: wywoÅ‚anie zwrotne do wywoÅ‚ania z indeksu z przycisku (1, 2 lub 3), lub gdy okno jest zwolniony bez naciÅ›nij przycisk (0). *(Funkcja)* + +* **tytuÅ‚**: okno tytuÅ‚. *(String)* (Opcjonalna, domyÅ›lnie`Confirm`) + +* **buttonLabels**: tablica ciÄ…gów, okreÅ›lajÄ…c etykiety przycisków. *(Tablica)* (Opcjonalna, domyÅ›lnie [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback` wykonuje, gdy użytkownik naciÅ›nie klawisz jeden z przycisków w oknie dialogowym potwierdzenia. + +WywoÅ‚anie zwrotne wymaga argumentu `buttonIndex` *(numer)*, który jest indeksem wciÅ›niÄ™ty przycisk. Należy zauważyć, że indeks używa, na podstawie jednego indeksowania, wiÄ™c wartoÅ›ciÄ… jest `1`, `2`, `3` itd. + +### PrzykÅ‚ad + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### Windows Phone 7 i 8 dziwactwa + +* Istnieje funkcja wbudowana przeglÄ…darka nie `window.confirm` , ale można go powiÄ…zać przypisujÄ…c: + + window.confirm = navigator.notification.confirm; + + +* Wzywa do `alert` i `confirm` sÄ… bez blokowania, wiÄ™c wynik jest tylko dostÄ™pnych asynchronicznie. + +### Windows dziwactwa + +* Na Windows8/8.1 to nie można dodać wiÄ™cej niż trzy przyciski do instancji MessageDialog. + +* Na Windows Phone 8.1 nie jest możliwe wyÅ›wietlić okno dialogowe z wiÄ™cej niż dwoma przyciskami. + +### Firefox OS dziwactwa: + +DostÄ™pne sÄ… zarówno rodzimych blokuje `window.confirm()` i bez blokowania `navigator.notification.confirm()`. + +## navigator.notification.prompt + +WyÅ›wietla okno dialogowe macierzystego, który bardziej niż przeglÄ…darki `prompt` funkcja. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **message**: komunikat okna dialogowego. *(String)* + +* **promptCallback**: wywoÅ‚anie zwrotne do wywoÅ‚ania z indeksu z przycisku (1, 2 lub 3), lub gdy okno jest zwolniony bez naciÅ›nij przycisk (0). *(Funkcja)* + +* **title**: okno tytuÅ‚ *(String)* (opcjonalna, domyÅ›lnie `polecenia`) + +* **buttonLabels**: tablica ciÄ…gów, okreÅ›lajÄ…c przycisk etykiety *(tablica)* (opcjonalna, domyÅ›lnie `["OK", "Anuluj"]`) + +* **defaultText**: domyÅ›lnie pole tekstowe wprowadzania wartoÅ›ci (`String`) (opcjonalna, domyÅ›lnie: pusty ciÄ…g) + +### promptCallback + +`promptCallback` wykonuje, gdy użytkownik naciÅ›nie klawisz jeden z przycisków w oknie dialogowym polecenia. Obiektu `results` przekazane do wywoÅ‚ania zwrotnego zawiera nastÄ™pujÄ…ce wÅ‚aÅ›ciwoÅ›ci: + +* **buttonIndex**: indeks wciÅ›niÄ™ty przycisk. *(Liczba)* Należy zauważyć, że indeks używa, na podstawie jednego indeksowania, wiÄ™c wartoÅ›ciÄ… jest `1`, `2`, `3` itd. + +* **input1**: Tekst wprowadzony w oknie polecenia. *(String)* + +### PrzykÅ‚ad + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* Firefox OS +* iOS +* Windows Phone 7 i 8 +* Windows 8 +* Windows + +### Dziwactwa Androida + +* Android obsÅ‚uguje maksymalnie trzy przyciski i wiÄ™cej niż to ignoruje. + +* Android 3.0 i nowszych przyciski sÄ… wyÅ›wietlane w kolejnoÅ›ci odwrotnej do urzÄ…dzenia, które używajÄ… tematu Holo. + +### Windows dziwactwa + +* W systemie Windows wierzyciel okno jest oparte na jÄ™zyku html, ze wzglÄ™du na brak takich natywnego api. + +### Firefox OS dziwactwa: + +DostÄ™pne sÄ… zarówno rodzimych blokuje `window.prompt()` i bez blokowania `navigator.notification.prompt()`. + +## navigator.notification.beep + +UrzÄ…dzenie odtwarza sygnaÅ‚ ciÄ…gÅ‚y dźwiÄ™k. + + navigator.notification.beep(times); + + +* **times**: liczba powtórzeÅ„ po sygnale. *(Liczba)* + +### PrzykÅ‚ad + + // Beep twice! + navigator.notification.beep(2); + + +### ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 i 8 +* Windows 8 + +### Amazon ogieÅ„ OS dziwactwa + +* Amazon ogieÅ„ OS gra domyÅ›lny **DźwiÄ™k powiadomienia** okreÅ›lone w panelu **ekranu/ustawienia i dźwiÄ™k**. + +### Dziwactwa Androida + +* Android gra domyÅ›lnie **dzwonek powiadomienia** okreÅ›lone w panelu **ustawieÅ„/dźwiÄ™k i wyÅ›wietlacz**. + +### Windows Phone 7 i 8 dziwactwa + +* Opiera siÄ™ na pliku rodzajowego sygnaÅ‚ z rozkÅ‚adu Cordova. + +### Dziwactwa Tizen + +* Tizen implementuje dźwiÄ™ków przez odtwarzania pliku audio za poÅ›rednictwem mediów API. + +* Plik dźwiÄ™kowy muszÄ… być krótkie, musi znajdować siÄ™ w podkatalogu `dźwiÄ™ki` w katalogu głównym aplikacji i musi być o nazwie `beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/doc/ru/index.md b/plugins/cordova-plugin-dialogs/doc/ru/index.md new file mode 100644 index 0000000..49474ea --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/ru/index.md @@ -0,0 +1,247 @@ + + +# cordova-plugin-dialogs + +Этот плагин обеÑпечивает доÑтуп к некоторым Ñлементам ÑобÑтвенного диалогового окна пользовательÑкого интерфейÑа. + +## УÑтановка + + cordova plugin add cordova-plugin-dialogs + + +## Методы + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +Показывает окно пользовательÑкие Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ диалоговое окно. БольшинÑтво реализаций Cordova иÑпользовать диалоговое окно родной Ð´Ð»Ñ Ñтой функции, но некоторые платформы браузера `alert` функциÑ, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐºÐ°Ðº правило менее наÑтраиваетÑÑ. + + Navigator.Notification.Alert (Ñообщение, alertCallback, [название], [buttonName]) + + +* **Ñообщение**: Ñообщение диалога. *(Строка)* + +* **alertCallback**: обратного вызова Ð´Ð»Ñ Ð²Ñ‹Ð·Ð¾Ð²Ð°, когда закрываетÑÑ Ð´Ð¸Ð°Ð»Ð¾Ð³Ð¾Ð²Ð¾Ðµ окно оповещениÑ. *(ФункциÑ)* + +* **название**: диалоговое окно название. *(Строка)* (Опционально, по умолчанию`Alert`) + +* **buttonName**: Ð¸Ð¼Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸. *(Строка)* (Опционально, по умолчанию`OK`) + +### Пример + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### ОÑобенноÑти Windows Phone 7 и 8 + +* СущеÑтвует предупреждение не вÑтроенный браузер, но можно привÑзать один Ñледующим позвонить `alert()` в глобальной облаÑти дейÑтвиÑ: + + window.alert = navigator.notification.alert; + + +* Оба `alert` и `confirm` ÑвлÑÑŽÑ‚ÑÑ Ð½Ðµ блокировка звонков, результаты которых доÑтупны только аÑинхронно. + +### Firefox OS причуды: + +Как родной блокировка `window.alert()` и неблокирующий `navigator.notification.alert()` доÑтупны. + +## navigator.notification.confirm + +Отображает диалоговое окно ÐаÑтраиваемый подтверждениÑ. + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **Ñообщение**: Ñообщение диалога. *(Строка)* + +* **confirmCallback**: обратного вызова Ñ Ð¸Ð½Ð´ÐµÐºÑом кнопка нажата (1, 2 или 3) или когда диалоговое окно закрываетÑÑ Ð±ÐµÐ· Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ (0). *(ФункциÑ)* + +* **название**: диалоговое окно название. *(Строка)* (Опционально, по умолчанию`Confirm`) + +* **buttonLabels**: маÑÑив Ñтрок, указав Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ ÐºÐ½Ð¾Ð¿Ð¾Ðº. *(МаÑÑив)* (Ðе обÑзательно, по умолчанию [ `OK,Cancel` ]) + +### confirmCallback + +`confirmCallback`ВыполнÑетÑÑ, когда пользователь нажимает одну из кнопок в диалоговом окне подтверждениÑ. + +Ðргументом функции обратного вызова `buttonIndex` *(номер)*, который ÑвлÑетÑÑ Ð¸Ð½Ð´ÐµÐºÑ Ð½Ð°Ð¶Ð°Ñ‚Ð¾Ð¹ кнопки. Обратите внимание, что Ð¸Ð½Ð´ÐµÐºÑ Ð¸Ñпользует единицы индекÑации, поÑтому значение `1` , `2` , `3` , и Ñ‚.д. + +### Пример + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### ОÑобенноÑти Windows Phone 7 и 8 + +* Ðет вÑтроенного браузера Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð´Ð»Ñ `window.confirm` , но его можно привÑзать путем приÑвоениÑ: + + window.confirm = navigator.notification.confirm; + + +* Вызовы `alert` и `confirm` ÑвлÑÑŽÑ‚ÑÑ Ð½Ðµ блокируетÑÑ, поÑтому результат доÑтупен только аÑинхронно. + +### Firefox OS причуды: + +Как родной блокировка `window.confirm()` и неблокирующий `navigator.notification.confirm()` доÑтупны. + +## navigator.notification.prompt + +Отображает родной диалоговое окно более наÑтраиваемый, чем в браузере `prompt` функции. + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **Ñообщение**: Ñообщение диалога. *(Строка)* + +* **promptCallback**: обратного вызова Ñ Ð¸Ð½Ð´ÐµÐºÑом кнопка нажата (1, 2 или 3) или когда диалоговое окно закрываетÑÑ Ð±ÐµÐ· Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ (0). *(ФункциÑ)* + +* **название**: диалоговое окно название *(String)* (опционально, по умолчанию`Prompt`) + +* **buttonLabels**: маÑÑив Ñтрок, указав кнопку Ñтикетки *(маÑÑив)* (опционально, по умолчанию`["OK","Cancel"]`) + +* **defaultText**: по умолчанию textbox входное значение ( `String` ) (опционально, по умолчанию: пуÑÑ‚Ð°Ñ Ñтрока) + +### promptCallback + +`promptCallback`ВыполнÑетÑÑ, когда пользователь нажимает одну из кнопок в диалоговом окне приглашениÑ. `results`Объект, переданный в метод обратного вызова Ñодержит Ñледующие ÑвойÑтва: + +* **buttonIndex**: Ð¸Ð½Ð´ÐµÐºÑ Ð½Ð°Ð¶Ð°Ñ‚Ð¾Ð¹ кнопки. *(ЧиÑло)* Обратите внимание, что Ð¸Ð½Ð´ÐµÐºÑ Ð¸Ñпользует единицы индекÑации, поÑтому значение `1` , `2` , `3` , и Ñ‚.д. + +* **INPUT1**: текÑÑ‚, введенный в диалоговом окне приглашениÑ. *(Строка)* + +### Пример + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* Firefox OS +* iOS +* Windows Phone 7 и 8 + +### ОÑобенноÑти Android + +* Android поддерживает макÑимум из трех кнопок и игнорирует больше, чем Ñто. + +* Ðа Android 3.0 и более поздних верÑиÑÑ… кнопки отображаютÑÑ Ð² обратном порÑдке Ð´Ð»Ñ ÑƒÑтройÑтв, которые иÑпользуют тему холо. + +### Firefox OS причуды: + +Как родной блокировка `window.prompt()` и неблокирующий `navigator.notification.prompt()` доÑтупны. + +## navigator.notification.beep + +УÑтройÑтво воÑпроизводит звуковой Ñигнал звук. + + navigator.notification.beep(times); + + +* **раз**: количеÑтво раз, чтобы повторить Ñигнал. *(ЧиÑло)* + +### Пример + + // Beep twice! + navigator.notification.beep(2); + + +### Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Tizen +* Windows Phone 7 и 8 +* Windows 8 + +### ОÑобенноÑти Amazon Fire OS + +* Amazon Fire OS играет по умолчанию **Звук уведомлениÑ** , указанного на панели **параметров/диÑплей и звук** . + +### ОÑобенноÑти Android + +* Android играет по умолчанию **ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ñ€Ð¸Ð½Ð³Ñ‚Ð¾Ð½** указанных в панели **наÑтройки/звук и диÑплей** . + +### ОÑобенноÑти Windows Phone 7 и 8 + +* ОпираетÑÑ Ð½Ð° общий звуковой файл из диÑтрибутива Кордова. + +### ОÑобенноÑти Tizen + +* Tizen реализует гудков, воÑпроизведении аудиофайла через СМИ API. + +* Звуковой файл должен быть коротким, должен быть раÑположен в `sounds` подкаталог корневого каталога Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ должны быть названы`beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/doc/zh/README.md b/plugins/cordova-plugin-dialogs/doc/zh/README.md new file mode 100644 index 0000000..c8c26c3 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/zh/README.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-dialogs + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-dialogs.svg)](https://travis-ci.org/apache/cordova-plugin-dialogs) + +é€™å€‹å¤–æŽ›ç¨‹å¼æä¾›å°ä¸€äº›æœ¬æ©Ÿå°è©±æ–¹å¡Šä½¿ç”¨è€…介é¢å…ƒç´ ï¼Œé€šéŽå…¨çƒ `navigator.notification` 物件的訪å•。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-dialogs + + +## 方法 + + * `navigator.notification.alert` + * `navigator.notification.confirm` + * `navigator.notification.prompt` + * `navigator.notification.beep` + +## navigator.notification.alert + +顯示一個自訂的警報或å°è©±æ–¹å¡Šæ¡†ã€‚ 大多數的科爾多瓦實ç¾ä½¿ç”¨æœ¬æ©Ÿçš„å°è©±æ–¹å¡Šç‚ºæ­¤åŠŸèƒ½ï¼Œä½†æŸäº›å¹³è‡ºä¸Šä½¿ç”¨ç€è¦½å™¨çš„ `alert` 功能,這是通常ä¸é‚£éº¼å¯è‡ªè¨‚。 + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + + * **message**: 消æ¯å°è©±æ–¹å¡Šã€‚*(String)* + + * **alertCallback**: 當警報å°è©±æ–¹å¡Šçš„被解雇時è¦èª¿ç”¨çš„回檔。*(函數)* + + * **title**: 標題å°è©±æ–¹å¡Šã€‚*(String)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`Alert`) + + * **buttonName**: 按鈕å稱。*(字串)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`OK`) + +### 示例 + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### Windows Phone 7 å’Œ 8 怪癖 + + * 有沒有內置ç€è¦½å™¨è­¦å ±ï¼Œä½†ä½ å¯ä»¥ç¶å®šä¸€å€‹ï¼Œå¦‚下所示調用 `alert()` 在全çƒç¯„åœå…§ï¼š + + window.alert = navigator.notification.alert; + + + * 兩個 `alert` å’Œ `confirm` çš„éžé˜»å¡žçš„èª¿ç”¨ï¼Œå…¶ä¸­çš„çµæžœæ‰æ˜¯å¯ç”¨çš„éžåŒæ­¥ã€‚ + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±æ€ªç™–: + +本機阻止 `window.alert()` å’Œéžé˜»å¡žçš„ `navigator.notification.alert()` 都å¯ã€‚ + +### 黑莓 10 怪癖 + +`navigator.notification.alert ('message'〠confirmCallback〠'title'〠'buttonLabels')` å›žæª”åƒæ•¸è¢«å‚³éžçš„æ•¸ä½ 1。 + +## navigator.notification.confirm + +顯示一個å¯è‡ªè¨‚的確èªå°è©±æ–¹å¡Šã€‚ + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + + * **message**: 消æ¯å°è©±æ–¹å¡Šã€‚*(String)* + + * **confirmCallback**: è¦ç”¨ç´¢å¼• (1〠2 或 3) 按下的按鈕,或者在沒有按下按鈕 (0) é§å›žäº†å°è©±æ–¹å¡Šä¸­æ™‚調用的回檔。*(函數)* + + * **title**: 標題å°è©±æ–¹å¡Šã€‚*(字串)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`Confirm`) + + * **buttonLabels**: 指定按鈕標籤的字串陣列。*(陣列)*(å¯é¸ï¼Œé è¨­å€¼ç‚º [ `OK,Cancel` ]) + +### confirmCallback + +當使用者按下確èªå°è©±æ–¹å¡Šä¸­çš„æŒ‰éˆ•之一時,將執行 `confirmCallback`。 + +回檔需è¦åƒæ•¸ `buttonIndex` *(編號)*ï¼Œå³æŒ‰ä¸‹çš„æŒ‰éˆ•的索引。 請注æ„索引使用一個基於索引,因此值 `1`〠`2`〠`3` 等。 + +### 示例 + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### Windows Phone 7 å’Œ 8 怪癖 + + * 有沒有內置的ç€è¦½å™¨åŠŸèƒ½çš„ `window.confirm` ,但你å¯ä»¥å°‡å®ƒç¶å®šé€šéŽåˆ†é…: + + window.confirm = navigator.notification.confirm; + + + * 調用到 `alert` å’Œ `confirm` çš„éžé˜»å¡žï¼Œæ‰€ä»¥çµæžœå°±æ˜¯åªå¯ç”¨ä»¥éžåŒæ­¥æ–¹å¼ã€‚ + +### Windows 的怪癖 + + * 在 Windows8/8.1 它是ä¸å¯èƒ½å°‡è¶…éŽä¸‰å€‹æŒ‰éˆ•添加到 MessageDialog 實例。 + + * 在 Windows Phone 8.1 它是ä¸å¯èƒ½é¡¯ç¤ºæœ‰è¶…éŽå…©å€‹æŒ‰éˆ•çš„å°è©±æ–¹å¡Šã€‚ + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±æ€ªç™–: + +本機阻止 `window.confirm()` å’Œéžé˜»å¡žçš„ `navigator.notification.confirm()` 都å¯ã€‚ + +## navigator.notification.prompt + +顯示本機的å°è©±æ–¹å¡Šï¼Œæ˜¯å¯å®šåˆ¶çš„æ¯”ç€è¦½å™¨çš„ `prompt` 功能。 + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + + * **message**: 消æ¯å°è©±æ–¹å¡Šã€‚*(String)* + + * **promptCallback**: è¦ç”¨æŒ‡æ•¸ (1〠2 或 3) 按下的按鈕或å°è©±æ–¹å¡Šä¸­è§£é›‡ç„¡ (0) 按下一個按鈕時調用的回檔。*(函數)* + + * **title**: 標題å°è©±æ–¹å¡Šã€‚*(String)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`Alert`) + + * **buttonLabels**: 指定按鈕標籤 (å¯é¸ï¼Œé è¨­å€¼ç‚º `["OK","Cancel"]` *(陣列)* 的字串陣列) + + * **defaultText**: é è¨­æ–‡å­—方塊中輸入值 (`字串`) (å¯é¸ï¼Œé è¨­å€¼ï¼š 空字串) + +### promptCallback + +當使用者按下其中一個æç¤ºå°è©±æ–¹å¡Šä¸­çš„æŒ‰éˆ•時,將執行 `promptCallback`。傳éžçµ¦å›žæª”çš„ `results` 物件包å«ä»¥ä¸‹å±¬æ€§ï¼š + + * **buttonIndex**: 按下的按鈕的索引。*(數)*請注æ„索引使用一個基於索引,因此值 `1`〠`2`〠`3` 等。 + + * **input1**: 在æç¤ºå°è©±æ–¹å¡Šä¸­è¼¸å…¥çš„æ–‡æœ¬ã€‚*(字串)* + +### 示例 + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + +### Android 的怪癖 + + * Android æ”¯æ´æœ€å¤šçš„三個按鈕,並忽略任何更多。 + + * 在 Android 3.0 åŠæ›´é«˜ç‰ˆæœ¬ï¼Œä½¿ç”¨å…¨æ¯ä¸»é¡Œçš„設備以相å的順åºé¡¯ç¤ºæŒ‰éˆ•。 + +### Windows 的怪癖 + + * 在 Windows 上æç¤ºå°è©±æ–¹å¡Šæ˜¯åŸºæ–¼ html 的缺ä¹é€™ç¨®æœ¬æ©Ÿ api。 + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±æ€ªç™–: + +本機阻止 `window.prompt()` å’Œéžé˜»å¡žçš„ `navigator.notification.prompt()` 都å¯ã€‚ + +## navigator.notification.beep + +該設備播放æç¤ºéŸ³çš„è²éŸ³ã€‚ + + navigator.notification.beep(times); + + + * **beep**: 次數é‡è¤‡åœ¨å—¶å—¶è²ã€‚*(數)* + +### 示例 + + // Beep twice! + navigator.notification.beep(2); + + +### 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * iOS + * Tizen + * Windows Phone 7 å’Œ 8 + * Windows 8 + +### 亞馬éœç« OS 怪癖 + + * 亞馬éœç« OS 播放é è¨­ **設置/顯示和è²éŸ³** æ¿ä¸‹æŒ‡å®šçš„ **通知è²éŸ³**。 + +### Android 的怪癖 + + * 安å“系統播放é è¨­ **通知鈴è²** **設置/è²éŸ³å’Œé¡¯ç¤º** 颿¿ä¸‹æŒ‡å®šã€‚ + +### Windows Phone 7 å’Œ 8 怪癖 + + * ä¾è³´äºŽæ³›åž‹èœ‚鳴音檔從科爾多瓦分佈。 + +### Tizen 怪癖 + + * Tizen é€šéŽæ’­æ”¾éŸ³è¨Šæª”通éŽåª’é«” API 實ç¾çš„蜂鳴è²ã€‚ + + * èœ‚é³´éŸ³æª”å¿…é ˆå¾ˆçŸ­ï¼Œå¿…é ˆä½æ–¼æ‡‰ç”¨ç¨‹å¼çš„æ ¹ç›®éŒ„中,一個 `è²éŸ³` å­ç›®éŒ„和必須將命å為 `beep.wav`. \ No newline at end of file diff --git a/plugins/cordova-plugin-dialogs/doc/zh/index.md b/plugins/cordova-plugin-dialogs/doc/zh/index.md new file mode 100644 index 0000000..b47fc5f --- /dev/null +++ b/plugins/cordova-plugin-dialogs/doc/zh/index.md @@ -0,0 +1,273 @@ + + +# cordova-plugin-dialogs + +é€™å€‹å¤–æŽ›ç¨‹å¼æä¾›å°ä¸€äº›æœ¬æ©Ÿå°è©±æ–¹å¡Šä½¿ç”¨è€…介é¢å…ƒç´ ï¼Œé€šéŽå…¨çƒ `navigator.notification` 物件的訪å•。 + +雖然該物件附加到全çƒç¯„åœå…§ `導航器`,它ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(navigator.notification); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-dialogs + + +## 方法 + +* `navigator.notification.alert` +* `navigator.notification.confirm` +* `navigator.notification.prompt` +* `navigator.notification.beep` + +## navigator.notification.alert + +顯示一個自訂的警報或å°è©±æ–¹å¡Šæ¡†ã€‚ 大多數的科爾多瓦實ç¾ä½¿ç”¨æœ¬æ©Ÿçš„å°è©±æ–¹å¡Šç‚ºæ­¤åŠŸèƒ½ï¼Œä½†æŸäº›å¹³è‡ºä¸Šä½¿ç”¨ç€è¦½å™¨çš„ `alert` 功能,這是通常ä¸é‚£éº¼å¯è‡ªè¨‚。 + + navigator.notification.alert(message, alertCallback, [title], [buttonName]) + + +* **message**: 消æ¯å°è©±æ–¹å¡Šã€‚*(String)* + +* **alertCallback**: 當警報å°è©±æ–¹å¡Šçš„被解雇時è¦èª¿ç”¨çš„回檔。*(函數)* + +* **title**: 標題å°è©±æ–¹å¡Šã€‚*(String)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`Alert`) + +* **buttonName**: 按鈕å稱。*(字串)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`OK`) + +### 示例 + + function alertDismissed() { + // do something + } + + navigator.notification.alert( + 'You are the winner!', // message + alertDismissed, // callback + 'Game Over', // title + 'Done' // buttonName + ); + + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± +* iOS +* Tizen +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### Windows Phone 7 å’Œ 8 怪癖 + +* 有沒有內置ç€è¦½å™¨è­¦å ±ï¼Œä½†ä½ å¯ä»¥ç¶å®šä¸€å€‹ï¼Œå¦‚下所示調用 `alert()` 在全çƒç¯„åœå…§ï¼š + + window.alert = navigator.notification.alert; + + +* 兩個 `alert` å’Œ `confirm` çš„éžé˜»å¡žçš„èª¿ç”¨ï¼Œå…¶ä¸­çš„çµæžœæ‰æ˜¯å¯ç”¨çš„éžåŒæ­¥ã€‚ + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±æ€ªç™–: + +本機阻止 `window.alert()` å’Œéžé˜»å¡žçš„ `navigator.notification.alert()` 都å¯ã€‚ + +### 黑莓 10 怪癖 + +`navigator.notification.alert ('message'〠confirmCallback〠'title'〠'buttonLabels')` å›žæª”åƒæ•¸è¢«å‚³éžçš„æ•¸ä½ 1。 + +## navigator.notification.confirm + +顯示一個å¯è‡ªè¨‚的確èªå°è©±æ–¹å¡Šã€‚ + + navigator.notification.confirm(message, confirmCallback, [title], [buttonLabels]) + + +* **message**: 消æ¯å°è©±æ–¹å¡Šã€‚*(字串)* + +* **confirmCallback**: è¦ç”¨ç´¢å¼• (1〠2 或 3) 按下的按鈕,或者在沒有按下按鈕 (0) é§å›žäº†å°è©±æ–¹å¡Šä¸­æ™‚調用的回檔。*(函數)* + +* **title**: 標題å°è©±æ–¹å¡Šã€‚*(字串)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`Confirm`) + +* **buttonLabels**: 指定按鈕標籤的字串陣列。*(陣列)*(å¯é¸ï¼Œé è¨­å€¼ç‚º [ `OK,Cancel` ]) + +### confirmCallback + +當使用者按下確èªå°è©±æ–¹å¡Šä¸­çš„æŒ‰éˆ•之一時,將執行 `confirmCallback`。 + +回檔需è¦åƒæ•¸ `buttonIndex` *(編號)*ï¼Œå³æŒ‰ä¸‹çš„æŒ‰éˆ•的索引。 請注æ„索引使用一個基於索引,因此值 `1`〠`2`〠`3` 等。 + +### 示例 + + function onConfirm(buttonIndex) { + alert('You selected button ' + buttonIndex); + } + + navigator.notification.confirm( + 'You are the winner!', // message + onConfirm, // callback to invoke with index of button pressed + 'Game Over', // title + ['Restart','Exit'] // buttonLabels + ); + + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± +* iOS +* Tizen +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### Windows Phone 7 å’Œ 8 怪癖 + +* 有沒有內置的ç€è¦½å™¨åŠŸèƒ½çš„ `window.confirm` ,但你å¯ä»¥å°‡å®ƒç¶å®šé€šéŽåˆ†é…: + + window.confirm = navigator.notification.confirm; + + +* 調用到 `alert` å’Œ `confirm` çš„éžé˜»å¡žï¼Œæ‰€ä»¥çµæžœå°±æ˜¯åªå¯ç”¨ä»¥éžåŒæ­¥æ–¹å¼ã€‚ + +### Windows 的怪癖 + +* 在 Windows8/8.1 它是ä¸å¯èƒ½å°‡è¶…éŽä¸‰å€‹æŒ‰éˆ•添加到 MessageDialog 實例。 + +* 在 Windows Phone 8.1 它是ä¸å¯èƒ½é¡¯ç¤ºæœ‰è¶…éŽå…©å€‹æŒ‰éˆ•çš„å°è©±æ–¹å¡Šã€‚ + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±æ€ªç™–: + +本機阻止 `window.confirm()` å’Œéžé˜»å¡žçš„ `navigator.notification.confirm()` 都å¯ã€‚ + +## navigator.notification.prompt + +顯示本機的å°è©±æ–¹å¡Šï¼Œæ˜¯å¯å®šåˆ¶çš„æ¯”ç€è¦½å™¨çš„ `prompt` 功能。 + + navigator.notification.prompt(message, promptCallback, [title], [buttonLabels], [defaultText]) + + +* **message**: 消æ¯å°è©±æ–¹å¡Šã€‚*(String)* + +* **promptCallback**: è¦ç”¨æŒ‡æ•¸ (1〠2 或 3) 按下的按鈕或å°è©±æ–¹å¡Šä¸­è§£é›‡ç„¡ (0) 按下一個按鈕時調用的回檔。*(函數)* + +* **title**: 標題å°è©±æ–¹å¡Šã€‚*(String)*(å¯é¸ï¼Œé è¨­å€¼ç‚º`Alert`) + +* **buttonLabels**: 指定按鈕標籤 (å¯é¸ï¼Œé è¨­å€¼ç‚º `["OK","Cancel"]` *(陣列)* 的字串陣列) + +* **defaultText**: é è¨­æ–‡å­—方塊中輸入值 (`字串`) (å¯é¸ï¼Œé è¨­å€¼ï¼š 空字串) + +### promptCallback + +當使用者按下其中一個æç¤ºå°è©±æ–¹å¡Šä¸­çš„æŒ‰éˆ•時,將執行 `promptCallback`。傳éžçµ¦å›žæª”çš„ `results` 物件包å«ä»¥ä¸‹å±¬æ€§ï¼š + +* **buttonIndex**: 按下的按鈕的索引。*(數)*請注æ„索引使用一個基於索引,因此值 `1`〠`2`〠`3` 等。 + +* **input1**: 在æç¤ºå°è©±æ–¹å¡Šä¸­è¼¸å…¥çš„æ–‡æœ¬ã€‚*(字串)* + +### 示例 + + function onPrompt(results) { + alert("You selected button number " + results.buttonIndex + " and entered " + results.input1); + } + + navigator.notification.prompt( + 'Please enter your name', // message + onPrompt, // callback to invoke + 'Registration', // title + ['Ok','Exit'], // buttonLabels + 'Jane Doe' // defaultText + ); + + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± +* iOS +* Windows Phone 7 å’Œ 8 +* Windows 8 +* Windows + +### Android 的怪癖 + +* Android æ”¯æ´æœ€å¤šçš„三個按鈕,並忽略任何更多。 + +* 在 Android 3.0 åŠæ›´é«˜ç‰ˆæœ¬ï¼Œä½¿ç”¨å…¨æ¯ä¸»é¡Œçš„設備以相å的順åºé¡¯ç¤ºæŒ‰éˆ•。 + +### Windows 的怪癖 + +* 在 Windows 上æç¤ºå°è©±æ–¹å¡Šæ˜¯åŸºæ–¼ html 的缺ä¹é€™ç¨®æœ¬æ©Ÿ api。 + +### ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±æ€ªç™–: + +本機阻止 `window.prompt()` å’Œéžé˜»å¡žçš„ `navigator.notification.prompt()` 都å¯ã€‚ + +## navigator.notification.beep + +該設備播放æç¤ºéŸ³çš„è²éŸ³ã€‚ + + navigator.notification.beep(times); + + +* **beep**: 次數é‡è¤‡åœ¨å—¶å—¶è²ã€‚*(數)* + +### 示例 + + // Beep twice! + navigator.notification.beep(2); + + +### 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* iOS +* Tizen +* Windows Phone 7 å’Œ 8 +* Windows 8 + +### 亞馬éœç« OS 怪癖 + +* 亞馬éœç« OS 播放é è¨­ **設置/顯示和è²éŸ³** æ¿ä¸‹æŒ‡å®šçš„ **通知è²éŸ³**。 + +### Android 的怪癖 + +* 安å“系統播放é è¨­ **通知鈴è²** **設置/è²éŸ³å’Œé¡¯ç¤º** 颿¿ä¸‹æŒ‡å®šã€‚ + +### Windows Phone 7 å’Œ 8 怪癖 + +* ä¾è³´äºŽæ³›åž‹èœ‚鳴音檔從科爾多瓦分佈。 + +### Tizen 怪癖 + +* Tizen é€šéŽæ’­æ”¾éŸ³è¨Šæª”通éŽåª’é«” API 實ç¾çš„蜂鳴è²ã€‚ + +* èœ‚é³´éŸ³æª”å¿…é ˆå¾ˆçŸ­ï¼Œå¿…é ˆä½æ–¼æ‡‰ç”¨ç¨‹å¼çš„æ ¹ç›®éŒ„中,一個 `è²éŸ³` å­ç›®éŒ„和必須將命å為 `beep.wav`. diff --git a/plugins/cordova-plugin-dialogs/package.json b/plugins/cordova-plugin-dialogs/package.json new file mode 100644 index 0000000..9400082 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/package.json @@ -0,0 +1,50 @@ +{ + "name": "cordova-plugin-dialogs", + "version": "1.2.1", + "description": "Cordova Notification Plugin", + "cordova": { + "id": "cordova-plugin-dialogs", + "platforms": [ + "firefoxos", + "android", + "browser", + "amazon-fireos", + "ubuntu", + "ios", + "blackberry10", + "wp7", + "wp8", + "windows8", + "windows" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-dialogs" + }, + "keywords": [ + "cordova", + "notification", + "ecosystem:cordova", + "cordova-firefoxos", + "cordova-android", + "cordova-browser", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-ios", + "cordova-blackberry10", + "cordova-wp7", + "cordova-wp8", + "cordova-windows8", + "cordova-windows" + ], + "scripts": { + "test": "npm run jshint", + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "devDependencies": { + "jshint": "^2.6.0" + } +} diff --git a/plugins/cordova-plugin-dialogs/plugin.xml b/plugins/cordova-plugin-dialogs/plugin.xml new file mode 100644 index 0000000..62d23c9 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/plugin.xml @@ -0,0 +1,171 @@ + + + + + + Notification + Cordova Notification Plugin + Apache 2.0 + cordova,notification + https://git-wip-us.apache.org/repos/asf/cordova-plugin-dialogs.git + https://issues.apache.org/jira/browse/CB/component/12320642 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-dialogs/src/android/Notification.java b/plugins/cordova-plugin-dialogs/src/android/Notification.java new file mode 100644 index 0000000..9be56c0 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/android/Notification.java @@ -0,0 +1,505 @@ +/* + 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.dialogs; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.widget.EditText; +import android.widget.TextView; + + +/** + * This class provides access to notifications on the device. + * + * Be aware that this implementation gets called on + * navigator.notification.{alert|confirm|prompt}, and that there is a separate + * implementation in org.apache.cordova.CordovaChromeClient that gets + * called on a simple window.{alert|confirm|prompt}. + */ +public class Notification extends CordovaPlugin { + + private static final String LOG_TAG = "Notification"; + + public int confirmResult = -1; + public ProgressDialog spinnerDialog = null; + public ProgressDialog progressDialog = null; + + /** + * Constructor. + */ + public Notification() { + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArray of arguments for the plugin. + * @param callbackContext The callback context used when calling back into JavaScript. + * @return True when the action was valid, false otherwise. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + /* + * Don't run any of these if the current activity is finishing + * in order to avoid android.view.WindowManager$BadTokenException + * crashing the app. Just return true here since false should only + * be returned in the event of an invalid action. + */ + if(this.cordova.getActivity().isFinishing()) return true; + + if (action.equals("beep")) { + this.beep(args.getLong(0)); + } + else if (action.equals("alert")) { + this.alert(args.getString(0), args.getString(1), args.getString(2), callbackContext); + return true; + } + else if (action.equals("confirm")) { + this.confirm(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext); + return true; + } + else if (action.equals("prompt")) { + this.prompt(args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), callbackContext); + return true; + } + else if (action.equals("activityStart")) { + this.activityStart(args.getString(0), args.getString(1)); + } + else if (action.equals("activityStop")) { + this.activityStop(); + } + else if (action.equals("progressStart")) { + this.progressStart(args.getString(0), args.getString(1)); + } + else if (action.equals("progressValue")) { + this.progressValue(args.getInt(0)); + } + else if (action.equals("progressStop")) { + this.progressStop(); + } + else { + return false; + } + + // Only alert and confirm are async. + callbackContext.success(); + return true; + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Beep plays the default notification ringtone. + * + * @param count Number of times to play notification + */ + public void beep(final long count) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + Ringtone notification = RingtoneManager.getRingtone(cordova.getActivity().getBaseContext(), ringtone); + + // If phone is not set to silent mode + if (notification != null) { + for (long i = 0; i < count; ++i) { + notification.play(); + long timeout = 5000; + while (notification.isPlaying() && (timeout > 0)) { + timeout = timeout - 100; + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + } + }); + } + + /** + * Builds and shows a native Android alert with given Strings + * @param message The message the alert should display + * @param title The title of the alert + * @param buttonLabel The label of the button + * @param callbackContext The callback context + */ + public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) { + final CordovaInterface cordova = this.cordova; + + Runnable runnable = new Runnable() { + public void run() { + + AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(true); + dlg.setPositiveButton(buttonLabel, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); + } + }); + dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); + } + }); + + changeTextDirection(dlg); + }; + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Builds and shows a native Android confirm dialog with given title, message, buttons. + * This dialog only shows up to 3 buttons. Any labels after that will be ignored. + * The index of the button pressed will be returned to the JavaScript callback identified by callbackId. + * + * @param message The message the dialog should display + * @param title The title of the dialog + * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) + * @param callbackContext The callback context. + */ + public synchronized void confirm(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) { + final CordovaInterface cordova = this.cordova; + + Runnable runnable = new Runnable() { + public void run() { + AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(true); + + // First button + if (buttonLabels.length() > 0) { + try { + dlg.setNegativeButton(buttonLabels.getString(0), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1)); + } + }); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on first button."); + } + } + + // Second button + if (buttonLabels.length() > 1) { + try { + dlg.setNeutralButton(buttonLabels.getString(1), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2)); + } + }); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on second button."); + } + } + + // Third button + if (buttonLabels.length() > 2) { + try { + dlg.setPositiveButton(buttonLabels.getString(2), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3)); + } + }); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on third button."); + } + } + dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); + } + }); + + changeTextDirection(dlg); + }; + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Builds and shows a native Android prompt dialog with given title, message, buttons. + * This dialog only shows up to 3 buttons. Any labels after that will be ignored. + * The following results are returned to the JavaScript callback identified by callbackId: + * buttonIndex Index number of the button selected + * input1 The text entered in the prompt dialog box + * + * @param message The message the dialog should display + * @param title The title of the dialog + * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) + * @param callbackContext The callback context. + */ + public synchronized void prompt(final String message, final String title, final JSONArray buttonLabels, final String defaultText, final CallbackContext callbackContext) { + + final CordovaInterface cordova = this.cordova; + + Runnable runnable = new Runnable() { + public void run() { + final EditText promptInput = new EditText(cordova.getActivity()); + promptInput.setHint(defaultText); + AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(true); + + dlg.setView(promptInput); + + final JSONObject result = new JSONObject(); + + // First button + if (buttonLabels.length() > 0) { + try { + dlg.setNegativeButton(buttonLabels.getString(0), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + try { + result.put("buttonIndex",1); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on first button.", e); + } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on first button."); + } + } + + // Second button + if (buttonLabels.length() > 1) { + try { + dlg.setNeutralButton(buttonLabels.getString(1), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + try { + result.put("buttonIndex",2); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on second button.", e); + } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on second button."); + } + } + + // Third button + if (buttonLabels.length() > 2) { + try { + dlg.setPositiveButton(buttonLabels.getString(2), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + try { + result.put("buttonIndex",3); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on third button.", e); + } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + } catch (JSONException e) { + LOG.d(LOG_TAG,"JSONException on third button."); + } + } + dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { + public void onCancel(DialogInterface dialog){ + dialog.dismiss(); + try { + result.put("buttonIndex",0); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { e.printStackTrace(); } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + + changeTextDirection(dlg); + }; + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Show the spinner. + * + * @param title Title of the dialog + * @param message The message of the dialog + */ + public synchronized void activityStart(final String title, final String message) { + if (this.spinnerDialog != null) { + this.spinnerDialog.dismiss(); + this.spinnerDialog = null; + } + final Notification notification = this; + final CordovaInterface cordova = this.cordova; + Runnable runnable = new Runnable() { + public void run() { + notification.spinnerDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + notification.spinnerDialog.setTitle(title); + notification.spinnerDialog.setMessage(message); + notification.spinnerDialog.setCancelable(true); + notification.spinnerDialog.setIndeterminate(true); + notification.spinnerDialog.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + notification.spinnerDialog = null; + } + }); + notification.spinnerDialog.show(); + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Stop spinner. + */ + public synchronized void activityStop() { + if (this.spinnerDialog != null) { + this.spinnerDialog.dismiss(); + this.spinnerDialog = null; + } + } + + /** + * Show the progress dialog. + * + * @param title Title of the dialog + * @param message The message of the dialog + */ + public synchronized void progressStart(final String title, final String message) { + if (this.progressDialog != null) { + this.progressDialog.dismiss(); + this.progressDialog = null; + } + final Notification notification = this; + final CordovaInterface cordova = this.cordova; + Runnable runnable = new Runnable() { + public void run() { + notification.progressDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + notification.progressDialog.setTitle(title); + notification.progressDialog.setMessage(message); + notification.progressDialog.setCancelable(true); + notification.progressDialog.setMax(100); + notification.progressDialog.setProgress(0); + notification.progressDialog.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + notification.progressDialog = null; + } + }); + notification.progressDialog.show(); + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Set value of progress bar. + * + * @param value 0-100 + */ + public synchronized void progressValue(int value) { + if (this.progressDialog != null) { + this.progressDialog.setProgress(value); + } + } + + /** + * Stop progress dialog. + */ + public synchronized void progressStop() { + if (this.progressDialog != null) { + this.progressDialog.dismiss(); + this.progressDialog = null; + } + } + + @SuppressLint("NewApi") + private AlertDialog.Builder createDialog(CordovaInterface cordova) { + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { + return new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + } else { + return new AlertDialog.Builder(cordova.getActivity()); + } + } + + @SuppressLint("InlinedApi") + private ProgressDialog createProgressDialog(CordovaInterface cordova) { + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + if (currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + } else { + return new ProgressDialog(cordova.getActivity()); + } + } + + @SuppressLint("NewApi") + private void changeTextDirection(Builder dlg){ + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + dlg.create(); + AlertDialog dialog = dlg.show(); + if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + TextView messageview = (TextView)dialog.findViewById(android.R.id.message); + messageview.setTextDirection(android.view.View.TEXT_DIRECTION_LOCALE); + } + } +} diff --git a/plugins/cordova-plugin-dialogs/src/blackberry10/index.js b/plugins/cordova-plugin-dialogs/src/blackberry10/index.js new file mode 100644 index 0000000..4969a77 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/blackberry10/index.js @@ -0,0 +1,89 @@ +/* +* Copyright 2013 Research In Motion Limited. +* +* 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. +*/ + +/* global qnx, PluginResult */ + +function showDialog(args, dialogType, result) { + //Unpack and map the args + var msg = JSON.parse(decodeURIComponent(args[0])), + title = JSON.parse(decodeURIComponent(args[1])), + btnLabel = JSON.parse(decodeURIComponent(args[2])); + + if (!Array.isArray(btnLabel)) { + //Converts to array for (string) and (string,string, ...) cases + btnLabel = btnLabel.split(","); + } + + if (msg && typeof msg === "string") { + msg = msg.replace(/^"|"$/g, "").replace(/\\"/g, '"'); + } else { + result.error("message is undefined"); + return; + } + + var messageObj = { + title : title, + htmlmessage : msg, + dialogType : dialogType, + optionalButtons : btnLabel + }; + + //TODO replace with getOverlayWebview() when available in webplatform + qnx.webplatform.getWebViews()[2].dialog.show(messageObj, function (data) { + if (typeof data === "number") { + //Confirm dialog call back needs to be called with one-based indexing [1,2,3 etc] + result.callbackOk(++data, false); + } else { + //Prompt dialog callback expects object + result.callbackOk({ + buttonIndex: data.ok ? 1 : 0, + input1: (data.oktext) ? decodeURIComponent(data.oktext) : "" + }, false); + } + }); + + result.noResult(true); +} + +module.exports = { + alert: function (success, fail, args, env) { + var result = new PluginResult(args, env); + + if (Object.keys(args).length < 3) { + result.error("Notification action - alert arguments not found."); + } else { + showDialog(args, "CustomAsk", result); + } + }, + confirm: function (success, fail, args, env) { + var result = new PluginResult(args, env); + + if (Object.keys(args).length < 3) { + result.error("Notification action - confirm arguments not found."); + } else { + showDialog(args, "CustomAsk", result); + } + }, + prompt: function (success, fail, args, env) { + var result = new PluginResult(args, env); + + if (Object.keys(args).length < 3) { + result.error("Notification action - prompt arguments not found."); + } else { + showDialog(args, "JavaScriptPrompt", result); + } + } +}; diff --git a/plugins/cordova-plugin-dialogs/src/firefoxos/notification.js b/plugins/cordova-plugin-dialogs/src/firefoxos/notification.js new file mode 100644 index 0000000..aea562d --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/firefoxos/notification.js @@ -0,0 +1,155 @@ +/* + * + * 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 modulemapper = require('cordova/modulemapper'); + + +var origOpenFunc = modulemapper.getOriginalSymbol(window, 'window.open'); + + +function _empty() {} + + +function modal(message, callback, title, buttonLabels, domObjects) { + var mainWindow = window; + var modalWindow = origOpenFunc(); + var modalDocument = modalWindow.document; + + modalDocument.write( + '' + + '' + + '' + + ''); + + var box = modalDocument.createElement('form'); + box.setAttribute('role', 'dialog'); + // prepare and append empty section + var section = modalDocument.createElement('section'); + box.appendChild(section); + // add title + var boxtitle = modalDocument.createElement('h1'); + boxtitle.appendChild(modalDocument.createTextNode(title)); + section.appendChild(boxtitle); + // add message + var boxMessage = modalDocument.createElement('p'); + boxMessage.appendChild(modalDocument.createTextNode(message)); + section.appendChild(boxMessage); + // inject what's needed + if (domObjects) { + section.appendChild(domObjects); + } + // add buttons and assign callbackButton on click + var menu = modalDocument.createElement('menu'); + box.appendChild(menu); + for (var index = 0; index < buttonLabels.length; index++) { + // TODO: last button listens to the cancel key + addButton(buttonLabels[index], (index+1), (index === 0)); + } + modalDocument.body.appendChild(box); + + function addButton(label, index, recommended) { + var thisButtonCallback = makeCallbackButton(index + 1); + var button = modalDocument.createElement('button'); + button.appendChild(modalDocument.createTextNode(label)); + button.addEventListener('click', thisButtonCallback, false); + if (recommended) { + // TODO: default one listens to Enter key + button.classList.add('recommend'); + } + menu.appendChild(button); + } + + // TODO: onUnload listens to the cancel key + function onUnload() { + var result = 0; + if (modalDocument.getElementById('prompt-input')) { + result = { + input1: '', + buttonIndex: 0 + }; + } + mainWindow.setTimeout(function() { + callback(result); + }, 10); + } + modalWindow.addEventListener('unload', onUnload, false); + + // call callback and destroy modal + function makeCallbackButton(labelIndex) { + return function() { + if (modalWindow) { + modalWindow.removeEventListener('unload', onUnload, false); + modalWindow.close(); + } + // checking if prompt + var promptInput = modalDocument.getElementById('prompt-input'); + var response; + if (promptInput) { + response = { + input1: promptInput.value, + buttonIndex: labelIndex + }; + } + response = response || labelIndex; + callback(response); + }; + } +} + +var Notification = { + vibrate: function(milliseconds) { + navigator.vibrate(milliseconds); + }, + alert: function(successCallback, errorCallback, args) { + var message = args[0]; + var title = args[1]; + var _buttonLabels = [args[2]]; + var _callback = (successCallback || _empty); + modal(message, _callback, title, _buttonLabels); + }, + confirm: function(successCallback, errorCallback, args) { + var message = args[0]; + var title = args[1]; + var buttonLabels = args[2]; + var _callback = (successCallback || _empty); + modal(message, _callback, title, buttonLabels); + }, + prompt: function(successCallback, errorCallback, args) { + var message = args[0]; + var title = args[1]; + var buttonLabels = args[2]; + var defaultText = args[3]; + var inputParagraph = document.createElement('p'); + inputParagraph.classList.add('input'); + var inputElement = document.createElement('input'); + inputElement.setAttribute('type', 'text'); + inputElement.id = 'prompt-input'; + if (defaultText) { + inputElement.setAttribute('placeholder', defaultText); + } + inputParagraph.appendChild(inputElement); + modal(message, successCallback, title, buttonLabels, inputParagraph); + } +}; + + +module.exports = Notification; +require('cordova/exec/proxy').add('Notification', Notification); diff --git a/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.bundle/beep.wav b/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.bundle/beep.wav new file mode 100644 index 0000000..05f5997 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.bundle/beep.wav differ diff --git a/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.h b/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.h new file mode 100644 index 0000000..9253f6a --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.h @@ -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. + */ + +#import +#import +#import +#import + +@interface CDVNotification : CDVPlugin {} + +- (void)alert:(CDVInvokedUrlCommand*)command; +- (void)confirm:(CDVInvokedUrlCommand*)command; +- (void)prompt:(CDVInvokedUrlCommand*)command; +- (void)beep:(CDVInvokedUrlCommand*)command; + +@end + +@interface CDVAlertView : UIAlertView {} +@property (nonatomic, copy) NSString* callbackId; + +@end diff --git a/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.m b/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.m new file mode 100644 index 0000000..d1f1e33 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/ios/CDVNotification.m @@ -0,0 +1,248 @@ +/* + 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 "CDVNotification.h" + +#define DIALOG_TYPE_ALERT @"alert" +#define DIALOG_TYPE_PROMPT @"prompt" + +static void soundCompletionCallback(SystemSoundID ssid, void* data); +static NSMutableArray *alertList = nil; + +@implementation CDVNotification + +/* + * showDialogWithMessage - Common method to instantiate the alert view for alert, confirm, and prompt notifications. + * Parameters: + * message The alert view message. + * title The alert view title. + * buttons The array of customized strings for the buttons. + * defaultText The input text for the textbox (if textbox exists). + * callbackId The commmand callback id. + * dialogType The type of alert view [alert | prompt]. + */ +- (void)showDialogWithMessage:(NSString*)message title:(NSString*)title buttons:(NSArray*)buttons defaultText:(NSString*)defaultText callbackId:(NSString*)callbackId dialogType:(NSString*)dialogType +{ + + int count = (int)[buttons count]; +#ifdef __IPHONE_8_0 + if (NSClassFromString(@"UIAlertController")) { + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + + if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.3) { + + CGRect alertFrame = [UIScreen mainScreen].applicationFrame; + + if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { + // swap the values for the app frame since it is now in landscape + CGFloat temp = alertFrame.size.width; + alertFrame.size.width = alertFrame.size.height; + alertFrame.size.height = temp; + } + + alertController.view.frame = alertFrame; + } + + __weak CDVNotification* weakNotif = self; + + for (int n = 0; n < count; n++) { + [alertController addAction:[UIAlertAction actionWithTitle:[buttons objectAtIndex:n] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + CDVPluginResult* result; + + if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) + { + NSString* value0 = [[alertController.textFields objectAtIndex:0] text]; + NSDictionary* info = @{ + @"buttonIndex":@(n + 1), + @"input1":(value0 ? value0 : [NSNull null]) + }; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info]; + } + else + { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)(n + 1)]; + } + + [weakNotif.commandDelegate sendPluginResult:result callbackId:callbackId]; + }]]; + } + + if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) { + + [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.text = defaultText; + }]; + } + + if(!alertList) + alertList = [[NSMutableArray alloc] init]; + [alertList addObject:alertController]; + + if ([alertList count]==1) { + [self presentAlertcontroller]; + } + + } + else + { +#endif + + CDVAlertView* alertView = [[CDVAlertView alloc] + initWithTitle:title + message:message + delegate:self + cancelButtonTitle:nil + otherButtonTitles:nil]; + + alertView.callbackId = callbackId; + + + + for (int n = 0; n < count; n++) { + [alertView addButtonWithTitle:[buttons objectAtIndex:n]]; + } + + if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) { + alertView.alertViewStyle = UIAlertViewStylePlainTextInput; + UITextField* textField = [alertView textFieldAtIndex:0]; + textField.text = defaultText; + } + + [alertView show]; +#ifdef __IPHONE_8_0 + } +#endif + +} + +- (void)alert:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* message = [command argumentAtIndex:0]; + NSString* title = [command argumentAtIndex:1]; + NSString* buttons = [command argumentAtIndex:2]; + + [self showDialogWithMessage:message title:title buttons:@[buttons] defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT]; +} + +- (void)confirm:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* message = [command argumentAtIndex:0]; + NSString* title = [command argumentAtIndex:1]; + NSArray* buttons = [command argumentAtIndex:2]; + + [self showDialogWithMessage:message title:title buttons:buttons defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT]; +} + +- (void)prompt:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* message = [command argumentAtIndex:0]; + NSString* title = [command argumentAtIndex:1]; + NSArray* buttons = [command argumentAtIndex:2]; + NSString* defaultText = [command argumentAtIndex:3]; + + [self showDialogWithMessage:message title:title buttons:buttons defaultText:defaultText callbackId:callbackId dialogType:DIALOG_TYPE_PROMPT]; +} + +/** + * Callback invoked when an alert dialog's buttons are clicked. + */ +- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + CDVAlertView* cdvAlertView = (CDVAlertView*)alertView; + CDVPluginResult* result; + + // Determine what gets returned to JS based on the alert view type. + if (alertView.alertViewStyle == UIAlertViewStyleDefault) { + // For alert and confirm, return button index as int back to JS. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)(buttonIndex + 1)]; + } else { + // For prompt, return button index and input text back to JS. + NSString* value0 = [[alertView textFieldAtIndex:0] text]; + NSDictionary* info = @{ + @"buttonIndex":@(buttonIndex + 1), + @"input1":(value0 ? value0 : [NSNull null]) + }; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info]; + } + [self.commandDelegate sendPluginResult:result callbackId:cdvAlertView.callbackId]; +} + +static void playBeep(int count) { + SystemSoundID completeSound; + NSInteger cbDataCount = count; + NSURL* audioPath = [[NSBundle mainBundle] URLForResource:@"CDVNotification.bundle/beep" withExtension:@"wav"]; + #if __has_feature(objc_arc) + AudioServicesCreateSystemSoundID((__bridge CFURLRef)audioPath, &completeSound); + #else + AudioServicesCreateSystemSoundID((CFURLRef)audioPath, &completeSound); + #endif + AudioServicesAddSystemSoundCompletion(completeSound, NULL, NULL, soundCompletionCallback, (void*)(cbDataCount-1)); + AudioServicesPlaySystemSound(completeSound); +} + +static void soundCompletionCallback(SystemSoundID ssid, void* data) { + int count = (int)data; + AudioServicesRemoveSystemSoundCompletion (ssid); + AudioServicesDisposeSystemSoundID(ssid); + if (count > 0) { + playBeep(count); + } +} + +- (void)beep:(CDVInvokedUrlCommand*)command +{ + NSNumber* count = [command argumentAtIndex:0 withDefault:[NSNumber numberWithInt:1]]; + playBeep([count intValue]); +} + +-(UIViewController *)getTopPresentedViewController { + UIViewController *presentingViewController = self.viewController; + while(presentingViewController.presentedViewController != nil && ![presentingViewController.presentedViewController isBeingDismissed]) + { + presentingViewController = presentingViewController.presentedViewController; + } + return presentingViewController; +} + +-(void)presentAlertcontroller { + + __weak CDVNotification* weakNotif = self; + [self.getTopPresentedViewController presentViewController:[alertList firstObject] animated:YES completion:^{ + [alertList removeObject:[alertList firstObject]]; + if ([alertList count]>0) { + [weakNotif presentAlertcontroller]; + } + }]; + +} + +@end + +@implementation CDVAlertView + +@synthesize callbackId; + +@end diff --git a/plugins/cordova-plugin-dialogs/src/ubuntu/notification.cpp b/plugins/cordova-plugin-dialogs/src/ubuntu/notification.cpp new file mode 100644 index 0000000..d0adf89 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/ubuntu/notification.cpp @@ -0,0 +1,85 @@ +/* + * + * 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. + */ + +#include "notification.h" + +#include + +void Dialogs::beep(int scId, int ecId, int times) { + Q_UNUSED(scId) + Q_UNUSED(ecId) + Q_UNUSED(times) + + _player.setVolume(100); + _player.setMedia(QUrl::fromLocalFile("/usr/share/sounds/ubuntu/stereo/bell.ogg")); + _player.play(); +} + +void Dialogs::alert(int scId, int ecId, const QString &message, const QString &title, const QString &buttonLabel) { + QStringList list; + list.append(buttonLabel); + + confirm(scId, ecId, message, title, list); +} + +void Dialogs::confirm(int scId, int ecId, const QString &message, const QString &title, const QStringList &buttonLabels) { + Q_UNUSED(ecId); + + if (_alertCallback) { + qCritical() << "can't open second dialog"; + return; + } + _alertCallback = scId; + + QString s1, s2, s3; + if (buttonLabels.size() > 0) + s1 = buttonLabels[0]; + if (buttonLabels.size() > 1) + s2 = buttonLabels[1]; + if (buttonLabels.size() > 2) + s3 = buttonLabels[2]; + + QString path = m_cordova->get_app_dir() + "/../qml/notification.qml"; + QString qml = QString("PopupUtils.open(%1, root, { root: root, cordova: cordova, title: %2, text: %3, promptVisible: false, button1Text: %4, button2Text: %5, button3Text: %6 })") + .arg(CordovaInternal::format(path)).arg(CordovaInternal::format(title)).arg(CordovaInternal::format(message)) + .arg(CordovaInternal::format(s1)).arg(CordovaInternal::format(s2)).arg(CordovaInternal::format(s3)); + + m_cordova->execQML(qml); +} + +void Dialogs::prompt(int scId, int ecId, const QString &message, const QString &title, const QStringList &buttonLabels, const QString &defaultText) { + Q_UNUSED(ecId); + + if (_alertCallback) { + qCritical() << "can't open second dialog"; + return; + } + _alertCallback = scId; + + QString s1, s2, s3; + if (buttonLabels.size() > 0) + s1 = buttonLabels[0]; + if (buttonLabels.size() > 1) + s2 = buttonLabels[1]; + if (buttonLabels.size() > 2) + s3 = buttonLabels[2]; + QString path = m_cordova->get_app_dir() + "/../qml/notification.qml"; + QString qml = QString("PopupUtils.open(%1, root, { root: root, cordova: cordova, title: %2, text: %3, promptVisible: true, defaultPromptText: %7, button1Text: %4, button2Text: %5, button3Text: %6 })") + .arg(CordovaInternal::format(path)).arg(CordovaInternal::format(title)).arg(CordovaInternal::format(message)) + .arg(CordovaInternal::format(s1)).arg(CordovaInternal::format(s2)) + .arg(CordovaInternal::format(s3)).arg(CordovaInternal::format(defaultText)); + + m_cordova->execQML(qml); +} diff --git a/plugins/cordova-plugin-dialogs/src/ubuntu/notification.h b/plugins/cordova-plugin-dialogs/src/ubuntu/notification.h new file mode 100644 index 0000000..5343073 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/ubuntu/notification.h @@ -0,0 +1,64 @@ +/* + * + * 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. + */ + +#ifndef NOTIFICATION_H +#define NOTIFICATION_H + +#include +#include +#include +#include + +class Dialogs: public CPlugin { + Q_OBJECT +public: + explicit Dialogs(Cordova *cordova): CPlugin(cordova), _alertCallback(0) { + } + + virtual const QString fullName() override { + return Dialogs::fullID(); + } + + virtual const QString shortName() override { + return "Notification"; + } + + static const QString fullID() { + return "Notification"; + } +public slots: + void beep(int scId, int ecId, int times); + void alert(int scId, int ecId, const QString &message, const QString &title, const QString &buttonLabel); + void confirm(int scId, int ecId, const QString &message, const QString &title, const QStringList &buttonLabels); + void prompt(int scId, int ecId, const QString &message, const QString &title, const QStringList &buttonLabels, const QString &defaultText); + + void notificationDialogButtonPressed(int buttonId, const QString &text, bool prompt) { + if (prompt) { + QVariantMap res; + res.insert("buttonIndex", buttonId); + res.insert("input1", text); + this->cb(_alertCallback, res); + } else { + this->cb(_alertCallback, buttonId); + } + _alertCallback = 0; + } + +private: + int _alertCallback; + QMediaPlayer _player; +}; + +#endif diff --git a/plugins/cordova-plugin-dialogs/src/ubuntu/notification.qml b/plugins/cordova-plugin-dialogs/src/ubuntu/notification.qml new file mode 100644 index 0000000..5fdc7d3 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/ubuntu/notification.qml @@ -0,0 +1,65 @@ +/* + * + * 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 QtQuick 2.0 +import Ubuntu.Components.Popups 0.1 +import Ubuntu.Components 0.1 + +Dialog { + id: dialogue + property string button1Text + property string button2Text + property string button3Text + property bool promptVisible + property string defaultPromptText + + TextField { + id: prompt + text: defaultPromptText + visible: promptVisible + focus: true + } + Button { + text: button1Text + color: "orange" + onClicked: { + root.exec("Notification", "notificationDialogButtonPressed", [1, prompt.text, promptVisible]); + PopupUtils.close(dialogue) + } + } + Button { + text: button2Text + visible: button2Text.length > 0 + color: "orange" + onClicked: { + root.exec("Notification", "notificationDialogButtonPressed", [2, prompt.text, promptVisible]); + PopupUtils.close(dialogue) + } + } + Button { + text: button3Text + visible: button3Text.length > 0 + onClicked: { + root.exec("Notification", "notificationDialogButtonPressed", [3, prompt.text, promptVisible]); + PopupUtils.close(dialogue) + } + } +} diff --git a/plugins/cordova-plugin-dialogs/src/windows/NotificationProxy.js b/plugins/cordova-plugin-dialogs/src/windows/NotificationProxy.js new file mode 100644 index 0000000..c9c9233 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/windows/NotificationProxy.js @@ -0,0 +1,256 @@ +/* + * + * 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:true, WinJS, toStaticHTML */ + +var cordova = require('cordova'); + +var isAlertShowing = false; +var alertStack = []; + +// CB-8928: When toStaticHTML is undefined, prompt fails to run +var _cleanHtml = function(html) { return html; }; +if (typeof toStaticHTML !== 'undefined') { + _cleanHtml = toStaticHTML; +} + +// Windows does not provide native UI for promp dialog so we use some +// simple html-based implementation until it is available +function createPromptDialog(title, message, buttons, defaultText, callback) { + + var isPhone = cordova.platformId == "windows" && WinJS.Utilities.isPhone; + + var dlgWrap = document.createElement("div"); + dlgWrap.style.position = "absolute"; + dlgWrap.style.width = "100%"; + dlgWrap.style.height = "100%"; + dlgWrap.style.backgroundColor = "rgba(0,0,0,0.25)"; + dlgWrap.style.zIndex = "100000"; + dlgWrap.className = "dlgWrap"; + + var dlg = document.createElement("div"); + dlg.style.width = "100%"; + dlg.style.minHeight = "180px"; + dlg.style.height = "auto"; + dlg.style.overflow = "auto"; + dlg.style.backgroundColor = "white"; + dlg.style.position = "relative"; + dlg.style.lineHeight = "2"; + + if (isPhone) { + dlg.style.padding = "0px 5%"; + } else { + dlg.style.top = "50%"; // center vertically + dlg.style.transform = "translateY(-50%)"; + dlg.style.padding = "0px 30%"; + } + + // dialog layout template + dlg.innerHTML = _cleanHtml("
" + // title + "
" + // message + "
"); // input fields + + dlg.querySelector('#lbl-title').appendChild(document.createTextNode(title)); + dlg.querySelector('#lbl-message').appendChild(document.createTextNode(message)); + dlg.querySelector('#prompt-input').setAttribute('placeholder', defaultText); + + function makeButtonCallback(idx) { + return function () { + var value = dlg.querySelector('#prompt-input').value; + dlgWrap.parentNode.removeChild(dlgWrap); + + if (callback) { + callback({ input1: value, buttonIndex: idx }); + } + }; + } + + function addButton(idx, label) { + var button = document.createElement('button'); + button.style.margin = "8px 0 8px 16px"; + button.style.float = "right"; + button.style.fontSize = "12pt"; + button.tabIndex = idx; + button.onclick = makeButtonCallback(idx + 1); + if (idx === 0) { + button.style.color = "white"; + button.style.backgroundColor = "#464646"; + } else { + button.style.backgroundColor = "#cccccc"; + } + button.style.border = "none"; + button.appendChild(document.createTextNode(label)); + dlg.appendChild(button); + } + + // reverse order is used since we align buttons to the right + for (var idx = buttons.length - 1; idx >= 0; idx--) { + addButton(idx, buttons[idx]); + } + + dlgWrap.appendChild(dlg); + document.body.appendChild(dlgWrap); + + // make sure input field is under focus + dlg.querySelector('#prompt-input').focus(); + + return dlgWrap; +} + +module.exports = { + alert:function(win, loseX, args) { + + if (isAlertShowing) { + var later = function () { + module.exports.alert(win, loseX, args); + }; + alertStack.push(later); + return; + } + isAlertShowing = true; + + var message = args[0]; + var _title = args[1]; + var _buttonLabel = args[2]; + + var md = new Windows.UI.Popups.MessageDialog(message, _title); + md.commands.append(new Windows.UI.Popups.UICommand(_buttonLabel)); + md.showAsync().then(function() { + isAlertShowing = false; + if (win) { + win(); + } + + if (alertStack.length) { + setTimeout(alertStack.shift(), 0); + } + + }); + }, + + prompt: function (win, lose, args) { + if (isAlertShowing) { + var later = function () { + module.exports.prompt(win, lose, args); + }; + alertStack.push(later); + return; + } + + isAlertShowing = true; + + var message = args[0], + title = args[1], + buttons = args[2], + defaultText = args[3]; + + try { + createPromptDialog(title, message, buttons, defaultText, function (evt) { + isAlertShowing = false; + if (win) { + win(evt); + } + }); + + } catch (e) { + // set isAlertShowing flag back to false in case of exception + isAlertShowing = false; + if (alertStack.length) { + setTimeout(alertStack.shift(), 0); + } + // rethrow exception + throw e; + } + }, + + confirm:function(win, loseX, args) { + + if (isAlertShowing) { + var later = function () { + module.exports.confirm(win, loseX, args); + }; + alertStack.push(later); + return; + } + + isAlertShowing = true; + + try { + var message = args[0]; + var _title = args[1]; + var buttons = args[2]; + + var md = new Windows.UI.Popups.MessageDialog(message, _title); + + buttons.forEach(function(buttonLabel) { + md.commands.append(new Windows.UI.Popups.UICommand(buttonLabel)); + }); + + md.showAsync().then(function(res) { + isAlertShowing = false; + var result = res ? buttons.indexOf(res.label) + 1 : 0; + if (win) { + win(result); + } + if (alertStack.length) { + setTimeout(alertStack.shift(), 0); + } + + }); + } catch (e) { + // set isAlertShowing flag back to false in case of exception + isAlertShowing = false; + if (alertStack.length) { + setTimeout(alertStack.shift(), 0); + } + // rethrow exception + throw e; + } + }, + + beep:function(winX, loseX, args) { + + // set a default args if it is not set + args = args && args.length ? args : ["1"]; + + var snd = new Audio('ms-winsoundevent:Notification.Default'); + var count = parseInt(args[0]) || 1; + snd.msAudioCategory = "Alerts"; + + var onEvent = function () { + if (count > 0) { + snd.play(); + } else { + snd.removeEventListener("ended", onEvent); + snd = null; + if (winX) { + winX(); // notification.js just sends null, but this is future friendly + } + } + count--; + }; + snd.addEventListener("ended", onEvent); + onEvent(); + + } +}; + +require("cordova/exec/proxy").add("Notification",module.exports); diff --git a/plugins/cordova-plugin-dialogs/src/wp/Notification.cs b/plugins/cordova-plugin-dialogs/src/wp/Notification.cs new file mode 100644 index 0000000..b621684 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/wp/Notification.cs @@ -0,0 +1,482 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Windows; +using System.Windows.Controls; +using Microsoft.Devices; +using System.Runtime.Serialization; +using System.Threading; +using System.Windows.Resources; +using Microsoft.Phone.Controls; +using Microsoft.Xna.Framework.Audio; +using WPCordovaClassLib.Cordova.UI; +using System.Diagnostics; + + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class Notification : BaseCommand + { + static ProgressBar progressBar = null; + const int DEFAULT_DURATION = 5; + + private NotificationBox notifyBox; + + private class NotifBoxData + { + public NotificationBox previous {get;set;} + public string callbackId { get; set; } + } + + private PhoneApplicationPage Page + { + get + { + PhoneApplicationPage page = null; + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + if (frame != null) + { + page = frame.Content as PhoneApplicationPage; + } + return page; + } + } + + // blink api - doesn't look like there is an equivalent api we can use... + + [DataContract] + public class AlertOptions + { + [OnDeserializing] + public void OnDeserializing(StreamingContext context) + { + // set defaults + this.message = "message"; + this.title = "Alert"; + this.buttonLabel = "ok"; + } + + /// + /// message to display in the alert box + /// + [DataMember] + public string message; + + /// + /// title displayed on the alert window + /// + [DataMember] + public string title; + + /// + /// text to display on the button + /// + [DataMember] + public string buttonLabel; + } + + [DataContract] + public class PromptResult + { + [DataMember] + public int buttonIndex; + + [DataMember] + public string input1; + + public PromptResult(int index, string text) + { + this.buttonIndex = index; + this.input1 = text; + } + } + + public void alert(string options) + { + string[] args = JSON.JsonHelper.Deserialize(options); + AlertOptions alertOpts = new AlertOptions(); + alertOpts.message = args[0]; + alertOpts.title = args[1]; + alertOpts.buttonLabel = args[2]; + string aliasCurrentCommandCallbackId = args[3]; + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + PhoneApplicationPage page = Page; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + var previous = notifyBox; + notifyBox = new NotificationBox(); + notifyBox.Tag = new NotifBoxData { previous = previous, callbackId = aliasCurrentCommandCallbackId }; + notifyBox.PageTitle.Text = alertOpts.title; + notifyBox.SubTitle.Text = alertOpts.message; + Button btnOK = new Button(); + btnOK.Content = alertOpts.buttonLabel; + btnOK.Click += new RoutedEventHandler(btnOK_Click); + btnOK.Tag = 1; + notifyBox.ButtonPanel.Children.Add(btnOK); + grid.Children.Add(notifyBox); + + if (previous == null) + { + page.BackKeyPress += page_BackKeyPress; + } + } + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION)); + } + }); + } + + public void prompt(string options) + { + string[] args = JSON.JsonHelper.Deserialize(options); + string message = args[0]; + string title = args[1]; + string buttonLabelsArray = args[2]; + string[] buttonLabels = JSON.JsonHelper.Deserialize(buttonLabelsArray); + string defaultText = args[3]; + string aliasCurrentCommandCallbackId = args[4]; + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + PhoneApplicationPage page = Page; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + var previous = notifyBox; + notifyBox = new NotificationBox(); + notifyBox.Tag = new NotifBoxData { previous = previous, callbackId = aliasCurrentCommandCallbackId }; + notifyBox.PageTitle.Text = title; + notifyBox.SubTitle.Text = message; + + //TextBox textBox = new TextBox(); + //textBox.Text = defaultText; + //textBox.AcceptsReturn = true; + //notifyBox.ContentScroller.Content = textBox; + + notifyBox.InputText.Text = defaultText; + notifyBox.InputText.Visibility = Visibility.Visible; + + for (int i = 0; i < buttonLabels.Length; ++i) + { + Button button = new Button(); + button.Content = buttonLabels[i]; + button.Tag = i + 1; + button.Click += promptBoxbutton_Click; + notifyBox.ButtonPanel.Orientation = Orientation.Vertical; + notifyBox.ButtonPanel.Children.Add(button); + } + + grid.Children.Add(notifyBox); + if (previous != null) + { + page.BackKeyPress += page_BackKeyPress; + } + } + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION)); + } + }); + } + + public void confirm(string options) + { + string[] args = JSON.JsonHelper.Deserialize(options); + AlertOptions alertOpts = new AlertOptions(); + alertOpts.message = args[0]; + alertOpts.title = args[1]; + alertOpts.buttonLabel = args[2]; + string aliasCurrentCommandCallbackId = args[3]; + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + PhoneApplicationPage page = Page; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + var previous = notifyBox; + notifyBox = new NotificationBox(); + notifyBox.Tag = new NotifBoxData { previous = previous, callbackId = aliasCurrentCommandCallbackId }; + notifyBox.PageTitle.Text = alertOpts.title; + notifyBox.SubTitle.Text = alertOpts.message; + + string[] labels = JSON.JsonHelper.Deserialize(alertOpts.buttonLabel); + + if (labels == null) + { + labels = alertOpts.buttonLabel.Split(','); + } + + for (int n = 0; n < labels.Length; n++) + { + Button btn = new Button(); + btn.Content = labels[n]; + btn.Tag = n; + btn.Click += new RoutedEventHandler(btnOK_Click); + notifyBox.ButtonPanel.Children.Add(btn); + } + + grid.Children.Add(notifyBox); + if (previous == null) + { + page.BackKeyPress += page_BackKeyPress; + } + } + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION)); + } + }); + } + + void promptBoxbutton_Click(object sender, RoutedEventArgs e) + { + Button button = sender as Button; + FrameworkElement promptBox = null; + int buttonIndex = 0; + string callbackId = string.Empty; + string text = string.Empty; + if (button != null) + { + buttonIndex = (int)button.Tag; + promptBox = button.Parent as FrameworkElement; + while ((promptBox = promptBox.Parent as FrameworkElement) != null && + !(promptBox is NotificationBox)) ; + } + + if (promptBox != null) + { + NotificationBox box = promptBox as NotificationBox; + + text = box.InputText.Text; + + PhoneApplicationPage page = Page; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + grid.Children.Remove(promptBox); + } + + NotifBoxData data = promptBox.Tag as NotifBoxData; + promptBox = data.previous as NotificationBox; + callbackId = data.callbackId as string; + + if (promptBox == null) + { + page.BackKeyPress -= page_BackKeyPress; + } + } + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new PromptResult(buttonIndex, text)), callbackId); + } + + void page_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e) + { + PhoneApplicationPage page = sender as PhoneApplicationPage; + string callbackId = ""; + if (page != null && notifyBox != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + grid.Children.Remove(notifyBox); + NotifBoxData notifBoxData = notifyBox.Tag as NotifBoxData; + notifyBox = notifBoxData.previous as NotificationBox; + callbackId = notifBoxData.callbackId as string; + } + if (notifyBox == null) + { + page.BackKeyPress -= page_BackKeyPress; + } + e.Cancel = true; + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, 0), callbackId); + } + + void btnOK_Click(object sender, RoutedEventArgs e) + { + Button btn = sender as Button; + FrameworkElement notifBoxParent = null; + int retVal = 0; + string callbackId = ""; + if (btn != null) + { + retVal = (int)btn.Tag + 1; + + notifBoxParent = btn.Parent as FrameworkElement; + while ((notifBoxParent = notifBoxParent.Parent as FrameworkElement) != null && + !(notifBoxParent is NotificationBox)) ; + } + if (notifBoxParent != null) + { + PhoneApplicationPage page = Page; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + grid.Children.Remove(notifBoxParent); + } + + NotifBoxData notifBoxData = notifBoxParent.Tag as NotifBoxData; + notifyBox = notifBoxData.previous as NotificationBox; + callbackId = notifBoxData.callbackId as string; + + if (notifyBox == null) + { + page.BackKeyPress -= page_BackKeyPress; + } + } + + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, retVal), callbackId); + } + + + + public void beep(string options) + { + string[] args = JSON.JsonHelper.Deserialize(options); + int times = int.Parse(args[0]); + + string resourcePath = BaseCommand.GetBaseURL() + "Plugins/cordova-plugin-dialogs/notification-beep.wav"; + + StreamResourceInfo sri = Application.GetResourceStream(new Uri(resourcePath, UriKind.Relative)); + + if (sri != null) + { + SoundEffect effect = SoundEffect.FromStream(sri.Stream); + SoundEffectInstance inst = effect.CreateInstance(); + ThreadPool.QueueUserWorkItem((o) => + { + // cannot interact with UI !! + do + { + inst.Play(); + Thread.Sleep(effect.Duration + TimeSpan.FromMilliseconds(100)); + } + while (--times > 0); + + }); + + } + + // TODO: may need a listener to trigger DispatchCommandResult after the alarm has finished executing... + DispatchCommandResult(); + } + + // Display an indeterminate progress indicator + public void activityStart(string unused) + { + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + + if (frame != null) + { + PhoneApplicationPage page = frame.Content as PhoneApplicationPage; + + if (page != null) + { + var temp = page.FindName("LayoutRoot"); + Grid grid = temp as Grid; + if (grid != null) + { + if (progressBar != null) + { + grid.Children.Remove(progressBar); + } + progressBar = new ProgressBar(); + progressBar.IsIndeterminate = true; + progressBar.IsEnabled = true; + + grid.Children.Add(progressBar); + } + } + } + }); + } + + + // Remove our indeterminate progress indicator + public void activityStop(string unused) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (progressBar != null) + { + progressBar.IsEnabled = false; + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + if (frame != null) + { + PhoneApplicationPage page = frame.Content as PhoneApplicationPage; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + grid.Children.Remove(progressBar); + } + } + } + progressBar = null; + } + }); + } + + public void vibrate(string vibrateDuration) + { + + int msecs = 200; // set default + + try + { + string[] args = JSON.JsonHelper.Deserialize(vibrateDuration); + + msecs = int.Parse(args[0]); + if (msecs < 1) + { + msecs = 1; + } + } + catch (FormatException) + { + + } + + VibrateController.Default.Start(TimeSpan.FromMilliseconds(msecs)); + + // TODO: may need to add listener to trigger DispatchCommandResult when the vibration ends... + DispatchCommandResult(); + } + } +} diff --git a/plugins/cordova-plugin-dialogs/src/wp/NotificationBox.xaml b/plugins/cordova-plugin-dialogs/src/wp/NotificationBox.xaml new file mode 100644 index 0000000..2d564fb --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/wp/NotificationBox.xaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-dialogs/src/wp/NotificationBox.xaml.cs b/plugins/cordova-plugin-dialogs/src/wp/NotificationBox.xaml.cs new file mode 100644 index 0000000..50b2f2a --- /dev/null +++ b/plugins/cordova-plugin-dialogs/src/wp/NotificationBox.xaml.cs @@ -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. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace WPCordovaClassLib.Cordova.UI +{ + public partial class NotificationBox : UserControl + { + public NotificationBox() + { + InitializeComponent(); + } + } +} diff --git a/plugins/cordova-plugin-dialogs/src/wp/notification-beep.wav b/plugins/cordova-plugin-dialogs/src/wp/notification-beep.wav new file mode 100644 index 0000000..d0ad085 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/src/wp/notification-beep.wav differ diff --git a/plugins/cordova-plugin-dialogs/tests/plugin.xml b/plugins/cordova-plugin-dialogs/tests/plugin.xml new file mode 100644 index 0000000..498038c --- /dev/null +++ b/plugins/cordova-plugin-dialogs/tests/plugin.xml @@ -0,0 +1,29 @@ + + + + + Cordova Notification Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-dialogs/tests/tests.js b/plugins/cordova-plugin-dialogs/tests/tests.js new file mode 100644 index 0000000..a4cf382 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/tests/tests.js @@ -0,0 +1,203 @@ +/* + * + * 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. + * +*/ + +/* jshint jasmine: true */ +/* global cordova */ + +exports.defineAutoTests = function () { + describe('Notification (navigator.notification)', function () { + it("should exist", function () { + expect(navigator.notification).toBeDefined(); + }); + + it("should contain a beep function", function () { + expect(typeof navigator.notification.beep).toBeDefined(); + expect(typeof navigator.notification.beep).toBe("function"); + }); + + it("should contain an alert function", function () { + expect(typeof navigator.notification.alert).toBeDefined(); + expect(typeof navigator.notification.alert).toBe("function"); + }); + + it("should contain a confirm function", function () { + expect(typeof navigator.notification.confirm).toBeDefined(); + expect(typeof navigator.notification.confirm).toBe("function"); + }); + + it("should contain a prompt function", function () { + expect(typeof navigator.notification.prompt).toBeDefined(); + expect(typeof navigator.notification.prompt).toBe("function"); + }); + }); +}; + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +exports.defineManualTests = function (contentEl, createActionButton) { + var logMessage = function (message) { + var log = document.getElementById('info'); + var logLine = document.createElement('div'); + logLine.innerHTML = message; + log.appendChild(logLine); + }; + + var clearLog = function () { + var log = document.getElementById('info'); + log.innerHTML = ''; + }; + + var beep = function () { + console.log("beep()"); + navigator.notification.beep(3); + }; + + var alertDialog = function (message, title, button) { + console.log("alertDialog()"); + navigator.notification.alert(message, + function () { + console.log("Alert dismissed."); + }, + title, button); + console.log("After alert"); + }; + + var confirmDialogA = function (message, title, buttons) { + clearLog(); + navigator.notification.confirm(message, + function (r) { + if (r === 0) { + logMessage("Dismissed dialog without making a selection."); + console.log("Dismissed dialog without making a selection."); + } else { + console.log("You selected " + r); + logMessage("You selected " + (buttons.split(","))[r - 1]); + } + }, + title, + buttons); + }; + + var confirmDialogB = function (message, title, buttons) { + clearLog(); + navigator.notification.confirm(message, + function (r) { + if (r === 0) { + logMessage("Dismissed dialog without making a selection."); + console.log("Dismissed dialog without making a selection."); + } else { + console.log("You selected " + r); + logMessage("You selected " + buttons[r - 1]); + } + }, + title, + buttons); + }; + + var promptDialog = function (message, title, buttons) { + clearLog(); + navigator.notification.prompt(message, + function (r) { + if (r && r.buttonIndex === 0) { + var msg = "Dismissed dialog"; + if (r.input1) { + msg += " with input: " + r.input1; + } + logMessage(msg); + console.log(msg); + } else { + console.log("You selected " + r.buttonIndex + " and entered: " + r.input1); + logMessage("You selected " + buttons[r.buttonIndex - 1] + " and entered: " + r.input1); + } + }, + title, + buttons); + }; + + /******************************************************************************/ + + var dialogs_tests = '
' + + 'Expected result: Device will beep (unless device is on silent). Nothing will get updated in status box.' + + '

Dialog Tests

' + + '

Dialog boxes will pop up for each of the following tests

' + + '

' + + 'Expected result: Dialog will say "You pressed alert". Press continue to close dialog. Nothing will get updated in status box.' + + '

' + + 'Expected result: Dialog will say "You pressed confirm". Press Yes, No, or Maybe to close dialog. Status box will tell you what option you selected.' + + '

' + + 'Expected result: Dialog will say "You pressed confirm". Press Yes, No, or Maybe, Not Sure to close dialog. Status box will tell you what option you selected, and should use 1-based indexing.' + + '

' + + 'Expected result: Dialog will say "You pressed prompt". Enter any message and press Yes, No, or Maybe, Not Sure to close dialog. Status box will tell you what option you selected and message you entered, and should use 1-based indexing.' + + '

' + + 'Expected result: Dialog will have title "index.html" and say "You pressed alert" Press OK to close dialog. Nothing will get updated in status box.' + + '

' + + 'Expected result: Dialog will have title "index.html" and say "You selected confirm". Press Cancel or OK to close dialog. Nothing will get updated in status box.' + + '

' + + 'Expected result: Dialog will have title "index.html" and say "This is a prompt". "Default value" will be in text box. Press Cancel or OK to close dialog. Nothing will get updated in status box.'; + + contentEl.innerHTML = '
' + + dialogs_tests; + + createActionButton('Beep', function () { + beep(); + }, 'beep'); + + createActionButton('Alert Dialog', function () { + alertDialog('You pressed alert.', 'Alert Dialog', 'Continue'); + }, 'alert'); + + // WP8.1 detection is necessary since it doesn't support confirm dialogs with more than 2 buttons + var isRunningOnWP81 = cordova.platformId == "windows" && navigator.userAgent.indexOf('Windows Phone') > -1; + + createActionButton('Confirm Dialog - Deprecated', function () { + var buttons = isRunningOnWP81 ? 'Yes,No' : 'Yes,No,Maybe'; + confirmDialogA('You pressed confirm.', 'Confirm Dialog', buttons); + }, 'confirm_deprecated'); + + createActionButton('Confirm Dialog', function () { + var buttons = isRunningOnWP81 ? ['Yes', 'Actually, No'] : ['Yes', 'No', 'Maybe, Not Sure']; + confirmDialogB('You pressed confirm.', 'Confirm Dialog', buttons); + }, 'confirm'); + + createActionButton('Prompt Dialog', function () { + promptDialog('You pressed prompt.', 'Prompt Dialog', ['Yes', 'No', 'Maybe, Not Sure']); + }, 'prompt'); + + createActionButton('Built-in Alert Dialog', function () { + if (typeof alert === 'function') { + alert('You pressed alert'); + } + }, 'built_in_alert'); + + createActionButton('Built-in Confirm Dialog', function () { + if (typeof confirm === 'function') { + confirm('You selected confirm'); + } + }, 'built_in_confirm'); + + createActionButton('Built-in Prompt Dialog', function () { + if (typeof prompt === 'function') { + prompt('This is a prompt', 'Default value'); + } + }, 'built_in_prompt'); +}; diff --git a/plugins/cordova-plugin-dialogs/www/android/notification.js b/plugins/cordova-plugin-dialogs/www/android/notification.js new file mode 100644 index 0000000..8936a5c --- /dev/null +++ b/plugins/cordova-plugin-dialogs/www/android/notification.js @@ -0,0 +1,74 @@ +/* + * + * 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'); + +/** + * Provides Android enhanced notification API. + */ +module.exports = { + activityStart : function(title, message) { + // If title and message not specified then mimic Android behavior of + // using default strings. + if (typeof title === "undefined" && typeof message == "undefined") { + title = "Busy"; + message = 'Please wait...'; + } + + exec(null, null, 'Notification', 'activityStart', [ title, message ]); + }, + + /** + * Close an activity dialog + */ + activityStop : function() { + exec(null, null, 'Notification', 'activityStop', []); + }, + + /** + * Display a progress dialog with progress bar that goes from 0 to 100. + * + * @param {String} + * title Title of the progress dialog. + * @param {String} + * message Message to display in the dialog. + */ + progressStart : function(title, message) { + exec(null, null, 'Notification', 'progressStart', [ title, message ]); + }, + + /** + * Close the progress dialog. + */ + progressStop : function() { + exec(null, null, 'Notification', 'progressStop', []); + }, + + /** + * Set the progress dialog value. + * + * @param {Number} + * value 0-100 + */ + progressValue : function(value) { + exec(null, null, 'Notification', 'progressValue', [ value ]); + } +}; diff --git a/plugins/cordova-plugin-dialogs/www/blackberry10/beep.js b/plugins/cordova-plugin-dialogs/www/blackberry10/beep.js new file mode 100644 index 0000000..da2e75d --- /dev/null +++ b/plugins/cordova-plugin-dialogs/www/blackberry10/beep.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. + * +*/ + +module.exports = function (quantity) { + var count = 0, + beepObj; + + function callback() { + if (--count > 0) { + play(); + } else { + beepObj.removeEventListener("ended", callback); + beepObj = null; + } + } + + function play() { + //create new object every time due to strage playback behaviour + beepObj = new Audio('local:///chrome/plugin/cordova-plugin-dialogs/notification-beep.wav'); + beepObj.addEventListener("ended", callback); + beepObj.play(); + } + + count += quantity || 1; + if (count > 0) { + play(); + } +}; diff --git a/plugins/cordova-plugin-dialogs/www/blackberry10/notification-beep.wav b/plugins/cordova-plugin-dialogs/www/blackberry10/notification-beep.wav new file mode 100644 index 0000000..d0ad085 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/blackberry10/notification-beep.wav differ diff --git a/plugins/cordova-plugin-dialogs/www/browser/notification.js b/plugins/cordova-plugin-dialogs/www/browser/notification.js new file mode 100644 index 0000000..1fdfafd --- /dev/null +++ b/plugins/cordova-plugin-dialogs/www/browser/notification.js @@ -0,0 +1,115 @@ +/* + * + * 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. + * +*/ + +// Platform: browser +window.navigator.notification = window.navigator.notification || {}; + +module.exports.alert = window.navigator.notification.alert = function(message, callback) { + // `notification.alert` executes asynchronously + setTimeout(function() { + window.alert(message); + if (callback) { + callback(); + } + }, 0); +}; + + +module.exports.confirm = window.navigator.notification.confirm = function(message, callback) { + // `notification.confirm` executes asynchronously + setTimeout(function() { + var result = window.confirm(message); + if (callback) { + if (result) { + callback(1); // OK + } + else { + callback(2); // Cancel + } + } + }, 0); +}; + + +module.exports.prompt = window.navigator.notification.prompt = function(message, callback, title, buttonLabels, defaultText) { + // `notification.prompt` executes asynchronously + setTimeout(function() { + var result = window.prompt(message, defaultText || ''); + if (callback) { + if (result === null) { + callback({ buttonIndex: 2, input1: '' }); // Cancel + } + else { + callback({ buttonIndex: 1, input1: result }); // OK + } + } + }, 0); +}; + + +var audioContext = (function() { + // Determine if the Audio API is supported by this browser + var AudioApi = window.AudioContext; + if (!AudioApi) { + AudioApi = window.webkitAudioContext; + } + + if (AudioApi) { + // The Audio API is supported, so create a singleton instance of the AudioContext + return new AudioApi(); + } + + return undefined; +}()); + +module.exports.beep = window.navigator.notification.beep = function(times) { + if (times > 0) { + var BEEP_DURATION = 700; + var BEEP_INTERVAL = 300; + + if (audioContext) { + // Start a beep, using the Audio API + var osc = audioContext.createOscillator(); + osc.type = 0; // sounds like a "beep" + osc.connect(audioContext.destination); + osc.start(0); + + setTimeout(function() { + // Stop the beep after the BEEP_DURATION + osc.stop(0); + + if (--times > 0) { + // Beep again, after a pause + setTimeout(function() { + navigator.notification.beep(times); + }, BEEP_INTERVAL); + } + + }, BEEP_DURATION); + } + else if (typeof(console) !== 'undefined' && typeof(console.log) === 'function') { + // Audio API isn't supported, so just write `beep` to the console + for (var i = 0; i < times; i++) { + console.log('Beep!'); + } + } + } +}; diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/danger-press.png b/plugins/cordova-plugin-dialogs/www/firefoxos/danger-press.png new file mode 100644 index 0000000..d7529b5 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/firefoxos/danger-press.png differ diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/danger.png b/plugins/cordova-plugin-dialogs/www/firefoxos/danger.png new file mode 100644 index 0000000..400e3ae Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/firefoxos/danger.png differ diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/default.png b/plugins/cordova-plugin-dialogs/www/firefoxos/default.png new file mode 100644 index 0000000..2ff298a Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/firefoxos/default.png differ diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/gradient.png b/plugins/cordova-plugin-dialogs/www/firefoxos/gradient.png new file mode 100644 index 0000000..b288545 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/firefoxos/gradient.png differ diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/notification.css b/plugins/cordova-plugin-dialogs/www/firefoxos/notification.css new file mode 100644 index 0000000..34d92b8 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/www/firefoxos/notification.css @@ -0,0 +1,248 @@ +/* + * + * 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. + * +*/ + +/* Main dialog setup */ +form[role="dialog"] { + background: + url(../img/pattern.png) repeat left top, + url(../img/gradient.png) no-repeat left top / 100% 100%; + overflow: hidden; + position: absolute; + z-index: 100; + top: 0; + left: 0; + right: 0; + bottom: 0; + padding: 1.5rem 0 7rem; + font-family: "MozTT", Sans-serif; + font-size: 0; + /* Using font-size: 0; we avoid the unwanted visual space (about 3px) + created by white-spaces and break lines in the code betewen inline-block elements */ + color: #fff; + text-align: left; +} + +form[role="dialog"]:before { + content: ""; + display: inline-block; + vertical-align: middle; + width: 0.1rem; + height: 100%; + margin-left: -0.1rem; +} + +form[role="dialog"] > section { + font-weight: lighter; + font-size: 1.8rem; + color: #FAFAFA; + padding: 0 1.5rem; + -moz-box-sizing: padding-box; + width: 100%; + display: inline-block; + overflow-y: scroll; + max-height: 100%; + vertical-align: middle; + white-space: normal; +} + +form[role="dialog"] h1 { + font-weight: normal; + font-size: 1.6rem; + line-height: 1.5em; + color: #fff; + margin: 0; + padding: 0 1.5rem 1rem; + border-bottom: 0.1rem solid #686868; +} + +/* Menu & buttons setup */ +form[role="dialog"] menu { + margin: 0; + padding: 1.5rem; + padding-bottom: 0.5rem; + border-top: solid 0.1rem rgba(255, 255, 255, 0.1); + background: #2d2d2d url(../img/pattern.png) repeat left top; + display: block; + overflow: hidden; + position: absolute; + left: 0; + right: 0; + bottom: 0; + text-align: center; +} + +form[role="dialog"] menu button::-moz-focus-inner { + border: none; + outline: none; +} +form[role="dialog"] menu button { + width: 100%; + height: 2.4rem; + margin: 0 0 1rem; + padding: 0 1.5rem; + -moz-box-sizing: border-box; + display: inline-block; + vertical-align: middle; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + background: #fafafa url(../img/default.png) repeat-x left bottom/ auto 100%; + border: 0.1rem solid #a6a6a6; + border-radius: 0.3rem; + font: 500 1.2rem/2.4rem 'MozTT', Sans-serif; + color: #333; + text-align: center; + text-shadow: 0.1rem 0.1rem 0 rgba(255,255,255,0.3); + text-decoration: none; + outline: none; +} + +/* Press (default & recommend) */ +form[role="dialog"] menu button:active, +form[role="dialog"] menu button.recommend:active, +a.recommend[role="button"]:active { + border-color: #008aaa; + color: #333; +} + +/* Recommend */ +form[role="dialog"] menu button.recommend { + background-image: url(../img/recommend.png); + background-color: #00caf2; + border-color: #008eab; +} + +/* Danger */ +form[role="dialog"] menu button.danger, +a.danger[role="button"] { + background-image: url(../img/danger.png); + background-color: #b70404; + color: #fff; + text-shadow: none; + border-color: #820000; +} + +/* Danger Press */ +form[role="dialog"] menu button.danger:active { + background-image: url(../img/danger-press.png); + background-color: #890707; +} + +/* Disabled */ +form[role="dialog"] > menu > button[disabled] { + background: #5f5f5f; + color: #4d4d4d; + text-shadow: none; + border-color: #4d4d4d; + pointer-events: none; +} + + +form[role="dialog"] menu button:nth-child(even) { + margin-left: 1rem; +} + +form[role="dialog"] menu button, +form[role="dialog"] menu button:nth-child(odd) { + margin: 0 0 1rem 0; +} + +form[role="dialog"] menu button { + width: calc((100% - 1rem) / 2); +} + +form[role="dialog"] menu button.full { + width: 100%; +} + +/* Specific component code */ +form[role="dialog"] p { + word-wrap: break-word; + margin: 1rem 0 0; + padding: 0 1.5rem 1rem; + line-height: 3rem; +} + +form[role="dialog"] p img { + float: left; + margin-right: 2rem; +} + +form[role="dialog"] p strong { + font-weight: lighter; +} + +form[role="dialog"] p small { + font-size: 1.4rem; + font-weight: normal; + color: #cbcbcb; + display: block; +} + +form[role="dialog"] dl { + border-top: 0.1rem solid #686868; + margin: 1rem 0 0; + overflow: hidden; + padding-top: 1rem; + font-size: 1.6rem; + line-height: 2.2rem; +} + +form[role="dialog"] dl > dt { + clear: both; + float: left; + width: 7rem; + padding-left: 1.5rem; + font-weight: 500; + text-align: left; +} + +form[role="dialog"] dl > dd { + padding-right: 1.5rem; + font-weight: 300; + text-overflow: ellipsis; + vertical-align: top; + overflow: hidden; +} + +/* input areas */ +input[type="text"], +input[type="password"], +input[type="email"], +input[type="tel"], +input[type="search"], +input[type="url"], +input[type="number"], +textarea { + -moz-box-sizing: border-box; + display: block; + overflow: hidden; + width: 100%; + height: 3rem; + resize: none; + padding: 0 1rem; + font-size: 1.6rem; + line-height: 3rem; + border: 0.1rem solid #ccc; + border-radius: 0.3rem; + box-shadow: none; /* override the box-shadow from the system (performance issue) */ + background: #fff url(input_areas/images/ui/shadow.png) repeat-x; +} diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/pattern.png b/plugins/cordova-plugin-dialogs/www/firefoxos/pattern.png new file mode 100644 index 0000000..af03f56 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/firefoxos/pattern.png differ diff --git a/plugins/cordova-plugin-dialogs/www/firefoxos/recommend.png b/plugins/cordova-plugin-dialogs/www/firefoxos/recommend.png new file mode 100644 index 0000000..42aed39 Binary files /dev/null and b/plugins/cordova-plugin-dialogs/www/firefoxos/recommend.png differ diff --git a/plugins/cordova-plugin-dialogs/www/notification.js b/plugins/cordova-plugin-dialogs/www/notification.js new file mode 100644 index 0000000..44f25c1 --- /dev/null +++ b/plugins/cordova-plugin-dialogs/www/notification.js @@ -0,0 +1,112 @@ +/* + * + * 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 platform = require('cordova/platform'); + +/** + * Provides access to notifications on the device. + */ + +module.exports = { + + /** + * Open a native alert dialog, with a customizable title and button text. + * + * @param {String} message Message to print in the body of the alert + * @param {Function} completeCallback The callback that is called when user clicks on a button. + * @param {String} title Title of the alert dialog (default: Alert) + * @param {String} buttonLabel Label of the close button (default: OK) + */ + alert: function(message, completeCallback, title, buttonLabel) { + var _title = (typeof title === "string" ? title : "Alert"); + var _buttonLabel = (buttonLabel || "OK"); + exec(completeCallback, null, "Notification", "alert", [message, _title, _buttonLabel]); + }, + + /** + * Open a native confirm dialog, with a customizable title and button text. + * The result that the user selects is returned to the result callback. + * + * @param {String} message Message to print in the body of the alert + * @param {Function} resultCallback The callback that is called when user clicks on a button. + * @param {String} title Title of the alert dialog (default: Confirm) + * @param {Array} buttonLabels Array of the labels of the buttons (default: ['OK', 'Cancel']) + */ + confirm: function(message, resultCallback, title, buttonLabels) { + var _title = (typeof title === "string" ? title : "Confirm"); + var _buttonLabels = (buttonLabels || ["OK", "Cancel"]); + + // Strings are deprecated! + if (typeof _buttonLabels === 'string') { + console.log("Notification.confirm(string, function, string, string) is deprecated. Use Notification.confirm(string, function, string, array)."); + } + + // Some platforms take an array of button label names. + // Other platforms take a comma separated list. + // For compatibility, we convert to the desired type based on the platform. + if (platform.id == "amazon-fireos" || platform.id == "android" || platform.id == "ios" || + platform.id == "windowsphone" || platform.id == "firefoxos" || platform.id == "ubuntu" || + platform.id == "windows8" || platform.id == "windows") { + + if (typeof _buttonLabels === 'string') { + _buttonLabels = _buttonLabels.split(","); // not crazy about changing the var type here + } + } else { + if (Array.isArray(_buttonLabels)) { + var buttonLabelArray = _buttonLabels; + _buttonLabels = buttonLabelArray.toString(); + } + } + exec(resultCallback, null, "Notification", "confirm", [message, _title, _buttonLabels]); + }, + + /** + * Open a native prompt dialog, with a customizable title and button text. + * The following results are returned to the result callback: + * buttonIndex Index number of the button selected. + * input1 The text entered in the prompt dialog box. + * + * @param {String} message Dialog message to display (default: "Prompt message") + * @param {Function} resultCallback The callback that is called when user clicks on a button. + * @param {String} title Title of the dialog (default: "Prompt") + * @param {Array} buttonLabels Array of strings for the button labels (default: ["OK","Cancel"]) + * @param {String} defaultText Textbox input value (default: empty string) + */ + prompt: function(message, resultCallback, title, buttonLabels, defaultText) { + var _message = (typeof message === "string" ? message : "Prompt message"); + var _title = (typeof title === "string" ? title : "Prompt"); + var _buttonLabels = (buttonLabels || ["OK","Cancel"]); + var _defaultText = (defaultText || ""); + exec(resultCallback, null, "Notification", "prompt", [_message, _title, _buttonLabels, _defaultText]); + }, + + /** + * Causes the device to beep. + * On Android, the default notification ringtone is played "count" times. + * + * @param {Integer} count The number of beeps. + */ + beep: function(count) { + var defaultedCount = count || 1; + exec(null, null, "Notification", "beep", [ defaultedCount ]); + } +}; diff --git a/plugins/cordova-plugin-file-transfer/CONTRIBUTING.md b/plugins/cordova-plugin-file-transfer/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-file-transfer/LICENSE b/plugins/cordova-plugin-file-transfer/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/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/plugins/cordova-plugin-file-transfer/NOTICE b/plugins/cordova-plugin-file-transfer/NOTICE new file mode 100644 index 0000000..46fed23 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/NOTICE @@ -0,0 +1,8 @@ +Apache Cordova +Copyright 2012 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +This product includes a copy of OkHttp from: +https://github.com/square/okhttp diff --git a/plugins/cordova-plugin-file-transfer/README.md b/plugins/cordova-plugin-file-transfer/README.md new file mode 100644 index 0000000..5b4b29b --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/README.md @@ -0,0 +1,317 @@ + + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +# cordova-plugin-file-transfer + +This plugin allows you to upload and download files. + +This plugin defines global `FileTransfer`, `FileUploadOptions` constructors. Although in the global scope, they are not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + +Report issues with this plugin on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20File%20Transfer%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + + cordova plugin add cordova-plugin-file-transfer + +## Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Browser +- Firefox OS** +- iOS +- Windows Phone 7 and 8* +- Windows + +\* _Do not support `onprogress` nor `abort()`_ + +\** _Do not support `onprogress`_ + +# FileTransfer + +The `FileTransfer` object provides a way to upload files using an HTTP +multi-part POST or PUT request, and to download files. + +## Properties + +- __onprogress__: Called with a `ProgressEvent` whenever a new chunk of data is transferred. _(Function)_ + +## Methods + +- __upload__: Sends a file to a server. + +- __download__: Downloads a file from server. + +- __abort__: Aborts an in-progress transfer. + + +## upload + +__Parameters__: + +- __fileURL__: Filesystem URL representing the file on the device or a [data URI](https://en.wikipedia.org/wiki/Data_URI_scheme). For backwards compatibility, this can also be the full path of the file on the device. (See [Backwards Compatibility Notes](#backwards-compatibility-notes) below) + +- __server__: URL of the server to receive the file, as encoded by `encodeURI()`. + +- __successCallback__: A callback that is passed a `FileUploadResult` object. _(Function)_ + +- __errorCallback__: A callback that executes if an error occurs retrieving the `FileUploadResult`. Invoked with a `FileTransferError` object. _(Function)_ + +- __options__: Optional parameters _(Object)_. Valid keys: + - __fileKey__: The name of the form element. Defaults to `file`. (DOMString) + - __fileName__: The file name to use when saving the file on the server. Defaults to `image.jpg`. (DOMString) + - __httpMethod__: The HTTP method to use - either `PUT` or `POST`. Defaults to `POST`. (DOMString) + - __mimeType__: The mime type of the data to upload. Defaults to `image/jpeg`. (DOMString) + - __params__: A set of optional key/value pairs to pass in the HTTP request. (Object, key/value - DOMString) + - __chunkedMode__: Whether to upload the data in chunked streaming mode. Defaults to `true`. (Boolean) + - __headers__: A map of header name/header values. Use an array to specify more than one value. On iOS, FireOS, and Android, if a header named Content-Type is present, multipart form data will NOT be used. (Object) + +- __trustAllHosts__: Optional parameter, defaults to `false`. If set to `true`, it accepts all security certificates. This is useful since Android rejects self-signed security certificates. Not recommended for production use. Supported on Android and iOS. _(boolean)_ + +### Example + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + +### Example with Upload Headers and Progress Events (Android and iOS only) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + +## FileUploadResult + +A `FileUploadResult` object is passed to the success callback of the +`FileTransfer` object's `upload()` method. + +### Properties + +- __bytesSent__: The number of bytes sent to the server as part of the upload. (long) + +- __responseCode__: The HTTP response code returned by the server. (long) + +- __response__: The HTTP response returned by the server. (DOMString) + +- __headers__: The HTTP response headers by the server. (Object) + - Currently supported on iOS only. + +### iOS Quirks + +- Does not support `responseCode` or `bytesSent`. + +### Browser Quirks + +- __withCredentials__: _boolean_ that tells the browser to set the withCredentials flag on the XMLHttpRequest + +### Windows Quirks + +- An option parameter with empty/null value is excluded in the upload operation due to the Windows API design. + +## download + +__Parameters__: + +- __source__: URL of the server to download the file, as encoded by `encodeURI()`. + +- __target__: Filesystem url representing the file on the device. For backwards compatibility, this can also be the full path of the file on the device. (See [Backwards Compatibility Notes](#backwards-compatibility-notes) below) + +- __successCallback__: A callback that is passed a `FileEntry` object. _(Function)_ + +- __errorCallback__: A callback that executes if an error occurs when retrieving the `FileEntry`. Invoked with a `FileTransferError` object. _(Function)_ + +- __trustAllHosts__: Optional parameter, defaults to `false`. If set to `true`, it accepts all security certificates. This is useful because Android rejects self-signed security certificates. Not recommended for production use. Supported on Android and iOS. _(boolean)_ + +- __options__: Optional parameters, currently only supports headers (such as Authorization (Basic Authentication), etc). + +### Example + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + +### WP8 Quirks + +- Download requests is being cached by native implementation. To avoid caching, pass `if-Modified-Since` header to download method. + +### Browser Quirks + +- __withCredentials__: _boolean_ that tells the browser to set the withCredentials flag on the XMLHttpRequest + +## abort + +Aborts an in-progress transfer. The onerror callback is passed a FileTransferError object which has an error code of `FileTransferError.ABORT_ERR`. + +### Example + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +A `FileTransferError` object is passed to an error callback when an error occurs. + +### Properties + +- __code__: One of the predefined error codes listed below. (Number) + +- __source__: URL to the source. (String) + +- __target__: URL to the target. (String) + +- __http_status__: HTTP status code. This attribute is only available when a response code is received from the HTTP connection. (Number) + +- __body__ Response body. This attribute is only available when a response is received from the HTTP connection. (String) + +- __exception__: Either e.getMessage or e.toString (String) + +### Constants + +- 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +- 2 = `FileTransferError.INVALID_URL_ERR` +- 3 = `FileTransferError.CONNECTION_ERR` +- 4 = `FileTransferError.ABORT_ERR` +- 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Windows Quirks + +- The plugin implementation is based on [BackgroundDownloader](https://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.backgroundtransfer.backgrounddownloader.aspx)/[BackgroundUploader](https://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.backgroundtransfer.backgrounduploader.aspx), which entails the latency issues on Windows devices (creation/starting of an operation can take up to a few seconds). You can use XHR or [HttpClient](https://msdn.microsoft.com/en-us/library/windows/apps/windows.web.http.httpclient.aspx) as a quicker alternative for small downloads. + +## Backwards Compatibility Notes + +Previous versions of this plugin would only accept device-absolute-file-paths as the source for uploads, or as the target for downloads. These paths would typically be of the form: + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + +For backwards compatibility, these paths are still accepted, and if your application has recorded paths like these in persistent storage, then they can continue to be used. + +These paths were previously exposed in the `fullPath` property of `FileEntry` and `DirectoryEntry` objects returned by the File plugin. New versions of the File plugin however, no longer expose these paths to JavaScript. + +If you are upgrading to a new (1.0.0 or newer) version of File, and you have previously been using `entry.fullPath` as arguments to `download()` or `upload()`, then you will need to change your code to use filesystem URLs instead. + +`FileEntry.toURL()` and `DirectoryEntry.toURL()` return a filesystem URL of the form: + + cdvfile://localhost/persistent/path/to/file + +which can be used in place of the absolute file path in both `download()` and `upload()` methods. diff --git a/plugins/cordova-plugin-file-transfer/RELEASENOTES.md b/plugins/cordova-plugin-file-transfer/RELEASENOTES.md new file mode 100644 index 0000000..19591b1 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/RELEASENOTES.md @@ -0,0 +1,267 @@ + +# Release Notes + +### 1.5.1 (Apr 15, 2016) +* CB-10536 Removing flaky test assertions about abort callback latency +* Removing the expectation in `spec.34` for the transfer method to be called. +* CB-10978 Fix `file-transfer.tests` JSHint issues +* CB-10782 Occasional failure in file transfer tests causing mobilespec crash +* CB-10771 Fixing failure when empty string passed as a value for option parameter in upload function +* CB-10636 Add `JSHint` for plugins + +### 1.5.0 (Jan 15, 2016) +* CB-10208 Fix `file-transfer` multipart form data upload format on **Windows** +* CB-9837 Add data `URI` support to `file-transfer` upload on **iOS** +* CB-9600 `FileUploadOptions` params not posted on **iOS** +* CB-9840 Fallback `file-transfer` `uploadResponse` encoding to `latin1` in case not encoded with `UTF-8` on **iOS** +* CB-9840 Fallback `file-transfer` upload/download response encoding to `latin1` in case not encoded with `UTF-8` on **iOS** +* CB-8641 **Windows Phone 8.1** Some `file-transfer` plugin tests occasionally fail in `mobilespec` +* Adding linting and fixing linter warnings. Reducing timeouts to 7 seconds. +* CB-10100 updated file dependency to not grab new majors +* CB-7006 Empty file is created on file transfer if server response is 304 +* CB-10098 `filetransfer.spec.33` is faulty +* CB-9969 Filetransfer upload error deletes original file +* CB-10088 `filetransfer spec.10` and `spec.11` test is faulty +* CB-9969 Filetransfer upload error deletes original file +* CB-10086 There are two `spec.31` tests for `file-transfer` tests +* CB-10037 Add progress indicator to file-transfer manual tests +* CB-9563 Mulptipart form data is used even a header named `Content-Type` is present +* CB-8863 fix block usage of self + +### 1.4.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* [CB-9879](https://issues.apache.org/jira/browse/CB-9879) `getCookie`s can cause unhandled `NullPointerException` +* [CB-6928](https://issues.apache.org/jira/browse/CB-6928) Wrong behaviour transferring cacheable content +* [CB-51](https://issues.apache.org/jira/browse/CB-51) FileTransfer - Support `PUT` Method +* [CB-9906](https://issues.apache.org/jira/browse/CB-9906) cleanup duplicate code, removed 2nd `isWP8` declaration. +* [CB-9950](https://issues.apache.org/jira/browse/CB-9950) Unpend Filetransfer spec.27 on **wp8** as custom headers are now supported +* [CB-9843](https://issues.apache.org/jira/browse/CB-9843) Added **wp8** quirk to test spec 12 +* Fixing contribute link. +* [CB-8431](https://issues.apache.org/jira/browse/CB-8431) File Transfer tests crash on **Android Lolipop** +* [CB-9790](https://issues.apache.org/jira/browse/CB-9790) Align `FileUploadOptions` `fileName` and `mimeType` default parameter values to the docs on **iOS** +* [CB-9385](https://issues.apache.org/jira/browse/CB-9385) Return `FILE_NOT_FOUND_ERR` when receiving `404` code on **iOS** +* [CB-9791](https://issues.apache.org/jira/browse/CB-9791) Decreased download and upload tests timeout + +### 1.3.0 (Sep 18, 2015) +* Found issue where : is accepted as a valid header, this is obviously wrong +* [CB-9562](https://issues.apache.org/jira/browse/CB-9562) Fixed incorrect headers handling on Android +* Fixing headers so they don't accept non-ASCII +* updated tests to use cordova apache vm +* [CB-9493](https://issues.apache.org/jira/browse/CB-9493) Fix file paths in file-transfer manual tests +* [CB-8816](https://issues.apache.org/jira/browse/CB-8816) Add cdvfile:// support on windows +* [CB-9376](https://issues.apache.org/jira/browse/CB-9376) Fix FileTransfer plugin manual tests issue - 'undefined' in paths + +### 1.2.1 (Jul 7, 2015) +* [CB-9275](https://issues.apache.org/jira/browse/CB-9275) [WP8] Fix build failure on WP8 by using reflection to detect presence of JSON.NET based serialization +* Updated code per code review. +* Updated documentation for browser +* Added option to allow for passing cookies automatically in the browser + +### 1.2.0 (Jun 17, 2015) +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-file-transfer documentation translation: cordova-plugin-file-transfer +* [CB-6503](https://issues.apache.org/jira/browse/CB-6503): Null pointer check for headers in upload (This closes #27) +* [CB-6503](https://issues.apache.org/jira/browse/CB-6503): Allow payload content-types other than multipart/form-data to be used for upload +* Fix NoSuchMethodException looking up cookies. +* fix npm md issue +* [CB-8951](https://issues.apache.org/jira/browse/CB-8951) (wp8) Handle exceptions in download() and upload() again +* [wp8] Relaxed engine version requirement, using reflection to see if methods are available +* Check for the existence of Json.net assembly to determin how we deserialize our headers. +* relax engine requirement to allow -dev versions +* Remove verbose console log messages +* fix bad commit (mine) for cordova-wp8@4.0.0 engine req +* bump required cordova-wp8 version to 4.0.0 +* This closes #80, This closes #12 +* fix failing test resulting from overlapping async calls +* [CB-8721](https://issues.apache.org/jira/browse/CB-8721) Fixes incorrect headers and upload params parsing on wp8 +* Replace all slashes in windows path + +### 1.1.0 (May 06, 2015) +* [CB-8951](https://issues.apache.org/jira/browse/CB-8951) Fixed crash related to headers parsing on **wp8** +* [CB-8933](https://issues.apache.org/jira/browse/CB-8933) Increased download and upload test timeout +* [CB-6313](https://issues.apache.org/jira/browse/CB-6313) **wp8**: Extra boundary in upload +* [CB-8761](https://issues.apache.org/jira/browse/CB-8761) **wp8**: Copy cookies from WebBrowser + +### 1.0.0 (Apr 15, 2015) +* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) bumped version of file dependency +* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump +* [CB-8641](https://issues.apache.org/jira/browse/CB-8641) Fixed tests to pass on windows and wp8 +* [CB-8583](https://issues.apache.org/jira/browse/CB-8583) Forces download to overwrite existing target file +* [CB-8589](https://issues.apache.org/jira/browse/CB-8589) Fixes upload failure when server's response doesn't contain any data +* [CB-8747](https://issues.apache.org/jira/browse/CB-8747) updated dependency, added peer dependency +* [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 +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* [CB-8654](https://issues.apache.org/jira/browse/CB-8654) Note WP8 download requests caching in docs +* [CB-8590](https://issues.apache.org/jira/browse/CB-8590) (Windows) Fixed download.onprogress.lengthComputable +* [CB-8566](https://issues.apache.org/jira/browse/CB-8566) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-file-transfer documentation translation: cordova-plugin-file-transfer +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8495](https://issues.apache.org/jira/browse/CB-8495) Fixed wp8 and wp81 test failures +* [CB-7957](https://issues.apache.org/jira/browse/CB-7957) Adds support for `browser` platform +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.5.0 (take 2) +* Fixes typo, introduced in https://github.com/apache/cordova-plugin-file-transfer/commit/bc43b46 +* [CB-8407](https://issues.apache.org/jira/browse/CB-8407) Use File proxy to construct valid FileEntry for download success callback +* [CB-8407](https://issues.apache.org/jira/browse/CB-8407) Removes excess path to native path conversion in download method +* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.5.0 +* [CB-7957](https://issues.apache.org/jira/browse/CB-7957) Adds support for `browser` platform +* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) Fixes JSHint and formatting issues +* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) Updates tests and documentation +* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) Rewrite upload method to support progress events properly +* android: Fix error reporting for unknown uri type on sourceUri instead of targetUri + +### 0.5.0 (Feb 04, 2015) +* [CB-8407](https://issues.apache.org/jira/browse/CB-8407) windows: Fix download of `ms-appdata:///` URIs +* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) windows: Rewrite upload method to support progress events properly +* [CB-5059](https://issues.apache.org/jira/browse/CB-5059) android: Add a CookieManager abstraction for pluggable webviews +* ios: Fix compile warning about implicity int conversion +* [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 DLog macro rather than CordovaLib version +* [CB-8296](https://issues.apache.org/jira/browse/CB-8296) ios: Fix crash when upload fails and file is not yet created (close #57) +* Document "body" property on FileTransferError +* [CB-7912](https://issues.apache.org/jira/browse/CB-7912) ios, android: Update to work with whitelist plugins in Cordova 4.x +* Error callback should always be called with the FileTransferError object, and not just the code +* windows: alias appData to Windows.Storage.ApplicationData.current +* [CB-8093](https://issues.apache.org/jira/browse/CB-8093) Fixes incorrect FileTransferError returned in case of download failure + +### 0.4.8 (Dec 02, 2014) +* [CB-8021](https://issues.apache.org/jira/browse/CB-8021) - adds documentation for `httpMethod` to `doc/index.md`. However, translations still need to be addressed. +* [CB-7223](https://issues.apache.org/jira/browse/CB-7223) spec.27 marked pending for **wp8** +* [CB-6900](https://issues.apache.org/jira/browse/CB-6900) fixed `spec.7` for **wp8** +* [CB-7944](https://issues.apache.org/jira/browse/CB-7944) Pended unsupported auto tests for *Windows* +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-file-transfer documentation translation: cordova-plugin-file-transfer + +### 0.4.7 (Oct 03, 2014) +* Construct proper FileEntry with nativeURL property set +* [CB-7532](https://issues.apache.org/jira/browse/CB-7532) Handle non-existent download dirs properly +* [CB-7529](https://issues.apache.org/jira/browse/CB-7529) Adds support for 'ms-appdata' URIs for windows + +### 0.4.6 (Sep 17, 2014) +* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-file-transfer documentation translation +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-file-transfer documentation translation +* [CB-7423](https://issues.apache.org/jira/browse/CB-7423) fix spec28,29 lastProgressEvent not visible to afterEach function +* Amazon related changes. +* Remove dupe file windows+windows8 both use the same one +* [CB-7316](https://issues.apache.org/jira/browse/CB-7316) Updates docs with actual information. +* [CB-7316](https://issues.apache.org/jira/browse/CB-7316) Adds support for Windows platform, moves \*Proxy files to proper directory. +* [CB-7316](https://issues.apache.org/jira/browse/CB-7316) Improves current specs compatibility: +* added documentation for new test +* [CB-6466](https://issues.apache.org/jira/browse/CB-6466) Fix failing test due to recent url change +* [CB-6466](https://issues.apache.org/jira/browse/CB-6466) created mobile-spec test +* Renamed test dir, added nested plugin.xml and test +* Fixed failing spec.19 on wp8 +* added documentation to manual tests +* [CB-6961](https://issues.apache.org/jira/browse/CB-6961) port file-transfer tests to framework + +### 0.4.5 (Aug 06, 2014) +* Upload parameters out of order +* **FirefoxOS** initial implementation +* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): Expose FileTransferError.exception to application +* [CB-6928](https://issues.apache.org/jira/browse/CB-6928): Add new error code to documentation +* [CB-6928](https://issues.apache.org/jira/browse/CB-6928): Handle 304 status code +* [CB-6928](https://issues.apache.org/jira/browse/CB-6928): Open output stream only if it's necessary. +* [BlackBerry10] Minor doc correction +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* [Windows8] upload uses the provided fileName or the actual fileName +* [CB-2420](https://issues.apache.org/jira/browse/CB-2420) [Windows8] honor fileKey and param options. This closes #15 +* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): Update new docs to match AlexNennker's changes in PR30 +* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): Continue previous commit with one new instance (This closes #30) +* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): add the exception text to the error object +* [CB-6890](https://issues.apache.org/jira/browse/CB-6890): Fix pluginManager access for 4.0.x branch + +### 0.4.4 (Jun 05, 2014) +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #21 +* ubuntu: support 'cdvfile' URI +* [CB-6802](https://issues.apache.org/jira/browse/CB-6802) Add license +* Upload progress now works also for second file +* [CB-6706](https://issues.apache.org/jira/browse/CB-6706): Relax dependency on file plugin +* [CB-3440](https://issues.apache.org/jira/browse/CB-3440) [BlackBerry10] Update implementation to use modules from file plugin +* [CB-6378](https://issues.apache.org/jira/browse/CB-6378) Use connection.disconnect() instead of stream.close() for thread-safety +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* [CB-6466](https://issues.apache.org/jira/browse/CB-6466) Auto-create directories in download +* [CB-6494](https://issues.apache.org/jira/browse/CB-6494) android: Fix upload of KitKat content URIs +* Upleveled from android port with following commits: 3c1ff16 Andrew Grieve - [CB-5762](https://issues.apache.org/jira/browse/CB-5762) android: Fix lengthComputable set wrong for gzip downloads 8374b3d Colin Mahoney - [CB-5631](https://issues.apache.org/jira/browse/CB-5631) Removed SimpleTrackingInputStream.read(byte[] buffer) 6f91ac3 Bas Bosman - [CB-4907](https://issues.apache.org/jira/browse/CB-4907) Close stream when we're finished with it 651460f Christoph Neumann - [CB-6000](https://issues.apache.org/jira/browse/CB-6000) Nginx rejects Content-Type without a space before "boundary". 35f80e4 Ian Clelland - [CB-6050](https://issues.apache.org/jira/browse/CB-6050): Use instance method on actual file plugin object to get FileEntry to return on download +* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 0.4.1 + +### 0.4.3 (Apr 17, 2014) +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422) [windows8] use cordova/exec/proxy +* iOS: Fix error where files were not removed on abort +* [CB-5175](https://issues.apache.org/jira/browse/CB-5175): [ios] CDVFileTransfer asynchronous download (Fixes #24) +* [ios] Cast id references to NSURL to avoid compiler warnings (Fixes: apache/cordova-plugin-file-transfer#18) +* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit +* [CB-5762](https://issues.apache.org/jira/browse/CB-5762): [FireOS] android: Fix lengthComputable set wrong for gzip downloads +* [CB-5631](https://issues.apache.org/jira/browse/CB-5631): [FireOS] Removed SimpleTrackingInputStream.read(byte[] buffer) +* [CB-4907](https://issues.apache.org/jira/browse/CB-4907): [FireOS] Close stream when we're finished with it +* [CB-6000](https://issues.apache.org/jira/browse/CB-6000): [FireOS] Nginx rejects Content-Type without a space before "boundary". +* [CB-6050](https://issues.apache.org/jira/browse/CB-6050): [FireOS] Use instance method on actual file plugin object to get FileEntry to return on download +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers + +### 0.4.2 (Feb 28, 2014) +* [CB-6106](https://issues.apache.org/jira/browse/CB-6106) Ensure that nativeURL is used by file transfer download +* iOS: Fix default value for trustAllHosts on iOS (YES->NO) +* [CB-6059](https://issues.apache.org/jira/browse/CB-6059) iOS: Stop FileTransfer.download doing IO on the UI thread. +* [CB-5588](https://issues.apache.org/jira/browse/CB-5588) iOS: Add response headers to upload result +* [CB-2190](https://issues.apache.org/jira/browse/CB-2190) iOS: Make backgroundTaskId apply to downloads as well. Move backgroundTaskId to the delegate. +* [CB-6050](https://issues.apache.org/jira/browse/CB-6050) Android: Use instance method on actual file plugin object to get FileEntry to return on download +* [CB-6000](https://issues.apache.org/jira/browse/CB-6000) Android: Nginx rejects Content-Type without a space before "boundary". +* [CB-4907](https://issues.apache.org/jira/browse/CB-4907) Android: Close stream when we're finished with it +* [CB-6022](https://issues.apache.org/jira/browse/CB-6022) Add backwards-compatibility notes to doc + +### 0.4.1 (Feb 05, 2014) +* [CB-5365](https://issues.apache.org/jira/browse/CB-5365) Remove unused exception var to prevent warnings? +* [CB-2421](https://issues.apache.org/jira/browse/CB-2421) explicitly write the bytesSent,responseCode,result to the FileUploadResult pending release of cordova-plugin-file dependency, added some sanity checks for callbacks +* iOS: Update for new file plugin api +* [CB-5631](https://issues.apache.org/jira/browse/CB-5631) Removed SimpleTrackingInputStream.read(byte[] buffer) +* [CB-5762](https://issues.apache.org/jira/browse/CB-5762) android: Fix lengthComputable set wrong for gzip downloads +* [CB-4899](https://issues.apache.org/jira/browse/CB-4899) [BlackBerry10] Improve binary file transfer download +* Delete stale test/ directory +* [CB-5722](https://issues.apache.org/jira/browse/CB-5722) [BlackBerry10] Update upload function to use native file object +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs +* Remove @1 designation from file plugin dependency until pushed to npm +* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Update to work with filesystem URLs + +### 0.4.0 (Dec 4, 2013) +* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Partial revert; we're not ready yet for FS urls +* add ubuntu platform +* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Minor version bump +* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Update FileTransfer plugin to accept filesystem urls +* Added amazon-fireos platform. Change to use amazon-fireos as the platform if the user agen string contains 'cordova-amazon-fireos' + +### 0.3.4 (Oct 28, 2013) +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for file transfer plugin +* [CB-5010](https://issues.apache.org/jira/browse/CB-5010) Incremented plugin version on dev branch. + +### 0.3.3 (Oct 9, 2013) +* removed un-needed undef check +* Fix missing headers in Windows 8 upload proxy +* Fix missing headers in Windows 8 Proxy +* Fix Windows 8 HTMLAnchorElement return host:80 which force Basic Auth Header to replace options Auth Header +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.3.2 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [windows8] commandProxy was moved +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) updating core references +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.file-transfer to org.apache.cordova.file-transfer and updating dependency +* Rename CHANGELOG.md -> RELEASENOTES.md diff --git a/plugins/cordova-plugin-file-transfer/doc/de/README.md b/plugins/cordova-plugin-file-transfer/doc/de/README.md new file mode 100644 index 0000000..03191cb --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/de/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +Plugin-Dokumentation: + +Dieses Plugin ermöglicht Ihnen zum Hochladen und Herunterladen von Dateien. + +Dieses Plugin wird global `FileTransfer`, `FileUploadOptions` Konstruktoren definiert. + +Obwohl im globalen Gültigkeitsbereich, sind sie nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Installation + + cordova plugin add cordova-plugin-file-transfer + + +## Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS ** + * iOS + * Windows Phone 7 und 8 * + * Windows 8 + * Windows + +\ * *Unterstützen keine `Onprogress` noch `abort()` * + +\ ** * `Onprogress` nicht unterstützt* + +# FileTransfer + +Das `FileTransfer` -Objekt stellt eine Möglichkeit zum Hochladen von Dateien, die mithilfe einer HTTP-mehrteiligen POST oder PUT-Anforderung, und auch Dateien herunterladen. + +## Eigenschaften + + * **OnProgress**: aufgerufen, wobei ein `ProgressEvent` wann wird eine neue Datenmenge übertragen. *(Funktion)* + +## Methoden + + * **Upload**: sendet eine Datei an einen Server. + + * **Download**: lädt eine Datei vom Server. + + * **abort**: Abbruch eine Übertragung in Bearbeitung. + +## Upload + +**Parameter**: + + * **FileURL**: Dateisystem-URL, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten) + + * **Server**: URL des Servers, die Datei zu empfangen, wie kodiert`encodeURI()`. + + * **successCallback**: ein Rückruf, der ein `FileUploadResult`-Objekt übergeben wird. *(Funktion)* + + * **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileUploadResult`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)* + + * **Optionen**: optionale Parameter *(Objekt)*. Gültige Schlüssel: + + * **FileKey**: der Name des Form-Elements. Wird standardmäßig auf `file` . (DOM-String und enthält) + * **Dateiname**: der Dateiname beim Speichern der Datei auf dem Server verwendet. Wird standardmäßig auf `image.jpg` . (DOM-String und enthält) + * **httpMethod**: die HTTP-Methode, die-entweder `PUT` oder `POST`. Der Standardwert ist `POST`. (DOM-String und enthält) + * **mimeType**: den Mime-Typ der Daten hochzuladen. Standardwerte auf `Image/Jpeg`. (DOM-String und enthält) + * **params**: eine Reihe von optionalen Schlüssel/Wert-Paaren in der HTTP-Anforderung übergeben. (Objekt) + * **chunkedMode**: ob die Daten in "Chunked" streaming-Modus hochladen. Der Standardwert ist `true`. (Boolean) + * **headers**: eine Karte von Header-Name-Header-Werte. Verwenden Sie ein Array, um mehr als einen Wert anzugeben. Auf iOS, FireOS und Android wenn ein Content-Type-Header vorhanden ist, werden mehrteilige Formulardaten nicht verwendet werden. (Object) + * **httpMethod**: die HTTP-Methode zu verwenden, z.B. POST oder PUT. Der Standardwert ist `POST`. (DOM-String enthält) + + * **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)* + +### Beispiel + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### Beispiel mit hochladen Kopf- und Progress-Ereignisse (Android und iOS nur) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Ein `FileUploadResult`-Objekt wird an den Erfolg-Rückruf des `Objekts FileTransfer`-Upload()-Methode übergeben. + +### Eigenschaften + + * **BytesSent**: die Anzahl der Bytes, die als Teil des Uploads an den Server gesendet. (lange) + + * **ResponseCode**: die HTTP-Response-Code vom Server zurückgegeben. (lange) + + * **response**: der HTTP-Antwort vom Server zurückgegeben. (DOM-String und enthält) + + * **Header**: die HTTP-Response-Header vom Server. (Objekt) + + * Derzeit unterstützt auf iOS nur. + +### iOS Macken + + * Unterstützt keine `responseCode` oder`bytesSent`. + +## Download + +**Parameter**: + + * **source**: URL des Servers, um die Datei herunterzuladen, wie kodiert`encodeURI()`. + + * **target**: Dateisystem-Url, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten) + + * **SuccessCallback**: ein Rückruf, der übergeben wird ein `FileEntry` Objekt. *(Funktion)* + + * **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileEntry`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)* + + * **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)* + + * **Options**: optionale Parameter, derzeit nur unterstützt Kopfzeilen (z. B. Autorisierung (Standardauthentifizierung), etc.). + +### Beispiel + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### WP8 Macken + + * Downloaden anfordert, wird von native Implementierung zwischengespeichert wird. Um zu vermeiden, Zwischenspeicherung, übergeben `If-Modified-Since` Header Methode herunterladen. + +## abort + +Bricht einen in-Progress-Transfer. Der Onerror-Rückruf wird ein FileTransferError-Objekt übergeben, die einen Fehlercode FileTransferError.ABORT_ERR hat. + +### Beispiel + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +Ein `FileTransferError`-Objekt wird an eine Fehler-Callback übergeben, wenn ein Fehler auftritt. + +### Eigenschaften + + * **Code**: einer der vordefinierten Fehlercodes aufgeführt. (Anzahl) + + * **Quelle**: URL der Quelle. (String) + + * **Ziel**: URL zum Ziel. (String) + + * **HTTP_STATUS**: HTTP-Statuscode. Dieses Attribut ist nur verfügbar, wenn ein Response-Code aus der HTTP-Verbindung eingeht. (Anzahl) + + * **body** Antworttext. Dieses Attribut ist nur verfügbar, wenn eine Antwort von der HTTP-Verbindung eingeht. (String) + + * **exception**: entweder e.getMessage oder e.toString (String) + +### Konstanten + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Hinweise rückwärts Kompatibilität + +Frühere Versionen des Plugins würde nur Gerät-Absolute-Dateipfade als Quelle für Uploads oder als Ziel für Downloads übernehmen. Diese Pfade wäre in der Regel der form + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Für rückwärts Kompatibilität, diese Pfade noch akzeptiert werden, und wenn Ihre Anwendung Pfade wie diese im permanenten Speicher aufgezeichnet hat, dann sie können weiter verwendet werden. + +Diese Pfade waren zuvor in der Eigenschaft `fullPath` `FileEntry` und `DirectoryEntry`-Objekte, die durch das Plugin Datei zurückgegeben ausgesetzt. Neue Versionen der die Datei-Erweiterung, jedoch nicht länger werden diese Pfade zu JavaScript. + +Wenn Sie ein auf eine neue Upgrade (1.0.0 oder neuere) Version der Datei, und Sie haben zuvor mit `entry.fullPath` als Argumente `download()` oder `upload()`, dann ändern Sie den Code, um die Dateisystem-URLs verwenden müssen. + +`FileEntry.toURL()` und `DirectoryEntry.toURL()` zurück, eine Dateisystem-URL in der form + + cdvfile://localhost/persistent/path/to/file + + +die anstelle der absoluten Dateipfad in `download()` und `upload()` Methode verwendet werden kann. \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/de/index.md b/plugins/cordova-plugin-file-transfer/doc/de/index.md new file mode 100644 index 0000000..4081503 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/de/index.md @@ -0,0 +1,302 @@ + + +# cordova-plugin-file-transfer + +Dieses Plugin ermöglicht Ihnen zum Hochladen und Herunterladen von Dateien. + +Dieses Plugin wird global `FileTransfer`, `FileUploadOptions` Konstruktoren definiert. + +Obwohl im globalen Gültigkeitsbereich, sind sie nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Installation + + cordova plugin add cordova-plugin-file-transfer + + +## Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS ** +* iOS +* Windows Phone 7 und 8 * +* Windows 8 +* Windows + +* *Unterstützen keine `onprogress` noch `abort()`* + +* * *`onprogress` nicht unterstützt* + +# FileTransfer + +Das `FileTransfer`-Objekt bietet eine Möglichkeit zum Hochladen von Dateien, die mithilfe einer HTTP-Anforderung für mehrteiligen POST sowie Informationen zum Herunterladen von Dateien sowie. + +## Eigenschaften + +* **OnProgress**: aufgerufen, wobei ein `ProgressEvent` wann wird eine neue Datenmenge übertragen. *(Funktion)* + +## Methoden + +* **Upload**: sendet eine Datei an einen Server. + +* **Download**: lädt eine Datei vom Server. + +* **abort**: Abbruch eine Übertragung in Bearbeitung. + +## Upload + +**Parameter**: + +* **FileURL**: Dateisystem-URL, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten) + +* **Server**: URL des Servers, die Datei zu empfangen, wie kodiert`encodeURI()`. + +* **successCallback**: ein Rückruf, der ein `FileUploadResult`-Objekt übergeben wird. *(Funktion)* + +* **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileUploadResult`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)* + +* **Optionen**: optionale Parameter *(Objekt)*. Gültige Schlüssel: + + * **FileKey**: der Name des Form-Elements. Wird standardmäßig auf `file` . (DOM-String und enthält) + * **Dateiname**: der Dateiname beim Speichern der Datei auf dem Server verwendet. Wird standardmäßig auf `image.jpg` . (DOM-String und enthält) + * **httpMethod**: die HTTP-Methode, die-entweder `PUT` oder `POST`. Der Standardwert ist `POST`. (DOM-String und enthält) + * **mimeType**: den Mime-Typ der Daten hochzuladen. Standardwerte auf `Image/Jpeg`. (DOM-String und enthält) + * **params**: eine Reihe von optionalen Schlüssel/Wert-Paaren in der HTTP-Anforderung übergeben. (Objekt) + * **chunkedMode**: ob die Daten in "Chunked" streaming-Modus hochladen. Der Standardwert ist `true`. (Boolean) + * **headers**: eine Karte von Header-Name-Header-Werte. Verwenden Sie ein Array, um mehr als einen Wert anzugeben. (Objekt) + +* **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)* + +### Beispiel + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### Beispiel mit hochladen Kopf- und Progress-Ereignisse (Android und iOS nur) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Ein `FileUploadResult`-Objekt wird an den Erfolg-Rückruf des `Objekts FileTransfer`-Upload()-Methode übergeben. + +### Eigenschaften + +* **BytesSent**: die Anzahl der Bytes, die als Teil des Uploads an den Server gesendet. (lange) + +* **ResponseCode**: die HTTP-Response-Code vom Server zurückgegeben. (lange) + +* **response**: der HTTP-Antwort vom Server zurückgegeben. (DOM-String und enthält) + +* **Header**: die HTTP-Response-Header vom Server. (Objekt) + + * Derzeit unterstützt auf iOS nur. + +### iOS Macken + +* Unterstützt keine `responseCode` oder`bytesSent`. + +## Download + +**Parameter**: + +* **source**: URL des Servers, um die Datei herunterzuladen, wie kodiert`encodeURI()`. + +* **target**: Dateisystem-Url, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten) + +* **SuccessCallback**: ein Rückruf, der übergeben wird ein `FileEntry` Objekt. *(Funktion)* + +* **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileEntry`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)* + +* **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)* + +* **Options**: optionale Parameter, derzeit nur unterstützt Kopfzeilen (z. B. Autorisierung (Standardauthentifizierung), etc.). + +### Beispiel + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## abort + +Bricht einen in-Progress-Transfer. Der Onerror-Rückruf wird ein FileTransferError-Objekt übergeben, die einen Fehlercode FileTransferError.ABORT_ERR hat. + +### Beispiel + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +Ein `FileTransferError`-Objekt wird an eine Fehler-Callback übergeben, wenn ein Fehler auftritt. + +### Eigenschaften + +* **Code**: einer der vordefinierten Fehlercodes aufgeführt. (Anzahl) + +* **Quelle**: URL der Quelle. (String) + +* **Ziel**: URL zum Ziel. (String) + +* **HTTP_STATUS**: HTTP-Statuscode. Dieses Attribut ist nur verfügbar, wenn ein Response-Code aus der HTTP-Verbindung eingeht. (Anzahl) + +* **body** Antworttext. Dieses Attribut ist nur verfügbar, wenn eine Antwort von der HTTP-Verbindung eingeht. (String) + +* **exception**: entweder e.getMessage oder e.toString (String) + +### Konstanten + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Hinweise rückwärts Kompatibilität + +Frühere Versionen des Plugins würde nur Gerät-Absolute-Dateipfade als Quelle für Uploads oder als Ziel für Downloads übernehmen. Diese Pfade wäre in der Regel der form + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Für rückwärts Kompatibilität, diese Pfade noch akzeptiert werden, und wenn Ihre Anwendung Pfade wie diese im permanenten Speicher aufgezeichnet hat, dann sie können weiter verwendet werden. + +Diese Pfade waren zuvor in der Eigenschaft `fullPath` `FileEntry` und `DirectoryEntry`-Objekte, die durch das Plugin Datei zurückgegeben ausgesetzt. Neue Versionen der die Datei-Erweiterung, jedoch nicht länger werden diese Pfade zu JavaScript. + +Wenn Sie ein auf eine neue Upgrade (1.0.0 oder neuere) Version der Datei, und Sie haben zuvor mit `entry.fullPath` als Argumente `download()` oder `upload()`, dann ändern Sie den Code, um die Dateisystem-URLs verwenden müssen. + +`FileEntry.toURL()` und `DirectoryEntry.toURL()` zurück, eine Dateisystem-URL in der form + + cdvfile://localhost/persistent/path/to/file + + +die anstelle der absoluten Dateipfad in `download()` und `upload()` Methode verwendet werden kann. diff --git a/plugins/cordova-plugin-file-transfer/doc/es/README.md b/plugins/cordova-plugin-file-transfer/doc/es/README.md new file mode 100644 index 0000000..1c8ee3f --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/es/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +Documentación del plugin: + +Este plugin te permite cargar y descargar archivos. + +Este plugin define global `FileTransfer` , `FileUploadOptions` constructores. + +Aunque en el ámbito global, no están disponibles hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Instalación + + cordova plugin add cordova-plugin-file-transfer + + +## Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Explorador + * Firefox OS ** + * iOS + * Windows Phone 7 y 8 * + * Windows 8 + * Windows + +\ * *No soporta `onprogress` ni `abort()` * + +\ ** *No soporta `onprogress` * + +# FileTransfer + +El objeto `FileTransfer` proporciona una manera para subir archivos utilizando una varias parte solicitud HTTP POST o PUT y descargar archivos, así. + +## Propiedades + + * **OnProgress**: llama con un `ProgressEvent` cuando se transfiere un nuevo paquete de datos. *(Función)* + +## Métodos + + * **cargar**: envía un archivo a un servidor. + + * **Descargar**: descarga un archivo del servidor. + + * **abortar**: aborta una transferencia en curso. + +## subir + +**Parámetros**: + + * **fileURL**: URL de Filesystem que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo) + + * **servidor**: dirección URL del servidor para recibir el archivo, como codificada por`encodeURI()`. + + * **successCallback**: una devolución de llamada que se pasa un `FileUploadResult` objeto. *(Función)* + + * **errorCallback**: una devolución de llamada que se ejecuta si se produce un error recuperar la `FileUploadResult` . Invocado con un `FileTransferError` objeto. *(Función)* + + * **Opciones**: parámetros opcionales *(objeto)*. Teclas válidas: + + * **fileKey**: el nombre del elemento de formulario. Por defecto es `file` . (DOMString) + * **nombre de archivo**: el nombre del archivo a utilizar al guardar el archivo en el servidor. Por defecto es `image.jpg` . (DOMString) + * **httpMethod**: método HTTP el utilizar - o `PUT` o `POST` . Por defecto es `POST` . (DOMString) + * **mimeType**: el tipo mime de los datos para cargar. Por defecto es `image/jpeg` . (DOMString) + * **params**: un conjunto de pares clave/valor opcional para pasar en la petición HTTP. (Objeto) + * **chunkedMode**: Si desea cargar los datos en modo de transmisión fragmentado. Por defecto es `true` . (Boolean) + * **headers**: un mapa de nombre de encabezado/valores de encabezado Utilice una matriz para especificar más de un valor. En iOS FireOS y Android, si existe un encabezado llamado Content-Type, datos de un formulario multipart no se utilizará. (Object) + * **httpMethod**: HTTP el método a utilizar por ejemplo POST o poner. Por defecto `el POST`. (DOMString) + + * **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil ya que Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)* + +### Ejemplo + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### Ejemplo con cabeceras de subir y eventos de progreso (Android y iOS solamente) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +A `FileUploadResult` objeto se pasa a la devolución del éxito de la `FileTransfer` del objeto `upload()` método. + +### Propiedades + + * **bytesSent**: el número de bytes enviados al servidor como parte de la carga. (largo) + + * **responseCode**: código de respuesta HTTP el devuelto por el servidor. (largo) + + * **respuesta**: respuesta el HTTP devuelto por el servidor. (DOMString) + + * **cabeceras**: cabeceras de respuesta HTTP el por el servidor. (Objeto) + + * Actualmente compatible con iOS solamente. + +### iOS rarezas + + * No es compatible con `responseCode` o`bytesSent`. + +## descargar + +**Parámetros**: + + * **fuente**: dirección URL del servidor para descargar el archivo, como codificada por`encodeURI()`. + + * **objetivo**: Filesystem url que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo) + + * **successCallback**: una devolución de llamada que se pasa un `FileEntry` objeto. *(Función)* + + * **errorCallback**: una devolución de llamada que se ejecuta si se produce un error al recuperar los `FileEntry` . Invocado con un `FileTransferError` objeto. *(Función)* + + * **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil porque Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)* + + * **Opciones**: parámetros opcionales, actualmente sólo soporta cabeceras (como autorización (autenticación básica), etc.). + +### Ejemplo + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### Rarezas de WP8 + + * Descargar pide se almacena en caché por aplicación nativa. Para evitar el almacenamiento en caché, pasar `if-Modified-Since` encabezado para descargar el método. + +## abortar + +Aborta a una transferencia en curso. El callback onerror se pasa un objeto FileTransferError que tiene un código de error de FileTransferError.ABORT_ERR. + +### Ejemplo + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +A `FileTransferError` objeto se pasa a un callback de error cuando se produce un error. + +### Propiedades + + * **código**: uno de los códigos de error predefinido enumerados a continuación. (Número) + + * **fuente**: URL a la fuente. (String) + + * **objetivo**: URL a la meta. (String) + + * **HTTP_STATUS**: código de estado HTTP. Este atributo sólo está disponible cuando se recibe un código de respuesta de la conexión HTTP. (Número) + + * **cuerpo** Cuerpo de la respuesta. Este atributo sólo está disponible cuando se recibe una respuesta de la conexión HTTP. (String) + + * **excepción**: cualquier e.getMessage o e.toString (String) + +### Constantes + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Al revés notas de compatibilidad + +Versiones anteriores de este plugin sólo aceptaría dispositivo-absoluto-archivo-rutas como la fuente de carga, o como destino para las descargas. Estos caminos normalmente sería de la forma + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Para atrás compatibilidad, estos caminos son aceptados todavía, y si su solicitud ha grabado caminos como éstos en almacenamiento persistente, entonces pueden seguir utilizarse. + +Estos caminos fueron expuestos anteriormente en el `fullPath` propiedad de `FileEntry` y `DirectoryEntry` objetos devueltos por el plugin de archivo. Las nuevas versiones del archivo plugin, sin embargo, ya no exponen estos caminos a JavaScript. + +Si va a actualizar a una nueva (1.0.0 o más reciente) versión del archivo y previamente han estado utilizando `entry.fullPath` como argumentos para `download()` o `upload()` , entonces tendrá que cambiar su código para usar URLs de sistema de archivos en su lugar. + +`FileEntry.toURL()`y `DirectoryEntry.toURL()` devolver un filesystem dirección URL de la forma + + cdvfile://localhost/persistent/path/to/file + + +que puede ser utilizado en lugar de la ruta del archivo absoluta tanto en `download()` y `upload()` los métodos. \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/es/index.md b/plugins/cordova-plugin-file-transfer/doc/es/index.md new file mode 100644 index 0000000..981c991 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/es/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-file-transfer + +Este plugin te permite cargar y descargar archivos. + +Este plugin define global `FileTransfer` , `FileUploadOptions` constructores. + +Aunque en el ámbito global, no están disponibles hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(FileTransfer)}; + + +## Instalación + + Cordova plugin añade cordova-plugin-file-transferencia + + +## Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Explorador +* Firefox OS ** +* iOS +* Windows Phone 7 y 8 * +* Windows 8 +* Windows + +* *No son compatibles con `onprogress` ni `abort()` * + +** *No son compatibles con `onprogress` * + +# FileTransfer + +El `FileTransfer` objeto proporciona una manera de subir archivos mediante una solicitud HTTP de POST varias parte y para descargar archivos. + +## Propiedades + +* **OnProgress**: llama con un `ProgressEvent` cuando se transfiere un nuevo paquete de datos. *(Función)* + +## Métodos + +* **cargar**: envía un archivo a un servidor. + +* **Descargar**: descarga un archivo del servidor. + +* **abortar**: aborta una transferencia en curso. + +## subir + +**Parámetros**: + +* **fileURL**: URL de Filesystem que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo) + +* **servidor**: dirección URL del servidor para recibir el archivo, como codificada por`encodeURI()`. + +* **successCallback**: una devolución de llamada que se pasa un `FileUploadResult` objeto. *(Función)* + +* **errorCallback**: una devolución de llamada que se ejecuta si se produce un error recuperar la `FileUploadResult` . Invocado con un `FileTransferError` objeto. *(Función)* + +* **Opciones**: parámetros opcionales *(objeto)*. Teclas válidas: + + * **fileKey**: el nombre del elemento de formulario. Por defecto es `file` . (DOMString) + * **nombre de archivo**: el nombre del archivo a utilizar al guardar el archivo en el servidor. Por defecto es `image.jpg` . (DOMString) + * **httpMethod**: método HTTP el utilizar - o `PUT` o `POST` . Por defecto es `POST` . (DOMString) + * **mimeType**: el tipo mime de los datos para cargar. Por defecto es `image/jpeg` . (DOMString) + * **params**: un conjunto de pares clave/valor opcional para pasar en la petición HTTP. (Objeto) + * **chunkedMode**: Si desea cargar los datos en modo de transmisión fragmentado. Por defecto es `true` . (Boolean) + * **cabeceras**: un mapa de valores de encabezado nombre/cabecera. Utilice una matriz para especificar más de un valor. (Objeto) + +* **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil ya que Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)* + +### Ejemplo + + // !! Asume fileURL variable contiene una dirección URL válida a un archivo de texto en el dispositivo, / / por ejemplo, ganar var cdvfile://localhost/persistent/path/to/file.txt = function (r) {console.log ("código =" + r.responseCode); + Console.log ("respuesta =" + r.response); + Console.log ("Sent =" + r.bytesSent);} + + var fallar = function (error) {alert ("ha ocurrido un error: código =" + error.code); + Console.log ("error al cargar el origen" + error.source); + Console.log ("upload error objetivo" + error.target);} + + var opciones = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "prueba"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + Ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, opciones); + + +### Ejemplo con cabeceras de subir y eventos de progreso (Android y iOS solamente) + + function win(r) {console.log ("código =" + r.responseCode); + Console.log ("respuesta =" + r.response); + Console.log ("Sent =" + r.bytesSent);} + + function fail(error) {alert ("ha ocurrido un error: código =" + error.code); + Console.log ("error al cargar el origen" + error.source); + Console.log ("upload error objetivo" + error.target);} + + var uri = encodeURI ("http://some.server.com/upload.php"); + + var opciones = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + cabeceras de var ={'headerParam':'headerValue'}; + + options.headers = encabezados; + + var ft = new FileTransfer(); + Ft.OnProgress = function(progressEvent) {si (progressEvent.lengthComputable) {loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } {loadingStatus.increment() más; + } + }; + Ft.upload (fileURL, uri, win, fail, opciones); + + +## FileUploadResult + +A `FileUploadResult` objeto se pasa a la devolución del éxito de la `FileTransfer` del objeto `upload()` método. + +### Propiedades + +* **bytesSent**: el número de bytes enviados al servidor como parte de la carga. (largo) + +* **responseCode**: código de respuesta HTTP el devuelto por el servidor. (largo) + +* **respuesta**: respuesta el HTTP devuelto por el servidor. (DOMString) + +* **cabeceras**: cabeceras de respuesta HTTP el por el servidor. (Objeto) + + * Actualmente compatible con iOS solamente. + +### iOS rarezas + +* No es compatible con `responseCode` o`bytesSent`. + +## descargar + +**Parámetros**: + +* **fuente**: dirección URL del servidor para descargar el archivo, como codificada por`encodeURI()`. + +* **objetivo**: Filesystem url que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo) + +* **successCallback**: una devolución de llamada que se pasa un `FileEntry` objeto. *(Función)* + +* **errorCallback**: una devolución de llamada que se ejecuta si se produce un error al recuperar los `FileEntry` . Invocado con un `FileTransferError` objeto. *(Función)* + +* **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil porque Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)* + +* **Opciones**: parámetros opcionales, actualmente sólo soporta cabeceras (como autorización (autenticación básica), etc.). + +### Ejemplo + + // !! Asume fileURL variable contiene una dirección URL válida a un camino en el dispositivo, / / por ejemplo, File Transfer var cdvfile://localhost/persistent/path/to/downloads/ = new FileTransfer(); + var uri = encodeURI ("http://some.server.com/download.php"); + + fileTransfer.download (uri, fileURL, function(entry) {console.log ("descarga completa:" + entry.toURL()); + }, function(error) {console.log ("error al descargar el origen" + error.source); + Console.log ("descargar error objetivo" + error.target); + Console.log ("código de error de carga" + error.code); + }, falso, {encabezados: {"Autorización": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA =="}}); + + +## abortar + +Aborta a una transferencia en curso. El callback onerror se pasa un objeto FileTransferError que tiene un código de error de FileTransferError.ABORT_ERR. + +### Ejemplo + + // !! Asume fileURL variable contiene una dirección URL válida a un archivo de texto en el dispositivo, / / por ejemplo, ganar cdvfile://localhost/persistent/path/to/file.txt var function(r) = {console.log ("no se debe llamar.");} + + var fallar = function(error) {/ / error.code == FileTransferError.ABORT_ERR alert ("ha ocurrido un error: código =" + error.code); + Console.log ("error al cargar el origen" + error.source); + Console.log ("upload error objetivo" + error.target);} + + var opciones = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + Ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, opciones); + Ft.Abort(); + + +## FileTransferError + +A `FileTransferError` objeto se pasa a un callback de error cuando se produce un error. + +### Propiedades + +* **código**: uno de los códigos de error predefinido enumerados a continuación. (Número) + +* **fuente**: URL a la fuente. (String) + +* **objetivo**: URL a la meta. (String) + +* **HTTP_STATUS**: código de estado HTTP. Este atributo sólo está disponible cuando se recibe un código de respuesta de la conexión HTTP. (Número) + +* **cuerpo** Cuerpo de la respuesta. Este atributo sólo está disponible cuando se recibe una respuesta de la conexión HTTP. (String) + +* **excepción**: cualquier e.getMessage o e.toString (String) + +### Constantes + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Al revés notas de compatibilidad + +Versiones anteriores de este plugin sólo aceptaría dispositivo-absoluto-archivo-rutas como la fuente de carga, o como destino para las descargas. Estos caminos normalmente sería de la forma + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Para atrás compatibilidad, estos caminos son aceptados todavía, y si su solicitud ha grabado caminos como éstos en almacenamiento persistente, entonces pueden seguir utilizarse. + +Estos caminos fueron expuestos anteriormente en el `fullPath` propiedad de `FileEntry` y `DirectoryEntry` objetos devueltos por el plugin de archivo. Las nuevas versiones del archivo plugin, sin embargo, ya no exponen estos caminos a JavaScript. + +Si va a actualizar a una nueva (1.0.0 o más reciente) versión del archivo y previamente han estado utilizando `entry.fullPath` como argumentos para `download()` o `upload()` , entonces tendrá que cambiar su código para usar URLs de sistema de archivos en su lugar. + +`FileEntry.toURL()`y `DirectoryEntry.toURL()` devolver un filesystem dirección URL de la forma + + cdvfile://localhost/persistent/path/to/file + + +que puede ser utilizado en lugar de la ruta del archivo absoluta tanto en `download()` y `upload()` los métodos. diff --git a/plugins/cordova-plugin-file-transfer/doc/fr/README.md b/plugins/cordova-plugin-file-transfer/doc/fr/README.md new file mode 100644 index 0000000..ca3e18a --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/fr/README.md @@ -0,0 +1,270 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +Documentation du plugin : + +Ce plugin vous permet de télécharger des fichiers. + +Ce plugin définit global `FileTransfer` , `FileUploadOptions` constructeurs. + +Bien que dans la portée globale, ils ne sont pas disponibles jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(FileTransfer);} + + +## Installation + + cordova plugin add cordova-plugin-file-transfer + + +## Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Navigateur + * Firefox OS ** + * iOS + * Windows Phone 7 et 8 * + * Windows 8 + * Windows + +\ * *Ne supportent pas `onprogress` ni `abort()` * + +\ ** *Ne prennent pas en charge les `onprogress` * + +# Transfert de fichiers + +L'objet de `FileTransfer` fournit un moyen de télécharger des fichiers à l'aide d'une requête HTTP multi-part POST ou PUT et pour télécharger des fichiers. + +## Propriétés + + * **onprogress** : fonction appelée avec un `ProgressEvent` à chaque fois qu'un nouveau segment de données est transféré. *(Function)* + +## Méthodes + + * **upload** : envoie un fichier à un serveur. + + * **download** : télécharge un fichier depuis un serveur. + + * **abort** : annule le transfert en cours. + +## upload + +**Paramètres**: + + * **fileURL** : système de fichiers URL représentant le fichier sur le périphérique. Pour la compatibilité ascendante, cela peut aussi être le chemin complet du fichier sur le périphérique. (Voir [Backwards Compatibility Notes] ci-dessous) + + * **server** : l'URL du serveur destiné à recevoir le fichier, encodée via `encodeURI()`. + + * **successCallback**: un rappel passé un `FileUploadResult` objet. *(Fonction)* + + * **errorCallback**: un rappel qui s'exécute si une erreur survient récupérer la `FileUploadResult` . Appelée avec un `FileTransferError` objet. *(Fonction)* + + * **options**: paramètres facultatifs *(objet)*. Clés valides : + + * **fileKey**: le nom de l'élément form. Valeur par défaut est `file` . (DOMString) + * **fileName**: le nom de fichier à utiliser lorsque vous enregistrez le fichier sur le serveur. Valeur par défaut est `image.jpg` . (DOMString) + * **httpMethod**: méthode de The HTTP à utiliser - soit `PUT` ou `POST` . Valeur par défaut est `POST` . (DOMString) + * **type MIME**: le type mime des données à télécharger. Valeur par défaut est `image/jpeg` . (DOMString) + * **params**: un ensemble de paires clé/valeur facultative pour passer dans la requête HTTP. (Objet) + * **chunkedMode**: s'il faut télécharger les données en mode streaming mémorisé en bloc. Valeur par défaut est `true` . (Boolean) + * **headers**: une carte des valeurs d'en-tête en-tête/nom. Un tableau permet de spécifier plusieurs valeurs. Sur iOS, FireOS et Android, si un en-tête nommé Content-Type n'est présent, les données de formulaire multipart servira pas. (Object) + * **httpMethod**: The HTTP méthode à utiliser par exemple poster ou mis. Par défaut, `message`. (DOMString) + + * **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur est `true` , il accepte tous les certificats de sécurité. Ceci est utile car Android rejette des certificats auto-signés. N'est pas recommandé pour une utilisation en production. Supporté sur Android et iOS. *(booléen)* + +### Exemple + + // !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function (r) {console.log ("Code =" + r.responseCode) ; + Console.log ("réponse =" + r.response) ; + Console.log ("envoyés =" + r.bytesSent);} + + échouer var = function (erreur) {alert ("une erreur est survenue : Code =" + error.code) ; + Console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log ("erreur de téléchargement cible" + error.target);} + + options de var = new FileUploadOptions() ; + options.fileKey = « fichier » ; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1) ; + options.mimeType = « text/plain » ; + + var params = {} ; + params.value1 = « test » ; + params.Value2 = « param » ; + + options.params = params ; + + ft var = new FileTransfer() ; + ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ; + + +### Exemple avec téléchargement du Header et des Progress Events (Android et iOS uniquement) + + function win(r) {console.log ("Code =" + r.responseCode) ; + Console.log ("réponse =" + r.response) ; + Console.log ("envoyés =" + r.bytesSent);} + + function fail(error) {alert ("une erreur est survenue : Code =" + error.code) ; + Console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log ("erreur de téléchargement cible" + error.target);} + + var uri = encodeURI ("http://some.server.com/upload.php") ; + + options de var = new FileUploadOptions() ; + options.fileKey="file" ; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1) ; + options.mimeType="text/plain" ; + + en-têtes var ={'headerParam':'headerValue'} ; + + options.Headers = en-têtes ; + + ft var = new FileTransfer() ; + ft.OnProgress = function(progressEvent) {si (progressEvent.lengthComputable) {loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total) ; + } else {loadingStatus.increment() ; + } + }; + ft.upload (fileURL, uri, win, fail, options) ; + + +## FileUploadResult + +A `FileUploadResult` objet est passé au rappel de succès la `FileTransfer` de l'objet `upload()` méthode. + +### Propriétés + + * **bytesSent** : le nombre d'octets envoyés au serveur dans le cadre du téléchargement. (long) + + * **responseCode** : le code de réponse HTTP retourné par le serveur. (long) + + * **response** : la réponse HTTP renvoyée par le serveur. (DOMString) + + * **en-têtes** : en-têtes de réponse HTTP par le serveur. (Objet) + + * Actuellement pris en charge sur iOS seulement. + +### Notes au sujet d'iOS + + * Ne prend pas en charge les propriétés `responseCode` et `bytesSent`. + +## download + +**Paramètres**: + + * **source** : l'URL du serveur depuis lequel télécharger le fichier, encodée via `encodeURI()`. + + * **target** : système de fichiers url représentant le fichier sur le périphérique. Pour la compatibilité ascendante, cela peut aussi être le chemin complet du fichier sur le périphérique. (Voir [Backwards Compatibility Notes] ci-dessous) + + * **successCallback** : une callback de succès à laquelle est passée un objet `FileEntry`. *(Function)* + + * **errorCallback**: un rappel qui s'exécute si une erreur se produit lors de la récupération du `FileEntry` . Appelée avec un `FileTransferError` objet. *(Fonction)* + + * **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur est `true` , il accepte tous les certificats de sécurité. Ceci peut être utile car Android rejette les certificats auto-signés. N'est pas recommandé pour une utilisation en production. Supporté sur Android et iOS. *(booléen)* + + * **options** : paramètres facultatifs, seules les en-têtes sont actuellement supportées (par exemple l'autorisation (authentification basique), etc.). + +### Exemple + + // !! Suppose fileURL variable contient une URL valide vers un chemin d'accès sur le périphérique, / / par exemple, transfert de fichiers var cdvfile://localhost/persistent/path/to/downloads/ = new FileTransfer() ; + var uri = encodeURI ("http://some.server.com/download.php") ; + + fileTransfer.download (uri, fileURL, function(entry) {console.log ("téléchargement complet:" + entry.toURL()) ; + }, function(error) {console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log (« erreur de téléchargement cible » + error.target) ; + Console.log (« code d'erreur de téléchargement » + error.code) ; + }, faux, {en-têtes: {« Autorisation »: « dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA base == "}}) ; + + +### Quirks wp8 + + * Télécharger demande est mis en cache par l'implémentation native. Pour éviter la mise en cache, pass `if-Modified-Since` en-tête Télécharger méthode. + +## abort + +Abandonne un transfert en cours. Le rappel onerror est passé à un objet FileTransferError qui a un code d'erreur de FileTransferError.ABORT_ERR. + +### Exemple + + // !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function(r) {console.log ("ne devrait pas être appelée.");} + + var fail = function(error) {/ / error.code == FileTransferError.ABORT_ERR alert ("une erreur est survenue : Code =" + error.code) ; + Console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log ("erreur de téléchargement cible" + error.target);} + + options de var = new FileUploadOptions() ; + options.fileKey="file" ; + options.fileName="myphoto.jpg" ; + options.mimeType="image/jpeg" ; + + ft var = new FileTransfer() ; + ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ; + ft.Abort() ; + + +## FileTransferError + +A `FileTransferError` objet est passé à un rappel d'erreur lorsqu'une erreur survient. + +### Propriétés + + * **code** : l'un des codes d'erreur prédéfinis énumérés ci-dessous. (Number) + + * **source** : l'URI de la source. (String) + + * **target**: l'URI de la destination. (String) + + * **http_status** : code d'état HTTP. Cet attribut n'est disponible que lorsqu'un code de réponse est fourni via la connexion HTTP. (Number) + + * **corps** Corps de réponse. Cet attribut n'est disponible que lorsqu'une réponse est reçue de la connexion HTTP. (String) + + * **exception**: soit e.getMessage ou e.toString (String) + +### Constantes + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Backwards Compatibility Notes + +Les versions précédentes de ce plugin n'accepterait périphérique--fichier-chemins d'accès absolus comme source pour les téléchargements, ou comme cible pour les téléchargements. Ces chemins seraient généralement de la forme + + / var/mobile/Applications/< application UUID >/Documents/chemin/vers/fichier (iOS), /storage/emulated/0/path/to/file (Android) + + +Pour vers l'arrière la compatibilité, ces chemins sont toujours acceptés, et si votre application a enregistré des chemins comme celles-ci dans un stockage persistant, alors ils peuvent continuer à être utilisé. + +Ces chemins ont été précédemment exposés dans le `fullPath` propriété de `FileEntry` et `DirectoryEntry` les objets retournés par le fichier plugin. Nouvelles versions du fichier plugin, cependant, ne plus exposent ces chemins à JavaScript. + +Si vous migrez vers une nouvelle (1.0.0 ou plus récent) version de fichier et vous avez précédemment utilisé `entry.fullPath` comme arguments à `download()` ou `upload()` , alors vous aurez besoin de modifier votre code pour utiliser le système de fichiers URL au lieu de cela. + +`FileEntry.toURL()`et `DirectoryEntry.toURL()` retournent une URL de système de fichiers du formulaire + + cdvfile://localhost/persistent/path/to/file + + +qui peut être utilisé à la place le chemin d'accès absolu au fichier dans les deux `download()` et `upload()` méthodes. \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/fr/index.md b/plugins/cordova-plugin-file-transfer/doc/fr/index.md new file mode 100644 index 0000000..6b2bce0 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/fr/index.md @@ -0,0 +1,261 @@ + + +# cordova-plugin-file-transfer + +Ce plugin vous permet de télécharger des fichiers. + +Ce plugin définit global `FileTransfer` , `FileUploadOptions` constructeurs. + +Bien que dans la portée globale, ils ne sont pas disponibles jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(FileTransfer);} + + +## Installation + + Cordova plugin ajouter cordova-plugin-file-transfert + + +## Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Navigateur +* Firefox OS ** +* iOS +* Windows Phone 7 et 8 * +* Windows 8 +* Windows + +* *Ne supportent pas `onprogress` ni `abort()` * + +** *Ne prennent pas en charge `onprogress` * + +# Transfert de fichiers + +Le `FileTransfer` objet fournit un moyen de télécharger des fichiers à l'aide d'une requête HTTP de la poste plusieurs partie et pour télécharger des fichiers aussi bien. + +## Propriétés + +* **onprogress** : fonction appelée avec un `ProgressEvent` à chaque fois qu'un nouveau segment de données est transféré. *(Function)* + +## Méthodes + +* **upload** : envoie un fichier à un serveur. + +* **download** : télécharge un fichier depuis un serveur. + +* **abort** : annule le transfert en cours. + +## upload + +**Paramètres**: + +* **fileURL** : système de fichiers URL représentant le fichier sur le périphérique. Pour la compatibilité ascendante, cela peut aussi être le chemin complet du fichier sur le périphérique. (Voir [Backwards Compatibility Notes] ci-dessous) + +* **server** : l'URL du serveur destiné à recevoir le fichier, encodée via `encodeURI()`. + +* **successCallback**: un rappel passé un `FileUploadResult` objet. *(Fonction)* + +* **errorCallback**: un rappel qui s'exécute si une erreur survient récupérer la `FileUploadResult` . Appelée avec un `FileTransferError` objet. *(Fonction)* + +* **options**: paramètres facultatifs *(objet)*. Clés valides : + + * **fileKey**: le nom de l'élément form. Valeur par défaut est `file` . (DOMString) + * **fileName**: le nom de fichier à utiliser lorsque vous enregistrez le fichier sur le serveur. Valeur par défaut est `image.jpg` . (DOMString) + * **httpMethod**: méthode de The HTTP à utiliser - soit `PUT` ou `POST` . Valeur par défaut est `POST` . (DOMString) + * **type MIME**: le type mime des données à télécharger. Valeur par défaut est `image/jpeg` . (DOMString) + * **params**: un ensemble de paires clé/valeur facultative pour passer dans la requête HTTP. (Objet) + * **chunkedMode**: s'il faut télécharger les données en mode streaming mémorisé en bloc. Valeur par défaut est `true` . (Boolean) + * **en-têtes**: une carte des valeurs d'en-tête en-tête/nom. Un tableau permet de spécifier plusieurs valeurs. (Objet) + +* **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur `true` , il accepte tous les certificats de sécurité. Ceci est utile car Android rejette des certificats auto-signés. Non recommandé pour une utilisation de production. Supporté sur Android et iOS. *(boolean)* + +### Exemple + + // !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function (r) {console.log ("Code =" + r.responseCode) ; + Console.log ("réponse =" + r.response) ; + Console.log ("envoyés =" + r.bytesSent);} + + échouer var = function (erreur) {alert ("une erreur est survenue : Code =" + error.code) ; + Console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log ("erreur de téléchargement cible" + error.target);} + + options de var = new FileUploadOptions() ; + options.fileKey = « fichier » ; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1) ; + options.mimeType = « text/plain » ; + + var params = {} ; + params.value1 = « test » ; + params.Value2 = « param » ; + + options.params = params ; + + ft var = new FileTransfer() ; + ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ; + + +### Exemple avec téléchargement du Header et des Progress Events (Android et iOS uniquement) + + function win(r) {console.log ("Code =" + r.responseCode) ; + Console.log ("réponse =" + r.response) ; + Console.log ("envoyés =" + r.bytesSent);} + + function fail(error) {alert ("une erreur est survenue : Code =" + error.code) ; + Console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log ("erreur de téléchargement cible" + error.target);} + + var uri = encodeURI ("http://some.server.com/upload.php") ; + + options de var = new FileUploadOptions() ; + options.fileKey="file" ; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1) ; + options.mimeType="text/plain" ; + + en-têtes var ={'headerParam':'headerValue'} ; + + options.Headers = en-têtes ; + + ft var = new FileTransfer() ; + ft.OnProgress = function(progressEvent) {si (progressEvent.lengthComputable) {loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total) ; + } else {loadingStatus.increment() ; + } + }; + ft.upload (fileURL, uri, win, fail, options) ; + + +## FileUploadResult + +A `FileUploadResult` objet est passé au rappel de succès la `FileTransfer` de l'objet `upload()` méthode. + +### Propriétés + +* **bytesSent** : le nombre d'octets envoyés au serveur dans le cadre du téléchargement. (long) + +* **responseCode** : le code de réponse HTTP retourné par le serveur. (long) + +* **response** : la réponse HTTP renvoyée par le serveur. (DOMString) + +* **en-têtes** : en-têtes de réponse HTTP par le serveur. (Objet) + + * Actuellement pris en charge sur iOS seulement. + +### iOS Remarques + +* Ne prend pas en charge les propriétés `responseCode` et `bytesSent`. + +## download + +**Paramètres**: + +* **source** : l'URL du serveur depuis lequel télécharger le fichier, encodée via `encodeURI()`. + +* **target** : système de fichiers url représentant le fichier sur le périphérique. Pour vers l'arrière la compatibilité, cela peut aussi être le chemin d'accès complet du fichier sur le périphérique. (Voir [vers l'arrière compatibilité note] ci-dessous) + +* **successCallback** : une callback de succès à laquelle est passée un objet `FileEntry`. *(Function)* + +* **errorCallback**: un rappel qui s'exécute si une erreur se produit lors de la récupération du `FileEntry` . Appelée avec un `FileTransferError` objet. *(Fonction)* + +* **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur est `true` , il accepte tous les certificats de sécurité. Ceci peut être utile car Android rejette les certificats auto-signés. N'est pas recommandé pour une utilisation en production. Supporté sur Android et iOS. *(booléen)* + +* **options** : paramètres facultatifs, seules les en-têtes sont actuellement supportées (par exemple l'autorisation (authentification basique), etc.). + +### Exemple + + // !! Suppose fileURL variable contient une URL valide vers un chemin d'accès sur le périphérique, / / par exemple, transfert de fichiers var cdvfile://localhost/persistent/path/to/downloads/ = new FileTransfer() ; + var uri = encodeURI ("http://some.server.com/download.php") ; + + fileTransfer.download (uri, fileURL, function(entry) {console.log ("téléchargement complet:" + entry.toURL()) ; + }, function(error) {console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log (« erreur de téléchargement cible » + error.target) ; + Console.log (« code d'erreur de téléchargement » + error.code) ; + }, faux, {en-têtes: {« Autorisation »: « dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA base == "}}) ; + + +## abort + +Abandonne un transfert en cours. Le rappel onerror est passé à un objet FileTransferError qui a un code d'erreur de FileTransferError.ABORT_ERR. + +### Exemple + + // !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function(r) {console.log ("ne devrait pas être appelée.");} + + var fail = function(error) {/ / error.code == FileTransferError.ABORT_ERR alert ("une erreur est survenue : Code =" + error.code) ; + Console.log (« source de l'erreur de téléchargement » + error.source) ; + Console.log ("erreur de téléchargement cible" + error.target);} + + options de var = new FileUploadOptions() ; + options.fileKey="file" ; + options.fileName="myphoto.jpg" ; + options.mimeType="image/jpeg" ; + + ft var = new FileTransfer() ; + ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ; + ft.Abort() ; + + +## FileTransferError + +A `FileTransferError` objet est passé à un rappel d'erreur lorsqu'une erreur survient. + +### Propriétés + +* **code** : l'un des codes d'erreur prédéfinis énumérés ci-dessous. (Number) + +* **source** : l'URI de la source. (String) + +* **target**: l'URI de la destination. (String) + +* **http_status** : code d'état HTTP. Cet attribut n'est disponible que lorsqu'un code de réponse est fourni via la connexion HTTP. (Number) + +* **corps** Corps de réponse. Cet attribut n'est disponible que lorsqu'une réponse est reçue de la connexion HTTP. (String) + +* **exception**: soit e.getMessage ou e.toString (String) + +### Constantes + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Backwards Compatibility Notes + +Les versions précédentes de ce plugin n'accepterait périphérique--fichier-chemins d'accès absolus comme source pour les téléchargements, ou comme cible pour les téléchargements. Ces chemins seraient généralement de la forme + + / var/mobile/Applications/< application UUID >/Documents/chemin/vers/fichier (iOS), /storage/emulated/0/path/to/file (Android) + + +Pour vers l'arrière la compatibilité, ces chemins sont toujours acceptés, et si votre application a enregistré des chemins comme celles-ci dans un stockage persistant, alors ils peuvent continuer à être utilisé. + +Ces chemins ont été précédemment exposés dans le `fullPath` propriété de `FileEntry` et `DirectoryEntry` les objets retournés par le fichier plugin. Nouvelles versions du fichier plugin, cependant, ne plus exposent ces chemins à JavaScript. + +Si vous migrez vers une nouvelle (1.0.0 ou plus récent) version de fichier et vous avez précédemment utilisé `entry.fullPath` comme arguments à `download()` ou `upload()` , alors vous aurez besoin de modifier votre code pour utiliser le système de fichiers URL au lieu de cela. + +`FileEntry.toURL()`et `DirectoryEntry.toURL()` retournent une URL de système de fichiers du formulaire + + cdvfile://localhost/persistent/path/to/file + + +qui peut être utilisé à la place le chemin d'accès absolu au fichier dans les deux `download()` et `upload()` méthodes. diff --git a/plugins/cordova-plugin-file-transfer/doc/it/README.md b/plugins/cordova-plugin-file-transfer/doc/it/README.md new file mode 100644 index 0000000..74d4ef5 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/it/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +Documentazione plugin: + +Questo plugin permette di caricare e scaricare file. + +Questo plugin definisce globale `FileTransfer`, costruttori di `FileUploadOptions`. + +Anche se in ambito globale, non sono disponibili fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Installazione + + cordova plugin add cordova-plugin-file-transfer + + +## Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Browser + * Firefox OS** + * iOS + * Windows Phone 7 e 8 * + * Windows 8 + * Windows + +\ * *Non supportano `onprogress` né `abort()` * + +\ * * *Non supportano `onprogress` * + +# FileTransfer + +L'oggetto `FileTransfer` fornisce un modo per caricare i file utilizzando una richiesta HTTP multiparte POST o PUT e scaricare file pure. + +## Proprietà + + * **OnProgress**: chiamata con un `ProgressEvent` ogni volta che un nuovo blocco di dati viene trasferito. *(Funzione)* + +## Metodi + + * **caricare**: invia un file a un server. + + * **Scarica**: Scarica un file dal server. + + * **Abort**: interrompe un trasferimento in corso. + +## upload + +**Parametri**: + + * **fileURL**: Filesystem URL che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto) + + * **server**: URL del server per ricevere il file, come codificato dal`encodeURI()`. + + * **successCallback**: un callback che viene passato un oggetto `FileUploadResult`. *(Funzione)* + + * **errorCallback**: un callback che viene eseguito se si verifica un errore di recupero `FileUploadResult`. Richiamato con un oggetto `FileTransferError`. *(Funzione)* + + * **opzioni**: parametri facoltativi *(oggetto)*. Chiavi valide: + + * **fileKey**: il nome dell'elemento form. Valore predefinito è `file` . (DOMString) + * **nome file**: il nome del file da utilizzare quando si salva il file sul server. Valore predefinito è `image.jpg` . (DOMString) + * **httpMethod**: metodo HTTP da utilizzare - `PUT` o `POST`. Impostazioni predefinite per `POST`. (DOMString) + * **mimeType**: il tipo mime dei dati da caricare. Impostazioni predefinite su `image/jpeg`. (DOMString) + * **params**: un insieme di coppie chiave/valore opzionale per passare nella richiesta HTTP. (Object) + * **chunkedMode**: se a caricare i dati in modalità streaming chunked. Impostazione predefinita è `true`. (Boolean) + * **headers**: una mappa di valori di intestazione e nome dell'intestazione. Utilizzare una matrice per specificare più di un valore. Su iOS, FireOS e Android, se è presente, un'intestazione Content-Type il nome dati form multipart non verranno utilizzati. (Object) + * **httpMethod**: metodo HTTP da utilizzare per esempio POST o PUT. Il valore predefinito è `POST`. (DOMString) + + * **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile poiché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)* + +### Esempio + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### Esempio con intestazioni di caricare ed eventi Progress (Android e iOS solo) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Un oggetto `FileUploadResult` viene passato al metodo di callback del metodo `upload()` dell'oggetto `FileTransfer` successo. + +### Proprietà + + * **bytesSent**: il numero di byte inviati al server come parte dell'upload. (lungo) + + * **responseCode**: codice di risposta HTTP restituito dal server. (lungo) + + * **risposta**: risposta HTTP restituito dal server. (DOMString) + + * **intestazioni**: intestazioni di risposta HTTP dal server. (Oggetto) + + * Attualmente supportato solo iOS. + +### iOS stranezze + + * Non supporta `responseCode` o`bytesSent`. + +## Scarica + +**Parametri**: + + * **fonte**: URL del server per scaricare il file, come codificato dal`encodeURI()`. + + * **destinazione**: Filesystem url che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto) + + * **successCallback**: un callback passato un `FileEntry` oggetto. *(Funzione)* + + * **errorCallback**: un callback che viene eseguito se si verifica un errore durante il recupero `FileEntry`. Richiamato con un oggetto `FileTransferError`. *(Function)* + + * **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile perché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)* + + * **opzioni**: parametri facoltativi, attualmente solo supporti intestazioni (ad esempio autorizzazione (autenticazione di base), ecc.). + +### Esempio + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### WP8 stranezze + + * Il download richiede è nella cache di implementazione nativa. Per evitare la memorizzazione nella cache, passare `if-Modified-Since` intestazione per metodo di download. + +## Abort + +Interrompe un trasferimento in corso. Il callback onerror viene passato un oggetto FileTransferError che presenta un codice di errore di FileTransferError.ABORT_ERR. + +### Esempio + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +Un oggetto `FileTransferError` viene passato a un callback di errore quando si verifica un errore. + +### Proprietà + + * **codice**: uno dei codici di errore predefiniti elencati di seguito. (Numero) + + * **fonte**: URL all'origine. (String) + + * **destinazione**: URL di destinazione. (String) + + * **http_status**: codice di stato HTTP. Questo attributo è disponibile solo quando viene ricevuto un codice di risposta della connessione HTTP. (Numero) + + * **body** Corpo della risposta. Questo attributo è disponibile solo quando viene ricevuta una risposta dalla connessione HTTP. (String) + + * **exception**: O e.getMessage o e.toString (String) + +### Costanti + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Note di compatibilità all'indietro + +Versioni precedenti di questo plugin accetterebbe solo dispositivo-assoluto-percorsi di file come origine per upload, o come destinazione per il download. Questi percorsi si sarebbero generalmente di forma + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Per indietro compatibilità, questi percorsi sono ancora accettati, e se l'applicazione ha registrato percorsi come questi in un archivio permanente, quindi possono continuare a essere utilizzato. + +Questi percorsi sono stati precedentemente esposti nella proprietà `fullPath` di `FileEntry` e oggetti `DirectoryEntry` restituiti dal File plugin. Nuove versioni del File plugin, tuttavia, non è più espongono questi percorsi a JavaScript. + +Se si esegue l'aggiornamento a una nuova (1.0.0 o più recente) versione del File e si hanno precedentemente utilizzato `entry.fullPath` come argomenti per `download()` o `upload()`, quindi sarà necessario cambiare il codice per utilizzare gli URL filesystem invece. + +`FileEntry.toURL()` e `DirectoryEntry.toURL()` restituiscono un filesystem URL del modulo + + cdvfile://localhost/persistent/path/to/file + + +che può essere utilizzato al posto del percorso assoluto nei metodi sia `download()` e `upload()`. \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/it/index.md b/plugins/cordova-plugin-file-transfer/doc/it/index.md new file mode 100644 index 0000000..e1b74e3 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/it/index.md @@ -0,0 +1,302 @@ + + +# cordova-plugin-file-transfer + +Questo plugin permette di caricare e scaricare file. + +Questo plugin definisce globale `FileTransfer`, costruttori di `FileUploadOptions`. + +Anche se in ambito globale, non sono disponibili fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Installazione + + cordova plugin add cordova-plugin-file-transfer + + +## Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Browser +* Firefox OS** +* iOS +* Windows Phone 7 e 8 * +* Windows 8 +* Windows + +* *Supporto `onprogress` né `abort()`* + +** *Non supportano `onprogress`* + +# FileTransfer + +L'oggetto `FileTransfer` fornisce un modo per caricare i file utilizzando una richiesta HTTP di POST più parte e scaricare file pure. + +## Proprietà + +* **OnProgress**: chiamata con un `ProgressEvent` ogni volta che un nuovo blocco di dati viene trasferito. *(Funzione)* + +## Metodi + +* **caricare**: invia un file a un server. + +* **Scarica**: Scarica un file dal server. + +* **Abort**: interrompe un trasferimento in corso. + +## caricare + +**Parametri**: + +* **fileURL**: Filesystem URL che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto) + +* **server**: URL del server per ricevere il file, come codificato dal`encodeURI()`. + +* **successCallback**: un callback che viene passato un oggetto `FileUploadResult`. *(Funzione)* + +* **errorCallback**: un callback che viene eseguito se si verifica un errore di recupero `FileUploadResult`. Richiamato con un oggetto `FileTransferError`. *(Funzione)* + +* **opzioni**: parametri facoltativi *(oggetto)*. Chiavi valide: + + * **fileKey**: il nome dell'elemento form. Valore predefinito è `file` . (DOMString) + * **nome file**: il nome del file da utilizzare quando si salva il file sul server. Valore predefinito è `image.jpg` . (DOMString) + * **httpMethod**: metodo HTTP da utilizzare - `PUT` o `POST`. Impostazioni predefinite per `POST`. (DOMString) + * **mimeType**: il tipo mime dei dati da caricare. Impostazioni predefinite su `image/jpeg`. (DOMString) + * **params**: un insieme di coppie chiave/valore opzionale per passare nella richiesta HTTP. (Object) + * **chunkedMode**: se a caricare i dati in modalità streaming chunked. Impostazione predefinita è `true`. (Boolean) + * **headers**: mappa di valori nome/intestazione intestazione. Utilizzare una matrice per specificare più valori. (Object) + +* **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile poiché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)* + +### Esempio + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### Esempio con intestazioni di caricare ed eventi Progress (Android e iOS solo) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Un oggetto `FileUploadResult` viene passato al metodo di callback del metodo `upload()` dell'oggetto `FileTransfer` successo. + +### Proprietà + +* **bytesSent**: il numero di byte inviati al server come parte dell'upload. (lungo) + +* **responseCode**: codice di risposta HTTP restituito dal server. (lungo) + +* **risposta**: risposta HTTP restituito dal server. (DOMString) + +* **intestazioni**: intestazioni di risposta HTTP dal server. (Oggetto) + + * Attualmente supportato solo iOS. + +### iOS stranezze + +* Non supporta `responseCode` o`bytesSent`. + +## Scarica + +**Parametri**: + +* **fonte**: URL del server per scaricare il file, come codificato dal`encodeURI()`. + +* **destinazione**: Filesystem url che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto) + +* **successCallback**: un callback passato un `FileEntry` oggetto. *(Funzione)* + +* **errorCallback**: un callback che viene eseguito se si verifica un errore durante il recupero `FileEntry`. Richiamato con un oggetto `FileTransferError`. *(Function)* + +* **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile perché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)* + +* **opzioni**: parametri facoltativi, attualmente solo supporti intestazioni (ad esempio autorizzazione (autenticazione di base), ecc.). + +### Esempio + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## Abort + +Interrompe un trasferimento in corso. Il callback onerror viene passato un oggetto FileTransferError che presenta un codice di errore di FileTransferError.ABORT_ERR. + +### Esempio + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +Un oggetto `FileTransferError` viene passato a un callback di errore quando si verifica un errore. + +### Proprietà + +* **codice**: uno dei codici di errore predefiniti elencati di seguito. (Numero) + +* **fonte**: URL all'origine. (String) + +* **destinazione**: URL di destinazione. (String) + +* **http_status**: codice di stato HTTP. Questo attributo è disponibile solo quando viene ricevuto un codice di risposta della connessione HTTP. (Numero) + +* **body** Corpo della risposta. Questo attributo è disponibile solo quando viene ricevuta una risposta dalla connessione HTTP. (String) + +* **exception**: O e.getMessage o e.toString (String) + +### Costanti + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Note di compatibilità all'indietro + +Versioni precedenti di questo plugin accetterebbe solo dispositivo-assoluto-percorsi di file come origine per upload, o come destinazione per il download. Questi percorsi si sarebbero generalmente di forma + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Per indietro compatibilità, questi percorsi sono ancora accettati, e se l'applicazione ha registrato percorsi come questi in un archivio permanente, quindi possono continuare a essere utilizzato. + +Questi percorsi sono stati precedentemente esposti nella proprietà `fullPath` di `FileEntry` e oggetti `DirectoryEntry` restituiti dal File plugin. Nuove versioni del File plugin, tuttavia, non è più espongono questi percorsi a JavaScript. + +Se si esegue l'aggiornamento a una nuova (1.0.0 o più recente) versione del File e si hanno precedentemente utilizzato `entry.fullPath` come argomenti per `download()` o `upload()`, quindi sarà necessario cambiare il codice per utilizzare gli URL filesystem invece. + +`FileEntry.toURL()` e `DirectoryEntry.toURL()` restituiscono un filesystem URL del modulo + + cdvfile://localhost/persistent/path/to/file + + +che può essere utilizzato al posto del percorso assoluto nei metodi sia `download()` e `upload()`. diff --git a/plugins/cordova-plugin-file-transfer/doc/ja/README.md b/plugins/cordova-plugin-file-transfer/doc/ja/README.md new file mode 100644 index 0000000..a8156a2 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/ja/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +プラグインã®ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«: + +ã“ã®ãƒ—ラグインã¯ã€ã‚¢ãƒƒãƒ—ロードã—ã€ãƒ•ァイルをダウンロードã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `FileTransfer`ã€`FileUploadOptions` コンス トラクターを定義ã—ã¾ã™ã€‚ + +グローãƒãƒ« スコープã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ã¾ã§ `deviceready` イベントã®å¾Œã§ã™ã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## インストール + + cordova plugin add cordova-plugin-file-transfer + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * ブラウザー + * Firefox ã® OS * * + * iOS + * Windows Phone 7 㨠8 * + * Windows 8 + * Windows + +\ * * `Onprogress`ã‚‚`abort()`をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。* + +\ * * * `Onprogress`をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。* + +# 出色 + +`出色`オブジェクトã¯ã€HTTP マルãƒãƒ‘ート POST ã¾ãŸã¯ PUT è¦æ±‚を使用ã—ã¦ãƒ•ァイルをアップロードã—ã€åŒæ§˜ã«ãƒ•ァイルをダウンロードã™ã‚‹æ–¹æ³•ã‚’æä¾›ã—ã¾ã™ã€‚ + +## プロパティ + + * **onprogress**: ã¨å‘¼ã°ã‚Œã‚‹ã€ `ProgressEvent` ãƒ‡ãƒ¼ã‚¿ã®æ–°ã—ã„ãƒãƒ£ãƒ³ã‚¯ãŒè»¢é€ã•れるãŸã³ã«ã€‚*(機能)* + +## メソッド + + * **アップロード**: サーãƒãƒ¼ã«ãƒ•ァイルをé€ä¿¡ã—ã¾ã™ã€‚ + + * **ダウンロード**: サーãƒãƒ¼ã‹ã‚‰ãƒ•ァイルをダウンロードã—ã¾ã™ã€‚ + + * **中止**: 進行中ã®è»¢é€ã‚’中止ã—ã¾ã™ã€‚ + +## upload + +**パラメーター**: + + * **fileURL**: デãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルを表ã™ãƒ•ァイルシステム URL。 下ä½äº’æ›æ€§ã¯ã€ã“ã®ã“ã¨ã‚‚ã€ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã®å®Œå…¨ãƒ‘スã§ã‚りã¾ã™ã€‚ (å‚ç…§ã—ã¦ãã ã•ã„ [å¾Œæ–¹äº’æ›æ€§ãƒ¡ãƒ¢] ã®ä¸‹) + + * **サーãƒãƒ¼**: ã«ã‚ˆã£ã¦ç¬¦å·åŒ–ã•れるよã†ã«ã€ãƒ•ァイルをå—ä¿¡ã™ã‚‹ã‚µãƒ¼ãƒãƒ¼ã® URL`encodeURI()`. + + * **successCallback**: `FileUploadResult` ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆãŒæ¸¡ã•れるコールãƒãƒƒã‚¯ã€‚*(機能)* + + * **errorCallback**: エラー `FileUploadResult` ã‚’å–å¾—ã™ã‚‹ãŒç™ºç”Ÿã—ãŸå ´åˆã«å®Ÿè¡Œã•れるコールãƒãƒƒã‚¯ã€‚`FileTransferError` オブジェクトを使ã£ã¦å‘¼ã³å‡ºã•れã¾ã™ã€‚*(機能)* + + * **オプション**: çœç•¥å¯èƒ½ãªãƒ‘ラメーター *(オブジェクト)*。有効ãªã‚­ãƒ¼: + + * **fileKey**: フォームè¦ç´ ã®åå‰ã€‚既定値㯠`file` ã§ã™ã€‚(,) + * **ファイルå**: ファイルåã€ã‚µãƒ¼ãƒãƒ¼ä¸Šã®ãƒ•ァイルをä¿å­˜ã™ã‚‹ã¨ãã«ä½¿ç”¨ã—ã¾ã™ã€‚既定値㯠`image.jpg` ã§ã™ã€‚(,) + * **httpMethod**: - `ã‚’ç½®ã` ã¾ãŸã¯ `POST` ã®ã„ãšã‚Œã‹ã‚’使用ã™ã‚‹ HTTP メソッド。デフォルト `ã®ãƒã‚¹ãƒˆ` ã§ã™ã€‚(,) + * **mimeType**: アップロードã™ã‚‹ãƒ‡ãƒ¼ã‚¿ã® mime タイプ。`イメージ/jpeg` ã®ãƒ‡ãƒ•ォルトã§ã™ã€‚(,) + * **params**: HTTP ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«æ¸¡ã™ãŸã‚ã«ä»»æ„ã®ã‚­ãƒ¼/値ペアã®ã‚»ãƒƒãƒˆã€‚(オブジェクト) + * **chunkedMode**: ãƒãƒ£ãƒ³ã‚¯ ストリーミング モードã§ãƒ‡ãƒ¼ã‚¿ã‚’アップロードã™ã‚‹ã‹ã©ã†ã‹ã€‚デフォルト㯠`true` ã§ã™ã€‚(ブール値) + * **headers**: ヘッダーå/ヘッダー値ã®ãƒžãƒƒãƒ—。 é…列を使用ã—ã¦ã€1 ã¤ä»¥ä¸Šã®å€¤ã‚’指定ã—ã¾ã™ã€‚ IOSã€FireOSã€ã‚¢ãƒ³ãƒ‰ãƒ­ã‚¤ãƒ‰ã§ã¯ã¨ã„ã†åå‰ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ タイプ ヘッダーãŒå­˜åœ¨ã™ã‚‹å ´åˆã€ãƒžãƒ«ãƒãƒ‘ート フォーム データã¯ä½¿ç”¨ã•れã¾ã›ã‚“。 (Object) + * **httpMethod**: 例ãˆã°ã‚’使用ã™ã‚‹ HTTP メソッドを POST ã¾ãŸã¯ PUT ã§ã™ã€‚ デフォルト`ã®ãƒã‚¹ãƒˆ`ã§ã™ã€‚(DOMString) + + * **trustAllHosts**: çœç•¥å¯èƒ½ãªãƒ‘ラメーターã¯ã€ãƒ‡ãƒ•ォルト `false` 。 å ´åˆè¨­å®š `true` ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã‚’ã™ã¹ã¦å—ã‘付ã‘ã¾ã™ã€‚ ã“れ㯠Android ã®è‡ªå·±ç½²å入りセキュリティ証明書を拒å¦ã™ã‚‹ã®ã§ä¾¿åˆ©ã§ã™ã€‚ é‹ç”¨ç’°å¢ƒã§ä½¿ç”¨ã—ãªã„ã§ãã ã•ã„。 Android 㨠iOS ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ *(ブール値)* + +### 例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### サンプルã®ã‚¢ãƒƒãƒ—ロード ヘッダーã¨é€²è¡Œçжæ³ã®ã‚¤ãƒ™ãƒ³ãƒˆ (Android 㨠iOS ã®ã¿ï¼‰ + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +`FileUploadResult` オブジェクト㯠`FileTransfer` オブジェクト `upload()` ãƒ¡ã‚½ãƒƒãƒ‰ã®æˆåŠŸæ™‚ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã«æ¸¡ã•れã¾ã™ã€‚ + +### プロパティ + + * **bytesSent**: アップロードã®ä¸€éƒ¨ã¨ã—ã¦ã‚µãƒ¼ãƒãƒ¼ã«é€ä¿¡ã•れãŸãƒã‚¤ãƒˆæ•°ã€‚(ロング) + + * **記述**: サーãƒãƒ¼ã«ã‚ˆã£ã¦è¿”ã•れる HTTP 応答コード。(ロング) + + * **応答**: サーãƒãƒ¼ã«ã‚ˆã£ã¦è¿”ã•れる HTTP 応答。(,) + + * **ヘッダー**: HTTP 応答ヘッダー サーãƒãƒ¼ã«ã‚ˆã£ã¦ã€‚(オブジェクト) + + * ç¾åœ¨ iOS ã®ã¿ã§ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚ + +### iOS ã®ç™– + + * サãƒãƒ¼ãƒˆã—ã¦ã„ãªã„ `responseCode` ã¾ãŸã¯`bytesSent`. + +## download + +**パラメーター**: + + * **ソース**: ã«ã‚ˆã£ã¦ç¬¦å·åŒ–ã•れるよã†ã«ã€ãƒ•ァイルをダウンロードã™ã‚‹ã‚µãƒ¼ãƒãƒ¼ã® URL`encodeURI()`. + + * **ターゲット**: デãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルを表ã™ãƒ•ァイルシステム url。 下ä½äº’æ›æ€§ã¯ã€ã“ã®ã“ã¨ã‚‚ã€ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã®å®Œå…¨ãƒ‘スã§ã‚りã¾ã™ã€‚ (å‚ç…§ã—ã¦ãã ã•ã„ [å¾Œæ–¹äº’æ›æ€§ãƒ¡ãƒ¢] ã®ä¸‹) + + * **successCallback**: 渡ã•れãŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã‚’ `FileEntry` オブジェクト。*(機能)* + + * **errorCallback**: `FileEntry` ã‚’å–å¾—ã™ã‚‹ã¨ãã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸå ´åˆã«å®Ÿè¡Œã•れるコールãƒãƒƒã‚¯ã€‚`FileTransferError` オブジェクトを使ã£ã¦å‘¼ã³å‡ºã•れã¾ã™ã€‚*(機能)* + + * **trustAllHosts**: çœç•¥å¯èƒ½ãªãƒ‘ラメーターã¯ã€ãƒ‡ãƒ•ォルト `false` 。 å ´åˆè¨­å®š `true` ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã‚’ã™ã¹ã¦å—ã‘付ã‘ã¾ã™ã€‚ Android ã¯ã€è‡ªå·±ç½²å入りセキュリティ証明書を拒å¦ã—ã¾ã™ã®ã§ä¾¿åˆ©ã§ã™ã€‚ é‹ç”¨ç’°å¢ƒã§ä½¿ç”¨ã—ãªã„ã§ãã ã•ã„。 Android 㨠iOS ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ *(ブール値)* + + * **オプション**: çœç•¥å¯èƒ½ãªãƒ‘ラメーターã¯ã€ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã™ã‚‹ãƒ˜ãƒƒãƒ€ãƒ¼ã®ã¿ (èªè¨¼ (基本èªè¨¼) ãªã©)。 + +### 例 + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### WP8 ç™– + + * ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰è¦æ±‚ã™ã‚‹ãƒã‚¤ãƒ†ã‚£ãƒ–実装ã«ã‚ˆã£ã¦ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã«æ ¼ç´ã•れã¦ã„ã¾ã™ã€‚キャッシュã•れãªã„よã†ã«ã€æ¸¡ã™`以æ¥å¤‰æ›´ã•れ㟠if`ヘッダー メソッドをダウンロードã—ã¾ã™ã€‚ + +## abort + +進行中ã®è»¢é€ã‚’中止ã—ã¾ã™ã€‚Onerror コールãƒãƒƒã‚¯ãŒ FileTransferError.ABORT_ERR ã®ã‚¨ãƒ©ãƒ¼ コードをæŒã£ã¦ã„ã‚‹ FileTransferError ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã«æ¸¡ã•れã¾ã™ã€‚ + +### 例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +`FileTransferError` オブジェクトã¯ã€ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã«ã‚¨ãƒ©ãƒ¼ コールãƒãƒƒã‚¯ã«æ¸¡ã•れã¾ã™ã€‚ + +### プロパティ + + * **コード**: 次ã®ã„ãšã‚Œã‹ã®å®šç¾©æ¸ˆã¿ã®ã‚¨ãƒ©ãƒ¼ コード。(æ•°) + + * **ソース**: ソース㮠URL。(文字列) + + * **ターゲット**: 先㮠URL。(文字列) + + * **http_status**: HTTP ステータス コード。ã“ã®å±žæ€§ã¯ã€HTTP 接続ã‹ã‚‰å¿œç­”コードをå—ä¿¡ã—ãŸã¨ãã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚(æ•°) + + * **body**応答本体。ã“ã®å±žæ€§ã¯ã€HTTP 接続ã‹ã‚‰å¿œç­”ã‚’å—ä¿¡ã—ãŸã¨ãã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚(文字列) + + * **exception**: ã©ã¡ã‚‰ã‹ e.getMessage ã¾ãŸã¯ e.toString (文字列) + +### 定数 + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## å¾Œæ–¹äº’æ›æ€§ã‚’ノートã—ã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインã®ä»¥å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¾ãŸã¯ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã®ã‚¿ãƒ¼ã‚²ãƒƒãƒˆã¨ã—ã¦ã€ã‚¢ãƒƒãƒ—ロードã®ã‚½ãƒ¼ã‚¹ã¨ã—ã¦ã®ã¿ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ•ァイル パスをå—ã‘入れるã§ã—ょã†ã€‚ã“れらã®ãƒ‘スã®å½¢å¼ã¯ã€é€šå¸¸ + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +下ä½äº’æ›æ€§ã€ã“れらã®ãƒ‘スを使用ã—ã¦ã‚‚ã€ã‚¢ãƒ—リケーションã¯ã€æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã§ã“ã®ã‚ˆã†ãªãƒ‘スを記録ã—ã¦ã„ã‚‹å ´åˆã€ã—彼らãŒå¼•ãç¶šã使用ã•れã¾ã™ã€‚ + +ã“れらã®ãƒ‘ス㮠`FileEntry` やファイル プラグインã«ã‚ˆã£ã¦è¿”ã•れる `DirectoryEntry` オブジェクト㮠`fullPath` プロパティã§å…¬é–‹ã•れã¦ã„ãªã‹ã£ãŸã€‚ æ–°ã—ã„プラグインã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ãƒ•ァイルã€ãŸã ã—ã€ã‚‚ã¯ã‚„ java スクリプトã®è¨­å®šã‚’ã“れらã®ãƒ‘スを公開ã—ã¾ã™ã€‚ + +æ–°ã—ã„ã«ã‚¢ãƒƒãƒ—グレードã™ã‚‹å ´åˆ (1.0.0 以é™) ファイルã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ä»¥å‰ã‚’使用ã—ã¦ã„ã‚‹ `entry.fullPath` `download()` ã¾ãŸã¯ `upload()` ã¸ã®å¼•æ•°ã¨ã—ã¦ã€ãƒ•ァイルシステム㮠Url を代ã‚りã«ä½¿ç”¨ã™ã‚‹ã‚³ãƒ¼ãƒ‰ã‚’変更ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +`FileEntry.toURL()` 㨠`DirectoryEntry.toURL()` ファイルシステム㮠URL ã‚’è¿”ã™ãƒ•ォーム + + cdvfile://localhost/persistent/path/to/file + + +`download()`ã€`upload()` メソッドã®çµ¶å¯¾ãƒ•ァイル パスã®ä»£ã‚りã«ä½¿ç”¨ã§ãã¾ã™ã€‚ \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/ja/index.md b/plugins/cordova-plugin-file-transfer/doc/ja/index.md new file mode 100644 index 0000000..f885b3c --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/ja/index.md @@ -0,0 +1,302 @@ + + +# cordova-plugin-file-transfer + +ã“ã®ãƒ—ラグインã¯ã€ã‚¢ãƒƒãƒ—ロードã—ã€ãƒ•ァイルをダウンロードã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `FileTransfer`ã€`FileUploadOptions` コンス トラクターを定義ã—ã¾ã™ã€‚ + +グローãƒãƒ« スコープã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ã¾ã§ `deviceready` イベントã®å¾Œã§ã™ã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## インストール + + cordova plugin add cordova-plugin-file-transfer + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* ブラウザー +* Firefox ã® OS * * +* iOS +* Windows Phone 7 㨠8 * +* Windows 8 +* Windows + +* *`onprogress` ã‚‚ `abort()` をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“* + +* * *`onprogress` をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“* + +# FileTransfer + +`FileTransfer` オブジェクトã¯ãƒžãƒ«ãƒãƒ‘ートã®ãƒã‚¹ãƒˆã€HTTP è¦æ±‚を使用ã—ã¦ãƒ•ァイルをアップロードã—ã¦åŒæ§˜ã«ãƒ•ァイルをダウンロードã™ã‚‹æ–¹æ³•ã‚’æä¾›ã—ã¾ã™ã€‚ + +## プロパティ + +* **onprogress**: ã¨å‘¼ã°ã‚Œã‚‹ã€ `ProgressEvent` ãƒ‡ãƒ¼ã‚¿ã®æ–°ã—ã„ãƒãƒ£ãƒ³ã‚¯ãŒè»¢é€ã•れるãŸã³ã«ã€‚*(機能)* + +## メソッド + +* **アップロード**: サーãƒãƒ¼ã«ãƒ•ァイルをé€ä¿¡ã—ã¾ã™ã€‚ + +* **ダウンロード**: サーãƒãƒ¼ã‹ã‚‰ãƒ•ァイルをダウンロードã—ã¾ã™ã€‚ + +* **中止**: 進行中ã®è»¢é€ã‚’中止ã—ã¾ã™ã€‚ + +## upload + +**パラメーター**: + +* **fileURL**: デãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルを表ã™ãƒ•ァイルシステム URL。 下ä½äº’æ›æ€§ã¯ã€ã“ã®ã“ã¨ã‚‚ã€ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã®å®Œå…¨ãƒ‘スã§ã‚りã¾ã™ã€‚ (å‚ç…§ã—ã¦ãã ã•ã„ [å¾Œæ–¹äº’æ›æ€§ãƒ¡ãƒ¢] ã®ä¸‹) + +* **サーãƒãƒ¼**: ã«ã‚ˆã£ã¦ç¬¦å·åŒ–ã•れるよã†ã«ã€ãƒ•ァイルをå—ä¿¡ã™ã‚‹ã‚µãƒ¼ãƒãƒ¼ã® URL`encodeURI()`. + +* **successCallback**: `FileUploadResult` ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆãŒæ¸¡ã•れるコールãƒãƒƒã‚¯ã€‚*(機能)* + +* **errorCallback**: エラー `FileUploadResult` ã‚’å–å¾—ã™ã‚‹ãŒç™ºç”Ÿã—ãŸå ´åˆã«å®Ÿè¡Œã•れるコールãƒãƒƒã‚¯ã€‚`FileTransferError` オブジェクトを使ã£ã¦å‘¼ã³å‡ºã•れã¾ã™ã€‚*(機能)* + +* **オプション**: çœç•¥å¯èƒ½ãªãƒ‘ラメーター *(オブジェクト)*。有効ãªã‚­ãƒ¼: + + * **fileKey**: フォームè¦ç´ ã®åå‰ã€‚既定値㯠`file` ã§ã™ã€‚(,) + * **ファイルå**: ファイルåã€ã‚µãƒ¼ãƒãƒ¼ä¸Šã®ãƒ•ァイルをä¿å­˜ã™ã‚‹ã¨ãã«ä½¿ç”¨ã—ã¾ã™ã€‚既定値㯠`image.jpg` ã§ã™ã€‚(,) + * **httpMethod**: - `ã‚’ç½®ã` ã¾ãŸã¯ `POST` ã®ã„ãšã‚Œã‹ã‚’使用ã™ã‚‹ HTTP メソッド。デフォルト `ã®ãƒã‚¹ãƒˆ` ã§ã™ã€‚(,) + * **mimeType**: アップロードã™ã‚‹ãƒ‡ãƒ¼ã‚¿ã® mime タイプ。`イメージ/jpeg` ã®ãƒ‡ãƒ•ォルトã§ã™ã€‚(,) + * **params**: HTTP ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«æ¸¡ã™ãŸã‚ã«ä»»æ„ã®ã‚­ãƒ¼/値ペアã®ã‚»ãƒƒãƒˆã€‚(オブジェクト) + * **chunkedMode**: ãƒãƒ£ãƒ³ã‚¯ ストリーミング モードã§ãƒ‡ãƒ¼ã‚¿ã‚’アップロードã™ã‚‹ã‹ã©ã†ã‹ã€‚デフォルト㯠`true` ã§ã™ã€‚(ブール値) + * **headers**: ヘッダーã®åå‰/ヘッダー値ã®ãƒžãƒƒãƒ—。1 ã¤ä»¥ä¸Šã®å€¤ã‚’指定ã™ã‚‹ã«ã¯ã€é…列を使用ã—ã¾ã™ã€‚(オブジェクト) + +* **trustAllHosts**: çœç•¥å¯èƒ½ãªãƒ‘ラメーターã¯ã€ãƒ‡ãƒ•ォルト `false` 。 å ´åˆè¨­å®š `true` ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã‚’ã™ã¹ã¦å—ã‘付ã‘ã¾ã™ã€‚ ã“れ㯠Android ã®è‡ªå·±ç½²å入りセキュリティ証明書を拒å¦ã™ã‚‹ã®ã§ä¾¿åˆ©ã§ã™ã€‚ é‹ç”¨ç’°å¢ƒã§ä½¿ç”¨ã—ãªã„ã§ãã ã•ã„。 Android 㨠iOS ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ *(ブール値)* + +### 例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### サンプルã®ã‚¢ãƒƒãƒ—ロード ヘッダーã¨é€²è¡Œçжæ³ã®ã‚¤ãƒ™ãƒ³ãƒˆ (Android 㨠iOS ã®ã¿ï¼‰ + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +`FileUploadResult` オブジェクト㯠`FileTransfer` オブジェクト `upload()` ãƒ¡ã‚½ãƒƒãƒ‰ã®æˆåŠŸæ™‚ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã«æ¸¡ã•れã¾ã™ã€‚ + +### プロパティ + +* **bytesSent**: アップロードã®ä¸€éƒ¨ã¨ã—ã¦ã‚µãƒ¼ãƒãƒ¼ã«é€ä¿¡ã•れãŸãƒã‚¤ãƒˆæ•°ã€‚(ロング) + +* **記述**: サーãƒãƒ¼ã«ã‚ˆã£ã¦è¿”ã•れる HTTP 応答コード。(ロング) + +* **応答**: サーãƒãƒ¼ã«ã‚ˆã£ã¦è¿”ã•れる HTTP 応答。(,) + +* **ヘッダー**: HTTP 応答ヘッダー サーãƒãƒ¼ã«ã‚ˆã£ã¦ã€‚(オブジェクト) + + * ç¾åœ¨ iOS ã®ã¿ã§ã‚µãƒãƒ¼ãƒˆã•れã¾ã™ã€‚ + +### iOS ã®ç™– + +* サãƒãƒ¼ãƒˆã—ã¦ã„ãªã„ `responseCode` ã¾ãŸã¯`bytesSent`. + +## download + +**パラメーター**: + +* **ソース**: ã«ã‚ˆã£ã¦ç¬¦å·åŒ–ã•れるよã†ã«ã€ãƒ•ァイルをダウンロードã™ã‚‹ã‚µãƒ¼ãƒãƒ¼ã® URL`encodeURI()`. + +* **ターゲット**: デãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルを表ã™ãƒ•ァイルシステム url。 下ä½äº’æ›æ€§ã¯ã€ã“ã®ã“ã¨ã‚‚ã€ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã®å®Œå…¨ãƒ‘スã§ã‚りã¾ã™ã€‚ (å‚ç…§ã—ã¦ãã ã•ã„ [å¾Œæ–¹äº’æ›æ€§ãƒ¡ãƒ¢] ã®ä¸‹) + +* **successCallback**: 渡ã•れãŸã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã‚’ `FileEntry` オブジェクト。*(機能)* + +* **errorCallback**: `FileEntry` ã‚’å–å¾—ã™ã‚‹ã¨ãã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸå ´åˆã«å®Ÿè¡Œã•れるコールãƒãƒƒã‚¯ã€‚`FileTransferError` オブジェクトを使ã£ã¦å‘¼ã³å‡ºã•れã¾ã™ã€‚*(機能)* + +* **trustAllHosts**: çœç•¥å¯èƒ½ãªãƒ‘ラメーターã¯ã€ãƒ‡ãƒ•ォルト `false` 。 å ´åˆè¨­å®š `true` ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã‚’ã™ã¹ã¦å—ã‘付ã‘ã¾ã™ã€‚ Android ã¯ã€è‡ªå·±ç½²å入りセキュリティ証明書を拒å¦ã—ã¾ã™ã®ã§ä¾¿åˆ©ã§ã™ã€‚ é‹ç”¨ç’°å¢ƒã§ä½¿ç”¨ã—ãªã„ã§ãã ã•ã„。 Android 㨠iOS ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ *(ブール値)* + +* **オプション**: çœç•¥å¯èƒ½ãªãƒ‘ラメーターã¯ã€ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã™ã‚‹ãƒ˜ãƒƒãƒ€ãƒ¼ã®ã¿ (èªè¨¼ (基本èªè¨¼) ãªã©)。 + +### 例 + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## abort + +進行中ã®è»¢é€ã‚’中止ã—ã¾ã™ã€‚Onerror コールãƒãƒƒã‚¯ãŒ FileTransferError.ABORT_ERR ã®ã‚¨ãƒ©ãƒ¼ コードをæŒã£ã¦ã„ã‚‹ FileTransferError ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã«æ¸¡ã•れã¾ã™ã€‚ + +### 例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +`FileTransferError` オブジェクトã¯ã€ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸã¨ãã«ã‚¨ãƒ©ãƒ¼ コールãƒãƒƒã‚¯ã«æ¸¡ã•れã¾ã™ã€‚ + +### プロパティ + +* **コード**: 次ã®ã„ãšã‚Œã‹ã®å®šç¾©æ¸ˆã¿ã®ã‚¨ãƒ©ãƒ¼ コード。(æ•°) + +* **ソース**: ソース㮠URL。(文字列) + +* **ターゲット**: 先㮠URL。(文字列) + +* **http_status**: HTTP ステータス コード。ã“ã®å±žæ€§ã¯ã€HTTP 接続ã‹ã‚‰å¿œç­”コードをå—ä¿¡ã—ãŸã¨ãã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚(æ•°) + +* **body**応答本体。ã“ã®å±žæ€§ã¯ã€HTTP 接続ã‹ã‚‰å¿œç­”ã‚’å—ä¿¡ã—ãŸã¨ãã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚(文字列) + +* **exception**: ã©ã¡ã‚‰ã‹ e.getMessage ã¾ãŸã¯ e.toString (文字列) + +### 定数 + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## å¾Œæ–¹äº’æ›æ€§ã‚’ノートã—ã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインã®ä»¥å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¾ãŸã¯ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã®ã‚¿ãƒ¼ã‚²ãƒƒãƒˆã¨ã—ã¦ã€ã‚¢ãƒƒãƒ—ロードã®ã‚½ãƒ¼ã‚¹ã¨ã—ã¦ã®ã¿ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ•ァイル パスをå—ã‘入れるã§ã—ょã†ã€‚ã“れらã®ãƒ‘スã®å½¢å¼ã¯ã€é€šå¸¸ + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +下ä½äº’æ›æ€§ã€ã“れらã®ãƒ‘スを使用ã—ã¦ã‚‚ã€ã‚¢ãƒ—リケーションã¯ã€æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã§ã“ã®ã‚ˆã†ãªãƒ‘スを記録ã—ã¦ã„ã‚‹å ´åˆã€ã—彼らãŒå¼•ãç¶šã使用ã•れã¾ã™ã€‚ + +ã“れらã®ãƒ‘ス㮠`FileEntry` やファイル プラグインã«ã‚ˆã£ã¦è¿”ã•れる `DirectoryEntry` オブジェクト㮠`fullPath` プロパティã§å…¬é–‹ã•れã¦ã„ãªã‹ã£ãŸã€‚ æ–°ã—ã„プラグインã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ãƒ•ァイルã€ãŸã ã—ã€ã‚‚ã¯ã‚„ java スクリプトã®è¨­å®šã‚’ã“れらã®ãƒ‘スを公開ã—ã¾ã™ã€‚ + +æ–°ã—ã„ã«ã‚¢ãƒƒãƒ—グレードã™ã‚‹å ´åˆ (1.0.0 以é™) ファイルã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ä»¥å‰ã‚’使用ã—ã¦ã„ã‚‹ `entry.fullPath` `download()` ã¾ãŸã¯ `upload()` ã¸ã®å¼•æ•°ã¨ã—ã¦ã€ãƒ•ァイルシステム㮠Url を代ã‚りã«ä½¿ç”¨ã™ã‚‹ã‚³ãƒ¼ãƒ‰ã‚’変更ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +`FileEntry.toURL()` 㨠`DirectoryEntry.toURL()` ファイルシステム㮠URL ã‚’è¿”ã™ãƒ•ォーム + + cdvfile://localhost/persistent/path/to/file + + +`download()`ã€`upload()` メソッドã®çµ¶å¯¾ãƒ•ァイル パスã®ä»£ã‚りã«ä½¿ç”¨ã§ãã¾ã™ã€‚ diff --git a/plugins/cordova-plugin-file-transfer/doc/ko/README.md b/plugins/cordova-plugin-file-transfer/doc/ko/README.md new file mode 100644 index 0000000..f91e8a2 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/ko/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +í”ŒëŸ¬ê·¸ì¸ ë¬¸ì„œ: + +ì´ í”ŒëŸ¬ê·¸ì¸ì„ 사용 하면 업로드 ë° ë‹¤ìš´ë¡œë“œ íŒŒì¼ ìˆ˜ 있습니다. + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `FileTransfer`, `FileUploadOptions` ìƒì„±ìžë¥¼ ì •ì˜í•©ë‹ˆë‹¤. + +ì „ì—­ 범위ì—서 ê·¸ë“¤ì€ ì œê³µ ë˜ì§€ 않습니다 때까지 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## 설치 + + cordova plugin add cordova-plugin-file-transfer + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * 브ë¼ìš°ì € + * 파ì´ì–´ í­ìФ OS * * + * iOS + * Windows Phone 7ê³¼ 8 * + * 윈ë„ìš° 8 + * 윈ë„ìš° + +\** `Onprogress` ë„ `abort()` 를 ì§€ì› í•˜ì§€ 않습니다* + +\*** `Onprogress` 를 ì§€ì› í•˜ì§€ 않습니다* + +# FileTransfer + +`FileTransfer` 개체는 HTTP 다중 파트 POST ë˜ëŠ” PUT ìš”ì²­ì„ ì‚¬ìš© 하 ì—¬ 파ì¼ì„ 업로드 하 ê³  ë˜í•œ 파ì¼ì„ 다운로드 하는 ë°©ë²•ì„ ì œê³µ 합니다. + +## ì†ì„± + + * **onprogress**:로 불리는 `ProgressEvent` 새로운 ì–‘ì˜ ë°ì´í„°ë¥¼ 전송 하는 때마다. *(기능)* + +## 메서드 + + * **업로드**: 파ì¼ì„ ì„œë²„ì— ë³´ëƒ…ë‹ˆë‹¤. + + * **다운로드**: 서버ì—서 파ì¼ì„ 다운로드 합니다. + + * **중단**: ì§„í–‰ ì¤‘ì¸ ì „ì†¡ 중단. + +## 업로드 + +**매개 변수**: + + * **fileURL**: ìž¥ì¹˜ì— íŒŒì¼ì„ 나타내는 íŒŒì¼ ì‹œìŠ¤í…œ URL. ì— ëŒ€ 한 ì´ì „ 버전과 호환성ì„ì´ ìˆ˜ë„ ìž¥ì¹˜ì— ìžˆëŠ” 파ì¼ì˜ ì „ì²´ 경로 수. (참조 [거꾸로 호환성 노트] 아래) + + * **서버**: ì¸ì½”딩 íŒŒì¼ ìˆ˜ì‹  ì„œë²„ì˜ URL`encodeURI()`. + + * **successCallback**: `FileUploadResult` 개체를 전달 하는 콜백. *(기능)* + + * **errorCallback**: `FileUploadResult` ê²€ìƒ‰ì— ì˜¤ë¥˜ê°€ ë°œìƒ í•˜ë©´ 실행 ë˜ëŠ” 콜백. `FileTransferError` 개체와 함께 호출 ë©ë‹ˆë‹¤. *(기능)* + + * **옵션**: ì„ íƒì  매개 변수 *(개체)*. 유효한 키: + + * **fileKey**: form ìš”ì†Œì˜ ì´ë¦„. ê¸°ë³¸ê°’ì€ `file` . (DOMString) + * **íŒŒì¼ ì´ë¦„**: íŒŒì¼ ì´ë¦„ì„ ì„œë²„ì— íŒŒì¼ì„ 저장할 때 사용 합니다. ê¸°ë³¸ê°’ì€ `image.jpg` . (DOMString) + * **httpMethod**: `넣어` ë˜ëŠ” `게시물`-사용 하ë„ë¡ HTTP 메서드. `게시물` 기본값입니다. (DOMString) + * **mimeType**: 업로드 ë°ì´í„°ì˜ mime 형ì‹ì„. `ì´ë¯¸ì§€/jpeg`ì˜ ê¸°ë³¸ê°’ìž…ë‹ˆë‹¤. (DOMString) + * **params**: HTTP ìš”ì²­ì— ì „ë‹¬í•  ì„ íƒì  키/ê°’ ìŒì˜ ì§‘í•©. (개체) + * **chunkedMode**: ì²­í¬ ìŠ¤íŠ¸ë¦¬ë° ëª¨ë“œì—서 ë°ì´í„° 업로드를 합니다. ê¸°ë³¸ê°’ì€ `true`입니다. (부울) + * **headers**: í—¤ë” ì´ë¦„ ë° í—¤ë” ê°’ì˜ ì§€ë„. ë°°ì—´ì„ ì‚¬ìš© 하 ì—¬ 하나 ì´ìƒì˜ ê°’ì„ ì§€ì •. IOS, FireOS, 안 드 로ì´ë“œì— 있으면 ë¼ëŠ” 콘í…츠 í˜•ì‹ í—¤ë”, 다중 ì–‘ì‹ ë°ì´í„°ëŠ” 사용할 수 없습니다. (Object) + * **httpMethod**: HTTP 메서드 예를 사용 하 ì—¬ 게시 하거나 넣어. `게시물`기본값입니다. (DOMString) + + * **trustAllHosts**: ì„ íƒì  매개 변수는 기본ì ìœ¼ë¡œ `false` . 만약 설정 `true` , ê·¸ê²ƒì€ ëª¨ë“  보안 ì¸ì¦ì„œë¥¼ 허용 합니다. ì´ ì•ˆ 드 로ì´ë“œ ìžì²´ 서명 ëœ ë³´ì•ˆ ì¸ì¦ì„œë¥¼ ê±°ë¶€ 하기 ë•Œë¬¸ì— ìœ ìš© 합니다. 프로ë•ì…˜ 환경ì—서 사용 권장 ë˜ì§€ 않습니다. 안 드 로ì´ë“œì™€ iOSì—서 ì§€ì›. *(부울)* + +### 예를 들어 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### 예를 들어 í—¤ë” ì—…ë¡œë“œ ë° ì§„í–‰ ì´ë²¤íЏ (안 드 로ì´ë“œì™€ iOSë§Œ) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +`FileUploadResult` 개체 `FileTransfer` ê°œì²´ì˜ `ì›ëž˜` ë°©ë²•ì˜ ì„±ê³µ ì½œë°±ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +### ì†ì„± + + * **bytesSent**: ì—…ë¡œë“œì˜ ì¼ë¶€ë¡œ ì„œë²„ì— ë³´ë‚¸ ë°”ì´íЏ 수. (긴) + + * **responseCode**: 서버ì—서 반환 ëœ HTTP ì‘답 코드. (긴) + + * **response**: 서버ì—서 반환 ë˜ëŠ” HTTP ì‘답. (DOMString) + + * **headers**: 서버ì—서 HTTP ì‘답 í—¤ë”. (개체) + + * 현재 iosë§Œ ì§€ì› í•©ë‹ˆë‹¤. + +### iOS ë‹¨ì  + + * ì§€ì› í•˜ì§€ 않는 `responseCode` ë˜ëŠ”`bytesSent`. + +## download + +**매개 변수**: + + * **source**: URL로 ì¸ì½”ë”©ëœ íŒŒì¼, 다운로드 서버`encodeURI()`. + + * **target**: ìž¥ì¹˜ì— íŒŒì¼ì„ 나타내는 íŒŒì¼ ì‹œìŠ¤í…œ url. ì— ëŒ€ 한 ì´ì „ 버전과 호환성ì„ì´ ìˆ˜ë„ ìž¥ì¹˜ì— ìžˆëŠ” 파ì¼ì˜ ì „ì²´ 경로 수. (참조 [거꾸로 호환성 노트] 아래) + + * **successCallback**: 콜백 전달 ë˜ëŠ” `FileEntry` 개체. *(기능)* + + * **errorCallback**: `FileEntry`를 검색할 때 오류가 ë°œìƒ í•˜ë©´ 실행 ë˜ëŠ” 콜백. `FileTransferError` 개체와 함께 호출 ë©ë‹ˆë‹¤. *(기능)* + + * **trustAllHosts**: ì„ íƒì  매개 변수는 기본ì ìœ¼ë¡œ `false` . 만약 설정 `true` , ê·¸ê²ƒì€ ëª¨ë“  보안 ì¸ì¦ì„œë¥¼ 허용 합니다. 안 드 로ì´ë“œ ìžì²´ 서명 ëœ ë³´ì•ˆ ì¸ì¦ì„œë¥¼ ê±°ë¶€ 하기 ë•Œë¬¸ì— ìœ ìš© 합니다. 프로ë•ì…˜ 환경ì—서 사용 권장 ë˜ì§€ 않습니다. 안 드 로ì´ë“œì™€ iOSì—서 ì§€ì›. *(부울)* + + * **options**: ì„ íƒì  매개 변수를 현재 ì§€ ì›í•˜ëŠ” 머리글만 (예: ì¸ì¦ (기본 ì¸ì¦), 등). + +### 예를 들어 + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### WP8 특수 + + * 다운로드 요청 기본 êµ¬í˜„ì— ì˜í•´ ìºì‹œ ë˜ ê³ . ìºì‹±ì„ ë°©ì§€ 하려면 전달 `if-수정-ì´í›„` í—¤ë”를 다운로드 하는 방법. + +## abort + +ì§„í–‰ ì¤‘ì¸ ì „ì†¡ì„ ì¤‘ë‹¨í•©ë‹ˆë‹¤. onerror 콜백 FileTransferError.ABORT_ERRì˜ ì˜¤ë¥˜ 코드는 FileTransferError 개체를 전달 합니다. + +### 예를 들어 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +`FileTransferError` 개체 오류가 ë°œìƒ í•˜ë©´ 오류 ì½œë°±ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +### ì†ì„± + + * **code**: 미리 ì •ì˜ ëœ ì˜¤ë¥˜ 코드 중 하나가 ì•„ëž˜ì— ë‚˜ì—´ ëœ. (수) + + * **source**: 소스 URL. (문ìžì—´) + + * **target**: ëŒ€ìƒ URL. (문ìžì—´) + + * **http_status**: HTTP ìƒíƒœ 코드. ì´ íŠ¹ì„±ì€ ì‘답 코드를 HTTP ì—°ê²°ì—서 ìˆ˜ì‹ ì— ì‚¬ìš©í•  수 있습니다. (수) + + * **body** ì‘답 본문입니다. ì´ íŠ¹ì„±ì€ HTTP ì—°ê²°ì—서 ì‘ë‹µì„ ë°›ì„ ë•Œì— ì‚¬ìš©í•  수 있습니다. (문ìžì—´) + + * **exception**: ì–´ëŠ e.getMessage ë˜ëŠ” e.toString (문ìžì—´) + +### ìƒìˆ˜ + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## ì´ì „ 버전과 호환성 노트 + +ì´ í”ŒëŸ¬ê·¸ì¸ì˜ ì´ì „ 버전만 ì—…ë¡œë“œì— ëŒ€ 한 소스 ë˜ëŠ” ë‹¤ìš´ë¡œë“œì— ëŒ€ 한 ëŒ€ìƒ ìž¥ì¹˜ 절대 íŒŒì¼ ê²½ë¡œ ë°›ì•„ë“¤ì¼ ê²ƒ ì´ë‹¤. ì´ëŸ¬í•œ 경로 ì¼ë°˜ì ìœ¼ë¡œ í¼ì˜ 것 + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +대 한 ë’¤ 호환성, ì´ëŸ¬í•œ 경로 여전히 허용, 그리고 ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì˜êµ¬ 저장소ì—서 ì´ì™€ ê°™ì€ ê²½ë¡œ ê¸°ë¡ í–ˆë‹¤, 그때 ê·¸ë“¤ì€ ê³„ì†í•  수 있다면 사용할 수. + +ì´ëŸ¬í•œ 경로 `FileEntry` ë° íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì— ì˜í•´ 반환 ëœ `DirectoryEntry` ê°œì²´ì˜ `fullPath` ì†ì„±ì— 노출 ë˜ì—ˆë˜. 그러나 íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì˜,, ë” ì´ìƒ 새로운 버전 ìžë°” 스í¬ë¦½íŠ¸ì´ ê²½ë¡œ 노출. + +새로 업그레ì´ë“œ 하는 경우 (1.0.0 ì´ìƒ) ë²„ì „ì˜ íŒŒì¼, ë° ì´ì „ 사용 하 ê³  `entry.fullPath` `download()` ë˜ëŠ” `upload()` ì¸ìˆ˜ë¡œ ë‹¤ìŒ ëŒ€ì‹  íŒŒì¼ ì‹œìŠ¤í…œ Urlì„ ì‚¬ìš© 하 ì—¬ 코드를 변경 해야 합니다. + +í¼ì˜ íŒŒì¼ URLì„ ë°˜í™˜ 하는 `FileEntry.toURL()` ë° `DirectoryEntry.toURL()` + + cdvfile://localhost/persistent/path/to/file + + +ì–´ë–¤ `download()` ë° `upload()` 방법ì—서 절대 íŒŒì¼ ê²½ë¡œ 대신 사용할 수 있습니다. \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/ko/index.md b/plugins/cordova-plugin-file-transfer/doc/ko/index.md new file mode 100644 index 0000000..a6e39c0 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/ko/index.md @@ -0,0 +1,302 @@ + + +# cordova-plugin-file-transfer + +ì´ í”ŒëŸ¬ê·¸ì¸ì„ 사용 하면 업로드 ë° ë‹¤ìš´ë¡œë“œ íŒŒì¼ ìˆ˜ 있습니다. + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `FileTransfer`, `FileUploadOptions` ìƒì„±ìžë¥¼ ì •ì˜í•©ë‹ˆë‹¤. + +ì „ì—­ 범위ì—서 ê·¸ë“¤ì€ ì œê³µ ë˜ì§€ 않습니다 때까지 `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## 설치 + + cordova plugin add cordova-plugin-file-transfer + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* 브ë¼ìš°ì € +* 파ì´ì–´ í­ìФ OS * * +* iOS +* Windows Phone 7ê³¼ 8 * +* 윈ë„ìš° 8 +* 윈ë„ìš° + +* *`onprogress`ë„ `abort()`를 ì§€ì› í•˜ì§€ 않습니다* + +* * *`onprogress`를 ì§€ì› í•˜ì§€ 않습니다* + +# FileTransfer + +`FileTransfer` 개체는 HTTP 다중 파트 POST ìš”ì²­ì„ ì‚¬ìš© 하 ì—¬ íŒŒì¼ ì—…ë¡œë“œ ë¿ë§Œ ì•„ë‹ˆë¼ íŒŒì¼ì„ 다운로드 하는 ë°©ë²•ì„ ì œê³µ 합니다. + +## ì†ì„± + +* **onprogress**:로 불리는 `ProgressEvent` 새로운 ì–‘ì˜ ë°ì´í„°ë¥¼ 전송 하는 때마다. *(기능)* + +## 메서드 + +* **업로드**: 파ì¼ì„ ì„œë²„ì— ë³´ëƒ…ë‹ˆë‹¤. + +* **다운로드**: 서버ì—서 파ì¼ì„ 다운로드 합니다. + +* **중단**: ì§„í–‰ ì¤‘ì¸ ì „ì†¡ 중단. + +## 업로드 + +**매개 변수**: + +* **fileURL**: ìž¥ì¹˜ì— íŒŒì¼ì„ 나타내는 íŒŒì¼ ì‹œìŠ¤í…œ URL. ì— ëŒ€ 한 ì´ì „ 버전과 호환성ì„ì´ ìˆ˜ë„ ìž¥ì¹˜ì— ìžˆëŠ” 파ì¼ì˜ ì „ì²´ 경로 수. (참조 [거꾸로 호환성 노트] 아래) + +* **서버**: ì¸ì½”딩 íŒŒì¼ ìˆ˜ì‹  ì„œë²„ì˜ URL`encodeURI()`. + +* **successCallback**: `FileUploadResult` 개체를 전달 하는 콜백. *(기능)* + +* **errorCallback**: `FileUploadResult` ê²€ìƒ‰ì— ì˜¤ë¥˜ê°€ ë°œìƒ í•˜ë©´ 실행 ë˜ëŠ” 콜백. `FileTransferError` 개체와 함께 호출 ë©ë‹ˆë‹¤. *(기능)* + +* **옵션**: ì„ íƒì  매개 변수 *(개체)*. 유효한 키: + + * **fileKey**: form ìš”ì†Œì˜ ì´ë¦„. ê¸°ë³¸ê°’ì€ `file` . (DOMString) + * **íŒŒì¼ ì´ë¦„**: íŒŒì¼ ì´ë¦„ì„ ì„œë²„ì— íŒŒì¼ì„ 저장할 때 사용 합니다. ê¸°ë³¸ê°’ì€ `image.jpg` . (DOMString) + * **httpMethod**: `넣어` ë˜ëŠ” `게시물`-사용 하ë„ë¡ HTTP 메서드. `게시물` 기본값입니다. (DOMString) + * **mimeType**: 업로드 ë°ì´í„°ì˜ mime 형ì‹ì„. `ì´ë¯¸ì§€/jpeg`ì˜ ê¸°ë³¸ê°’ìž…ë‹ˆë‹¤. (DOMString) + * **params**: HTTP ìš”ì²­ì— ì „ë‹¬í•  ì„ íƒì  키/ê°’ ìŒì˜ ì§‘í•©. (개체) + * **chunkedMode**: ì²­í¬ ìŠ¤íŠ¸ë¦¬ë° ëª¨ë“œì—서 ë°ì´í„° 업로드를 합니다. ê¸°ë³¸ê°’ì€ `true`입니다. (부울) + * **headers**: í—¤ë” ì´ë¦„/í—¤ë” ê°’ì˜ ì§€ë„. ë°°ì—´ì„ ì‚¬ìš© 하 ì—¬ 하나 ì´ìƒì˜ ê°’ì„ ì§€ì • 합니다. (개체) + +* **trustAllHosts**: ì„ íƒì  매개 변수는 기본ì ìœ¼ë¡œ `false` . 만약 설정 `true` , ê·¸ê²ƒì€ ëª¨ë“  보안 ì¸ì¦ì„œë¥¼ 허용 합니다. ì´ ì•ˆ 드 로ì´ë“œ ìžì²´ 서명 ëœ ë³´ì•ˆ ì¸ì¦ì„œë¥¼ ê±°ë¶€ 하기 ë•Œë¬¸ì— ìœ ìš© 합니다. 프로ë•ì…˜ 환경ì—서 사용 권장 ë˜ì§€ 않습니다. 안 드 로ì´ë“œì™€ iOSì—서 ì§€ì›. *(부울)* + +### 예를 들어 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### 예를 들어 í—¤ë” ì—…ë¡œë“œ ë° ì§„í–‰ ì´ë²¤íЏ (안 드 로ì´ë“œì™€ iOSë§Œ) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +`FileUploadResult` 개체 `FileTransfer` ê°œì²´ì˜ `ì›ëž˜` ë°©ë²•ì˜ ì„±ê³µ ì½œë°±ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +### ì†ì„± + +* **bytesSent**: ì—…ë¡œë“œì˜ ì¼ë¶€ë¡œ ì„œë²„ì— ë³´ë‚¸ ë°”ì´íЏ 수. (긴) + +* **responseCode**: 서버ì—서 반환 ëœ HTTP ì‘답 코드. (긴) + +* **response**: 서버ì—서 반환 ë˜ëŠ” HTTP ì‘답. (DOMString) + +* **headers**: 서버ì—서 HTTP ì‘답 í—¤ë”. (개체) + + * 현재 iosë§Œ ì§€ì› í•©ë‹ˆë‹¤. + +### iOS ë‹¨ì  + +* ì§€ì› í•˜ì§€ 않는 `responseCode` ë˜ëŠ”`bytesSent`. + +## download + +**매개 변수**: + +* **source**: URL로 ì¸ì½”ë”©ëœ íŒŒì¼, 다운로드 서버`encodeURI()`. + +* **target**: ìž¥ì¹˜ì— íŒŒì¼ì„ 나타내는 íŒŒì¼ ì‹œìŠ¤í…œ url. ì— ëŒ€ 한 ì´ì „ 버전과 호환성ì„ì´ ìˆ˜ë„ ìž¥ì¹˜ì— ìžˆëŠ” 파ì¼ì˜ ì „ì²´ 경로 수. (참조 [거꾸로 호환성 노트] 아래) + +* **successCallback**: 콜백 전달 ë˜ëŠ” `FileEntry` 개체. *(기능)* + +* **errorCallback**: `FileEntry`를 검색할 때 오류가 ë°œìƒ í•˜ë©´ 실행 ë˜ëŠ” 콜백. `FileTransferError` 개체와 함께 호출 ë©ë‹ˆë‹¤. *(기능)* + +* **trustAllHosts**: ì„ íƒì  매개 변수는 기본ì ìœ¼ë¡œ `false` . 만약 설정 `true` , ê·¸ê²ƒì€ ëª¨ë“  보안 ì¸ì¦ì„œë¥¼ 허용 합니다. 안 드 로ì´ë“œ ìžì²´ 서명 ëœ ë³´ì•ˆ ì¸ì¦ì„œë¥¼ ê±°ë¶€ 하기 ë•Œë¬¸ì— ìœ ìš© 합니다. 프로ë•ì…˜ 환경ì—서 사용 권장 ë˜ì§€ 않습니다. 안 드 로ì´ë“œì™€ iOSì—서 ì§€ì›. *(부울)* + +* **options**: ì„ íƒì  매개 변수를 현재 ì§€ ì›í•˜ëŠ” 머리글만 (예: ì¸ì¦ (기본 ì¸ì¦), 등). + +### 예를 들어 + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## abort + +ì§„í–‰ ì¤‘ì¸ ì „ì†¡ì„ ì¤‘ë‹¨í•©ë‹ˆë‹¤. onerror 콜백 FileTransferError.ABORT_ERRì˜ ì˜¤ë¥˜ 코드는 FileTransferError 개체를 전달 합니다. + +### 예를 들어 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +`FileTransferError` 개체 오류가 ë°œìƒ í•˜ë©´ 오류 ì½œë°±ì— ì „ë‹¬ ë©ë‹ˆë‹¤. + +### ì†ì„± + +* **code**: 미리 ì •ì˜ ëœ ì˜¤ë¥˜ 코드 중 하나가 ì•„ëž˜ì— ë‚˜ì—´ ëœ. (수) + +* **source**: 소스 URL. (문ìžì—´) + +* **target**: ëŒ€ìƒ URL. (문ìžì—´) + +* **http_status**: HTTP ìƒíƒœ 코드. ì´ íŠ¹ì„±ì€ ì‘답 코드를 HTTP ì—°ê²°ì—서 ìˆ˜ì‹ ì— ì‚¬ìš©í•  수 있습니다. (수) + +* **body** ì‘답 본문입니다. ì´ íŠ¹ì„±ì€ HTTP ì—°ê²°ì—서 ì‘ë‹µì„ ë°›ì„ ë•Œì— ì‚¬ìš©í•  수 있습니다. (문ìžì—´) + +* **exception**: ì–´ëŠ e.getMessage ë˜ëŠ” e.toString (문ìžì—´) + +### ìƒìˆ˜ + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## ì´ì „ 버전과 호환성 노트 + +ì´ í”ŒëŸ¬ê·¸ì¸ì˜ ì´ì „ 버전만 ì—…ë¡œë“œì— ëŒ€ 한 소스 ë˜ëŠ” ë‹¤ìš´ë¡œë“œì— ëŒ€ 한 ëŒ€ìƒ ìž¥ì¹˜ 절대 íŒŒì¼ ê²½ë¡œ ë°›ì•„ë“¤ì¼ ê²ƒ ì´ë‹¤. ì´ëŸ¬í•œ 경로 ì¼ë°˜ì ìœ¼ë¡œ í¼ì˜ 것 + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +대 한 ë’¤ 호환성, ì´ëŸ¬í•œ 경로 여전히 허용, 그리고 ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì˜êµ¬ 저장소ì—서 ì´ì™€ ê°™ì€ ê²½ë¡œ ê¸°ë¡ í–ˆë‹¤, 그때 ê·¸ë“¤ì€ ê³„ì†í•  수 있다면 사용할 수. + +ì´ëŸ¬í•œ 경로 `FileEntry` ë° íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì— ì˜í•´ 반환 ëœ `DirectoryEntry` ê°œì²´ì˜ `fullPath` ì†ì„±ì— 노출 ë˜ì—ˆë˜. 그러나 íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì˜,, ë” ì´ìƒ 새로운 버전 ìžë°” 스í¬ë¦½íŠ¸ì´ ê²½ë¡œ 노출. + +새로 업그레ì´ë“œ 하는 경우 (1.0.0 ì´ìƒ) ë²„ì „ì˜ íŒŒì¼, ë° ì´ì „ 사용 하 ê³  `entry.fullPath` `download()` ë˜ëŠ” `upload()` ì¸ìˆ˜ë¡œ ë‹¤ìŒ ëŒ€ì‹  íŒŒì¼ ì‹œìŠ¤í…œ Urlì„ ì‚¬ìš© 하 ì—¬ 코드를 변경 해야 합니다. + +í¼ì˜ íŒŒì¼ URLì„ ë°˜í™˜ 하는 `FileEntry.toURL()` ë° `DirectoryEntry.toURL()` + + cdvfile://localhost/persistent/path/to/file + + +ì–´ë–¤ `download()` ë° `upload()` 방법ì—서 절대 íŒŒì¼ ê²½ë¡œ 대신 사용할 수 있습니다. diff --git a/plugins/cordova-plugin-file-transfer/doc/pl/README.md b/plugins/cordova-plugin-file-transfer/doc/pl/README.md new file mode 100644 index 0000000..c430979 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/pl/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +Plugin dokumentacja: + +Plugin pozwala na przesyÅ‚anie i pobieranie plików. + +Ten plugin okreÅ›la globalne `FileTransfer`, `FileUploadOptions` konstruktorów. + +Chociaż w globalnym zasiÄ™gu, sÄ… nie dostÄ™pne dopiero po `deviceready` imprezie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Instalacja + + cordova plugin add cordova-plugin-file-transfer + + +## ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * PrzeglÄ…darka + * Firefox OS ** + * iOS + * Windows Phone 7 i 8 * + * Windows 8 + * Windows + +\ * *Nie obsÅ‚ugujÄ… `onprogress` ani `abort()` * + +\ ** *Nie obsÅ‚ugujÄ… `onprogress` * + +# FileTransfer + +Obiekt `FileTransfer` zapewnia sposób wgrać pliki za pomocÄ… Multi-część POST lub PUT żądania HTTP i pobierania plików, jak również. + +## WÅ‚aÅ›ciwoÅ›ci + + * **OnProgress**: o nazwie `ProgressEvent` gdy nowy kawaÅ‚ek danych jest przenoszona. *(Funkcja)* + +## Metody + + * **wgraj**: wysyÅ‚a plik na serwer. + + * **do pobrania**: pliki do pobrania pliku z serwera. + + * **przerwać**: przerywa w toku transferu. + +## upload + +**Parametry**: + + * **fileURL**: URL plików reprezentujÄ…cych pliku na urzÄ…dzenie. Dla wstecznej kompatybilnoÅ›ci, to może również być peÅ‚nÄ… Å›cieżkÄ™ pliku na urzÄ…dzenie. (Zobacz [wstecz zgodnoÅ›ci zauważa] poniżej) + + * **serwer**: adres URL serwera, aby otrzymać plik, jak kodowane przez`encodeURI()`. + + * **successCallback**: wywoÅ‚ania zwrotnego, który jest przekazywany obiekt `FileUploadResult`. *(Funkcja)* + + * **errorCallback**: wywoÅ‚anie zwrotne, które wykonuje, jeżeli wystÄ…pi błąd pobierania `FileUploadResult`. WywoÅ‚ywany z obiektu `FileTransferError`. *(Funkcja)* + + * **Opcje**: parametry opcjonalne *(obiektu)*. Ważne klucze: + + * **fileKey**: nazwa elementu form. DomyÅ›lnie `file` . (DOMString) + * **Nazwa pliku**: nazwy pliku, aby użyć podczas zapisywania pliku na serwerze. DomyÅ›lnie `image.jpg` . (DOMString) + * **element httpMethod**: Metoda HTTP do użycia - `umieÅ›cić` lub `POST`. DomyÅ›lnie `POST`. (DOMString) + * **mimeType**: Typ mime danych do przesÅ‚ania. DomyÅ›lnie do `image/jpeg`. (DOMString) + * **params**: zestaw par opcjonalny klucz/wartość w żądaniu HTTP. (Obiekt) + * **chunkedMode**: czy przekazać dane w trybie pakietowego przesyÅ‚ania strumieniowego. WartoÅ›ciÄ… domyÅ›lnÄ… jest `true`. (Wartość logiczna) + * **headers**: Mapa wartoÅ›ci Nazwa/nagłówka nagłówek. Aby okreÅ›lić wiÄ™cej niż jednÄ… wartość, należy użyć tablicÄ™. Na iOS, FireOS i Android jeÅ›li nagłówek o nazwie Content-Type jest obecny, wieloczęściowa forma nie danych. (Object) + * **element httpMethod**: Metoda HTTP np. POST lub PUT. Ustawienia domyÅ›lne do `POST`. (DOMString) + + * **trustAllHosts**: parametr opcjonalny, domyÅ›lnie `false` . JeÅ›li zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeÅ„stwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca siÄ™ do użytku produkcyjnego. ObsÅ‚ugiwane na Androida i iOS. *(wartość logiczna)* + +### PrzykÅ‚ad + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### PrzykÅ‚ad z PrzeÅ›lij nagłówki i zdarzeÅ„ postÄ™pu (Android i iOS tylko) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Obiekt `FileUploadResult` jest przekazywana do sukcesu wywoÅ‚ania zwrotnego metody `upload() sÅ‚użącÄ…` obiektu `FileTransfer`. + +### WÅ‚aÅ›ciwoÅ›ci + + * **bytesSent**: liczba bajtów wysÅ‚anych do serwera jako część upload. (dÅ‚ugie) + + * **responseCode**: kod odpowiedzi HTTP, zwracane przez serwer. (dÅ‚ugie) + + * **odpowiedź**: HTTP odpowiedzi zwracane przez serwer. (DOMString) + + * **nagłówki**: nagłówki HTTP odpowiedzi przez serwer. (Obiekt) + + * Obecnie obsÅ‚ugiwane na iOS tylko. + +### Dziwactwa iOS + + * Nie obsÅ‚uguje `responseCode` lub`bytesSent`. + +## download + +**Parametry**: + + * **ŹródÅ‚o**: adres URL serwera, aby pobrać plik, jak kodowane przez`encodeURI()`. + + * **cel**: url plików reprezentujÄ…cych pliku na urzÄ…dzenie. Dla wstecznej kompatybilnoÅ›ci, to może również być peÅ‚nÄ… Å›cieżkÄ™ pliku na urzÄ…dzenie. (Zobacz [wstecz zgodnoÅ›ci zauważa] poniżej) + + * **successCallback**: wywoÅ‚ania zwrotnego, który jest przekazywany `FileEntry` obiektu. *(Funkcja)* + + * **errorCallback**: wywoÅ‚anie zwrotne, które wykonuje, jeÅ›li wystÄ…pi błąd podczas pobierania `FileEntry`. WywoÅ‚ywany z obiektu `FileTransferError`. *(Funkcja)* + + * **trustAllHosts**: parametr opcjonalny, domyÅ›lnie `false` . JeÅ›li zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeÅ„stwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca siÄ™ do użytku produkcyjnego. ObsÅ‚ugiwane na Androida i iOS. *(wartość logiczna)* + + * **Opcje**: parametry opcjonalne, obecnie tylko obsÅ‚uguje nagłówki (takie jak autoryzacja (uwierzytelnianie podstawowe), itp.). + +### PrzykÅ‚ad + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### WP8 dziwactwa + + * Pobierz wnioski sÄ… buforowane przez rodzimych realizacji. Aby uniknąć, buforowanie, przekazać `if-Modified-Since` nagłówka do pobrania Metoda. + +## abort + +Przerywa w toku transferu. Onerror callback jest przekazywany obiekt FileTransferError, który kod błędu z FileTransferError.ABORT_ERR. + +### PrzykÅ‚ad + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +Obiekt `FileTransferError` jest przekazywana do błąd wywoÅ‚ania zwrotnego, gdy wystÄ…pi błąd. + +### WÅ‚aÅ›ciwoÅ›ci + + * **Kod**: jeden z kodów błędów wstÄ™pnie zdefiniowanych poniżej. (Liczba) + + * **ŹródÅ‚o**: URL do źródÅ‚a. (String) + + * **cel**: adres URL do docelowego. (String) + + * **HTTP_STATUS**: kod stanu HTTP. Ten atrybut jest dostÄ™pna tylko po otrzymaniu kodu odpowiedzi z połączenia HTTP. (Liczba) + + * **body** Treść odpowiedzi. Ten atrybut jest dostÄ™pna tylko wtedy, gdy odpowiedź jest otrzymanych od połączenia HTTP. (String) + + * **exception**: albo e.getMessage lub e.toString (String) + +### StaÅ‚e + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Do tyÅ‚u zgodnoÅ›ci stwierdza + +Poprzednie wersje tego pluginu tylko zaakceptować urzÄ…dzenia bezwzglÄ™dnych Å›cieżek jako źródÅ‚o dla przekazywania, lub w celu pobrania. Te Å›cieżki bÄ™dzie zazwyczaj formy + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Do tyÅ‚u zgodnoÅ›ci, akceptowane sÄ… jeszcze te Å›cieżki, i jeÅ›li aplikacja nagraÅ‚ Å›cieżki, jak te w trwaÅ‚ej pamiÄ™ci, nastÄ™pnie można nadal stosować. + +Te Å›cieżki byÅ‚y narażone wczeÅ›niej we wÅ‚aÅ›ciwoÅ›ci `fullPath` `FileEntry` i `DirectoryEntry` obiektów zwróconych przez wtyczki pliku. Nowe wersje pliku plugin, jednak już wystawiać te Å›cieżki do JavaScript. + +JeÅ›li uaktualniasz nowy (1.0.0 lub nowsza) wersja pliku i mieć wczeÅ›niej przy `entry.fullPath` jako argumenty `download()` lub `upload() sÅ‚użącÄ…`, a nastÄ™pnie trzeba bÄ™dzie zmienić kod aby używać adresów URL plików zamiast. + +`FileEntry.toURL()` i `DirectoryEntry.toURL()` zwraca adres URL plików formularza + + cdvfile://localhost/persistent/path/to/file + + +które mogÄ… być używane zamiast bezwzglÄ™dna Å›cieżka zarówno `download()` i `metody upload()` metody. \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/pl/index.md b/plugins/cordova-plugin-file-transfer/doc/pl/index.md new file mode 100644 index 0000000..ff9b557 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/pl/index.md @@ -0,0 +1,302 @@ + + +# cordova-plugin-file-transfer + +Plugin pozwala na przesyÅ‚anie i pobieranie plików. + +Ten plugin okreÅ›la globalne `FileTransfer`, `FileUploadOptions` konstruktorów. + +Chociaż w globalnym zasiÄ™gu, sÄ… nie dostÄ™pne dopiero po `deviceready` imprezie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## Instalacja + + cordova plugin add cordova-plugin-file-transfer + + +## ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* PrzeglÄ…darka +* Firefox OS ** +* iOS +* Windows Phone 7 i 8 * +* Windows 8 +* Windows + +* *Nie obsÅ‚ugujÄ… `onprogress` ani `abort()`* + +* * *Nie obsÅ‚ugujÄ… `onprogress`* + +# FileTransfer + +Obiekt `FileTransfer` zapewnia sposób wgrać pliki przy użyciu żądania HTTP wieloczęściowe POST i pobierania plików, jak również. + +## WÅ‚aÅ›ciwoÅ›ci + +* **OnProgress**: o nazwie `ProgressEvent` gdy nowy kawaÅ‚ek danych jest przenoszona. *(Funkcja)* + +## Metody + +* **wgraj**: wysyÅ‚a plik na serwer. + +* **do pobrania**: pliki do pobrania pliku z serwera. + +* **przerwać**: przerywa w toku transferu. + +## upload + +**Parametry**: + +* **fileURL**: URL plików reprezentujÄ…cych pliku na urzÄ…dzenie. Dla wstecznej kompatybilnoÅ›ci, to może również być peÅ‚nÄ… Å›cieżkÄ™ pliku na urzÄ…dzenie. (Zobacz [wstecz zgodnoÅ›ci zauważa] poniżej) + +* **serwer**: adres URL serwera, aby otrzymać plik, jak kodowane przez`encodeURI()`. + +* **successCallback**: wywoÅ‚ania zwrotnego, który jest przekazywany obiekt `FileUploadResult`. *(Funkcja)* + +* **errorCallback**: wywoÅ‚anie zwrotne, które wykonuje, jeżeli wystÄ…pi błąd pobierania `FileUploadResult`. WywoÅ‚ywany z obiektu `FileTransferError`. *(Funkcja)* + +* **Opcje**: parametry opcjonalne *(obiektu)*. Ważne klucze: + + * **fileKey**: nazwa elementu form. DomyÅ›lnie `file` . (DOMString) + * **Nazwa pliku**: nazwy pliku, aby użyć podczas zapisywania pliku na serwerze. DomyÅ›lnie `image.jpg` . (DOMString) + * **element httpMethod**: Metoda HTTP do użycia - `umieÅ›cić` lub `POST`. DomyÅ›lnie `POST`. (DOMString) + * **mimeType**: Typ mime danych do przesÅ‚ania. DomyÅ›lnie do `image/jpeg`. (DOMString) + * **params**: zestaw par opcjonalny klucz/wartość w żądaniu HTTP. (Obiekt) + * **chunkedMode**: czy przekazać dane w trybie pakietowego przesyÅ‚ania strumieniowego. WartoÅ›ciÄ… domyÅ›lnÄ… jest `true`. (Wartość logiczna) + * **headers**: Mapa wartoÅ›ci Nazwa/nagłówka nagłówek. Aby okreÅ›lić wiÄ™cej niż jednÄ… wartość, należy użyć tablicÄ™. (Obiekt) + +* **trustAllHosts**: parametr opcjonalny, domyÅ›lnie `false` . JeÅ›li zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeÅ„stwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca siÄ™ do użytku produkcyjnego. ObsÅ‚ugiwane na Androida i iOS. *(wartość logiczna)* + +### PrzykÅ‚ad + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### PrzykÅ‚ad z PrzeÅ›lij nagłówki i zdarzeÅ„ postÄ™pu (Android i iOS tylko) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Obiekt `FileUploadResult` jest przekazywana do sukcesu wywoÅ‚ania zwrotnego metody `upload() sÅ‚użącÄ…` obiektu `FileTransfer`. + +### WÅ‚aÅ›ciwoÅ›ci + +* **bytesSent**: liczba bajtów wysÅ‚anych do serwera jako część upload. (dÅ‚ugie) + +* **responseCode**: kod odpowiedzi HTTP, zwracane przez serwer. (dÅ‚ugie) + +* **odpowiedź**: HTTP odpowiedzi zwracane przez serwer. (DOMString) + +* **nagłówki**: nagłówki HTTP odpowiedzi przez serwer. (Obiekt) + + * Obecnie obsÅ‚ugiwane na iOS tylko. + +### Dziwactwa iOS + +* Nie obsÅ‚uguje `responseCode` lub`bytesSent`. + +## download + +**Parametry**: + +* **ŹródÅ‚o**: adres URL serwera, aby pobrać plik, jak kodowane przez`encodeURI()`. + +* **cel**: url plików reprezentujÄ…cych pliku na urzÄ…dzenie. Dla wstecznej kompatybilnoÅ›ci, to może również być peÅ‚nÄ… Å›cieżkÄ™ pliku na urzÄ…dzenie. (Zobacz [wstecz zgodnoÅ›ci zauważa] poniżej) + +* **successCallback**: wywoÅ‚ania zwrotnego, który jest przekazywany `FileEntry` obiektu. *(Funkcja)* + +* **errorCallback**: wywoÅ‚anie zwrotne, które wykonuje, jeÅ›li wystÄ…pi błąd podczas pobierania `FileEntry`. WywoÅ‚ywany z obiektu `FileTransferError`. *(Funkcja)* + +* **trustAllHosts**: parametr opcjonalny, domyÅ›lnie `false` . JeÅ›li zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeÅ„stwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca siÄ™ do użytku produkcyjnego. ObsÅ‚ugiwane na Androida i iOS. *(wartość logiczna)* + +* **Opcje**: parametry opcjonalne, obecnie tylko obsÅ‚uguje nagłówki (takie jak autoryzacja (uwierzytelnianie podstawowe), itp.). + +### PrzykÅ‚ad + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## abort + +Przerywa w toku transferu. Onerror callback jest przekazywany obiekt FileTransferError, który kod błędu z FileTransferError.ABORT_ERR. + +### PrzykÅ‚ad + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +Obiekt `FileTransferError` jest przekazywana do błąd wywoÅ‚ania zwrotnego, gdy wystÄ…pi błąd. + +### WÅ‚aÅ›ciwoÅ›ci + +* **Kod**: jeden z kodów błędów wstÄ™pnie zdefiniowanych poniżej. (Liczba) + +* **ŹródÅ‚o**: URL do źródÅ‚a. (String) + +* **cel**: adres URL do docelowego. (String) + +* **HTTP_STATUS**: kod stanu HTTP. Ten atrybut jest dostÄ™pna tylko po otrzymaniu kodu odpowiedzi z połączenia HTTP. (Liczba) + +* **body** Treść odpowiedzi. Ten atrybut jest dostÄ™pna tylko wtedy, gdy odpowiedź jest otrzymanych od połączenia HTTP. (String) + +* **exception**: albo e.getMessage lub e.toString (String) + +### StaÅ‚e + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Do tyÅ‚u zgodnoÅ›ci stwierdza + +Poprzednie wersje tego pluginu tylko zaakceptować urzÄ…dzenia bezwzglÄ™dnych Å›cieżek jako źródÅ‚o dla przekazywania, lub w celu pobrania. Te Å›cieżki bÄ™dzie zazwyczaj formy + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Do tyÅ‚u zgodnoÅ›ci, akceptowane sÄ… jeszcze te Å›cieżki, i jeÅ›li aplikacja nagraÅ‚ Å›cieżki, jak te w trwaÅ‚ej pamiÄ™ci, nastÄ™pnie można nadal stosować. + +Te Å›cieżki byÅ‚y narażone wczeÅ›niej we wÅ‚aÅ›ciwoÅ›ci `fullPath` `FileEntry` i `DirectoryEntry` obiektów zwróconych przez wtyczki pliku. Nowe wersje pliku plugin, jednak już wystawiać te Å›cieżki do JavaScript. + +JeÅ›li uaktualniasz nowy (1.0.0 lub nowsza) wersja pliku i mieć wczeÅ›niej przy `entry.fullPath` jako argumenty `download()` lub `upload() sÅ‚użącÄ…`, a nastÄ™pnie trzeba bÄ™dzie zmienić kod aby używać adresów URL plików zamiast. + +`FileEntry.toURL()` i `DirectoryEntry.toURL()` zwraca adres URL plików formularza + + cdvfile://localhost/persistent/path/to/file + + +które mogÄ… być używane zamiast bezwzglÄ™dna Å›cieżka zarówno `download()` i `metody upload()` metody. diff --git a/plugins/cordova-plugin-file-transfer/doc/ru/index.md b/plugins/cordova-plugin-file-transfer/doc/ru/index.md new file mode 100644 index 0000000..cdd5a15 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/ru/index.md @@ -0,0 +1,290 @@ + + +# cordova-plugin-file-transfer + +Этот плагин позволÑет вам загружать и Ñкачивать файлы. + +## УÑтановка + + cordova plugin add cordova-plugin-file-transfer + + +## Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS ** +* iOS +* Windows Phone 7 и 8 * +* Windows 8 ***| +* Windows ***| + +* *Ðе поддерживают `onprogress` , ни `abort()` * + +** *Ðе поддерживает `onprogress` * + +ЧаÑÑ‚Ð¸Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ° `onprogress` Ð´Ð»Ñ Ð·Ð°ÐºÐ°Ñ‡ÐºÐ¸ метод. `onprogress` вызываетÑÑ Ñ Ð¿ÑƒÑтой ход Ñобытий Ð±Ð»Ð°Ð³Ð¾Ð´Ð°Ñ€Ñ Windows limitations_ + +# FileTransfer + +`FileTransfer`Объект предоÑтавлÑет ÑпоÑоб Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ файлов Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ неÑкольких чаÑтей запроÑа POST HTTP и Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ файлов, а также. + +## Параметры + +* **OnProgress**: называетÑÑ Ñ `ProgressEvent` вÑÑкий раз, когда новый фрагмент данных передаетÑÑ. *(ФункциÑ)* + +## Методы + +* **добавлено**: отправлÑет файл на Ñервер. + +* **Ñкачать**: Скачать файл Ñ Ñервера. + +* **прервать**: прерывает передачу в прогреÑÑ. + +## загрузить + +**Параметры**: + +* **fileURL**: файловой ÑиÑтемы URL-адреÑ, предÑтавлÑющий файл на уÑтройÑтве. Ð”Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти, Ñто также может быть полный путь к файлу на уÑтройÑтве. (См. [обратной ÑовмеÑтимоÑти отмечает] ниже) + +* **Ñервер**: URL-Ð°Ð´Ñ€ÐµÑ Ñервера, чтобы получить файл, как закодированные`encodeURI()`. + +* **successCallback**: обратного вызова, передаваемого `Metadata` объект. *(ФункциÑ)* + +* **errorCallback**: обратного вызова, который выполнÑетÑÑ Ð² Ñлучае Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¸ `Metadata` . Вызываемый Ñ `FileTransferError` объект. *(ФункциÑ)* + +* **опции**: необÑзательные параметры *(объект)*. ДопуÑтимые ключи: + + * **fileKey**: Ð¸Ð¼Ñ Ñлемента form. По умолчанию `file` . (DOMString) + * **Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°**: Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸ Ñохранении файла на Ñервере. По умолчанию `image.jpg` . (DOMString) + * **mimeType**: mime-тип данных Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸. По умолчанию `image/jpeg` . (DOMString) + * **params**: набор пар дополнительный ключ/значение Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ в HTTP-запроÑе. (Объект) + * **chunkedMode**: Ñледует ли загружать данные в фрагментарноÑти потоковом режиме. По умолчанию `true` . (ЛогичеÑкое значение) + * **заголовки**: Карта значений заголовок Ð¸Ð¼Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°. ИÑпользуйте маÑÑив Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð±Ð¾Ð»ÐµÐµ одного значениÑ. (Объект) + +* **trustAllHosts**: необÑзательный параметр, по умолчанию `false` . ЕÑли значение `true` , она принимает вÑе Ñертификаты безопаÑноÑти. Это полезно, поÑкольку Android отвергает ÑамозаверÑющие Ñертификаты. Ðе рекомендуетÑÑ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² производÑтве. ПоддерживаетÑÑ Ð½Ð° Android и iOS. *(логичеÑкое значение)* + +### Пример + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### Пример Ñ Ð·Ð°Ð³Ñ€ÑƒÐ¶Ð°Ñ‚ÑŒ заголовки и ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Progress (Android и iOS только) + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +Объект `FileUploadResult` передаетÑÑ Ð½Ð° уÑпех обратного вызова метода `upload()` объекта `FileTransfer`. + +### Параметры + +* **bytesSent**: количеÑтво байт, отправленных на Ñервер как чаÑть загрузки. (длиннаÑ) + +* **responseCode**: код ответа HTTP, возвращаемых Ñервером. (длиннаÑ) + +* **ответ**: ответ HTTP, возвращаемых Ñервером. (DOMString) + +* **заголовки**: заголовки ответов HTTP-Ñервером. (Объект) + + * Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÑ‚ÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ iOS. + +### ОÑобенноÑти iOS + +* Ðе поддерживает `responseCode` или`bytesSent`. + +## Скачать + +**Параметры**: + +* **иÑточник**: URL-Ð°Ð´Ñ€ÐµÑ Ñервера Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ файла, как закодированные`encodeURI()`. + +* **Цель**: файловой ÑиÑтемы URL-адреÑ, предÑтавлÑющий файл на уÑтройÑтве. Ð”Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти, Ñто также может быть полный путь к файлу на уÑтройÑтве. (См. [обратной ÑовмеÑтимоÑти отмечает] ниже) + +* **successCallback**: обратного вызова, передаваемого `FileEntry` объект. *(ФункциÑ)* + +* **errorCallback**: обратного вызова, который выполнÑетÑÑ, еÑли возникает ошибка при получении `Metadata` . Вызываемый Ñ `FileTransferError` объект. *(ФункциÑ)* + +* **trustAllHosts**: необÑзательный параметр, по умолчанию `false` . ЕÑли значение `true` , она принимает вÑе Ñертификаты безопаÑноÑти. Это полезно, потому что Android отвергает ÑамозаверÑющие Ñертификаты. Ðе рекомендуетÑÑ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² производÑтве. ПоддерживаетÑÑ Ð½Ð° Android и iOS. *(логичеÑкое значение)* + +* **опции**: необÑзательные параметры, в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ поддерживает заголовки (например авторизации (Ð±Ð°Ð·Ð¾Ð²Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ) и Ñ‚.д.). + +### Пример + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## прервать + +Прерывает передачу в прогреÑÑ. Onerror обратного вызова передаетÑÑ Ð¾Ð±ÑŠÐµÐºÑ‚ FileTransferError, который имеет код ошибки FileTransferError.ABORT_ERR. + +### Пример + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +A `FileTransferError` при ошибке обратного вызова передаетÑÑ Ð¾Ð±ÑŠÐµÐºÑ‚, при возникновении ошибки. + +### Параметры + +* **код**: один из кодов Ñтандартных ошибок, перечиÑленные ниже. (ЧиÑло) + +* **иÑточник**: URL-Ð°Ð´Ñ€ÐµÑ Ð¸Ñточника. (Строка) + +* **Цель**: URL-Ð°Ð´Ñ€ÐµÑ Ðº целевому объекту. (Строка) + +* **http_status**: код ÑоÑтоÑÐ½Ð¸Ñ HTTP. Этот атрибут доÑтупен только при код ответа от HTTP-ÑоединениÑ. (ЧиÑло) + +* **иÑключение**: либо e.getMessage или e.toString (Ñтрока) + +### КонÑтанты + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## Обратной ÑовмеÑтимоÑти отмечает + +Предыдущие верÑии Ñтого плагина будет принимать только уÑтройÑтва ÐбÑолют файлам как иÑточник Ð´Ð»Ñ Ð·Ð°ÐºÐ°Ñ‡ÐºÐ¸, или как целевых Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¾Ðº. Обычно Ñти пути бы формы + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Ð”Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти, по-прежнему принимаютÑÑ Ñти пути, и еÑли ваше приложение зарегиÑтрировано пути как в поÑтоÑнное хранилище, то они могут продолжать иÑпользоватьÑÑ. + +Эти пути ранее были видны в `fullPath` ÑвойÑтва `FileEntry` и `DirectoryEntry` объекты, возвращаемые файл плагина. Ðовые верÑии файла плагина, однако, не подвергать Ñти пути в JavaScript. + +ЕÑли вы переходите на новый (1.0.0 или новее) верÑию файла и вы ранее иÑпользовали `entry.fullPath` в качеÑтве аргументов `download()` или `upload()` , то вам необходимо будет изменить код Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы URL вмеÑто. + +`FileEntry.toURL()`и `DirectoryEntry.toURL()` возвращает URL-Ð°Ð´Ñ€ÐµÑ Ñ„Ð¾Ñ€Ð¼Ñ‹ файловой ÑиÑтемы + + cdvfile://localhost/persistent/path/to/file + + +которые могут быть иÑпользованы вмеÑто абÑолютного пути в обоих `download()` и `upload()` методы. diff --git a/plugins/cordova-plugin-file-transfer/doc/zh/README.md b/plugins/cordova-plugin-file-transfer/doc/zh/README.md new file mode 100644 index 0000000..5399b04 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/zh/README.md @@ -0,0 +1,311 @@ + + +# cordova-plugin-file-transfer + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer) + +å¤–æŽ›ç¨‹å¼æª”: + +這個外掛程å¼å…許你上傳和下載檔案。 + +這個外掛程å¼å®šç¾©å…¨åŸŸ `FileTransfer`,`FileUploadOptions` 的建構函å¼ã€‚ + +雖然在全çƒç¯„åœå…§ï¼Œä»–們ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-file-transfer + + +## 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç€è¦½å™¨ + * ç«ç‹ç€è¦½å™¨çš„作業系統 * * + * iOS + * Windows Phone 7 å’Œ 8 * + * Windows 8 + * Windows + +\ **䏿”¯æ´`onprogress`也`abort()` * + +\ * **䏿”¯æ´`onprogress` * + +# 檔案傳輸 + +`FileTransfer`物件æä¾›ä¸Šå‚³æª”使用 HTTP 多部分è·ä½æˆ–付諸表決的請求,並將檔以åŠä¸‹è¼‰çš„æ–¹å¼ã€‚ + +## 屬性 + + * **onprogress**: 使用調用 `ProgressEvent` æ¯ç•¶ä¸€å¡Šæ–°çš„資料傳輸。*(函數)* + +## 方法 + + * **upload**: 將檔發é€åˆ°ä¼ºæœå™¨ã€‚ + + * **download**: 從伺æœå™¨ä¸Šä¸‹è¼‰æª”案。 + + * **abort**: 中止正在進行轉讓。 + +## upload + +**åƒæ•¸**: + + * **fileURL**: 表示檔在設備上的檔案系統 URL。 為å‘後相容性,這也å¯ä»¥å°‡è¨­å‚™ä¸Šçš„æª”的完整路徑。 (請åƒè¦‹ [å‘後相容性注æ„到] 下é¢) + + * **server**: 伺æœå™¨ä»¥æŽ¥æ”¶è©²æª”,由編碼的 URL`encodeURI()`. + + * **successCallback**: 一個通éŽä¸€å€‹ `FileUploadResult` 物件的回檔。*(函數)* + + * **errorCallback**: 如果發生錯誤,檢索 `FileUploadResult` 執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)* + + * **options**: å¯é¸åƒæ•¸*(物件)*。有效的金鑰: + + * **fileKey**: 表單元素的å稱。é è¨­å€¼ç‚º `file` 。() DOMString + * **fileName**: è¦ä¿å­˜åœ¨ä¼ºæœå™¨ä¸Šçš„æª”時使用的檔案å稱。é è¨­å€¼ç‚º `image.jpg` 。() DOMString + * **httpMethod**: HTTP 方法使用-`PUT` 或 `POST`。é è¨­å€¼ç‚º `POST`。() DOMString + * **mimeType**: è¦ä¸Šè¼‰çš„資料的 mime 類型。é è¨­è¨­ç½®ç‚º `image/jpeg`。() DOMString + * **params**: 一組è¦åœ¨ HTTP è¦æ±‚中傳éžçš„å¯é¸çš„éµå€¼å°ã€‚(物件) + * **chunkedMode**: 是å¦è¦åˆ†å¡Šçš„æµå¼è™•ç†æ¨¡å¼ä¸­çš„資料上載。é è¨­å€¼ç‚º `true`。(布林值) + * **headers**: 地圖的標頭å稱/標頭值。 使用陣列來指定多個值。 IOS〠FireOS,和安å“系統,如果已命å的內容類型標頭存在,多部分表單資料ä¸è¢«ä½¿ç”¨ã€‚ (Object) + * **httpMethod**: HTTP 方法,例如使用張貼或放。 é è¨­ç‚º`開機自檢`。() DOMString + + * **trustAllHosts**: å¯é¸åƒæ•¸ï¼Œé è¨­å€¼ç‚º `false` 。 如果設置為 `true` ,它å¯ä»¥æŽ¥å—的所有安全證書。 這是有用的因為 android 系統拒絕自簽å的安全證書。 ä¸å»ºè­°ä¾›ç”Ÿç”¢ä½¿ç”¨ã€‚ 在 Android å’Œ iOS ä¸Šå—æ”¯æ´ã€‚ *(布林值)* + +### 示例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### 與上傳的標頭和進度事件 (Android å’Œ iOS åªï¼‰ 的示例 + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +`FileUploadResult` 物件將傳éžçµ¦è©² `檔案傳輸` 物件的 `upload()` 方法的æˆåŠŸå›žæª”ã€‚ + +### 屬性 + + * **bytesSent**: 作為上載的一部分發é€åˆ°ä¼ºæœå™¨çš„ä½å…ƒçµ„數。(é•·) + + * **responseCode**: 由伺æœå™¨è¿”回的 HTTP 回應代碼。(é•·) + + * **response**: 由伺æœå™¨è¿”回的 HTTP 回應。() DOMString + + * **headers**: 由伺æœå™¨çš„ HTTP 回應標頭。(物件) + + * ç›®å‰æ”¯æ´çš„ iOS åªã€‚ + +### iOS 的怪癖 + + * 䏿”¯æ´ `responseCode` 或`bytesSent`. + +## download + +**åƒæ•¸**: + + * **source**: è¦ä¸‹è¼‰çš„æª”,如由編碼的伺æœå™¨çš„ URL`encodeURI()`. + + * **target**: 表示檔在設備上的檔案系統 url。 為å‘後相容性,這也å¯ä»¥å°‡è¨­å‚™ä¸Šçš„æª”的完整路徑。 (請åƒè¦‹ [å‘後相容性注æ„到] 下é¢) + + * **successCallback**: 傳éžä¸€å€‹å›žæª” `FileEntry` 物件。*(函數)* + + * **errorCallback**: 如果檢索 `FileEntry` 時發生錯誤,則執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)* + + * **trustAllHosts**: å¯é¸åƒæ•¸ï¼Œé è¨­å€¼ç‚º `false` 。 如果設置為 `true` ,它å¯ä»¥æŽ¥å—的所有安全證書。 這是有用的因為 Android 拒絕自行簽署å¼å®‰å…¨è­‰æ›¸ã€‚ ä¸å»ºè­°ä¾›ç”Ÿç”¢ä½¿ç”¨ã€‚ 在 Android å’Œ iOS ä¸Šå—æ”¯æ´ã€‚ *(布林值)* + + * **options**: å¯é¸åƒæ•¸ï¼Œç›®å‰åªæ”¯æ´æ¨™é¡Œ (如授權 (基本驗證) 等)。 + +### 示例 + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +### WP8 的怪癖 + + * 下載請求由本機實ç¾è¢«ç·©å­˜ã€‚è‹¥è¦é¿å…緩存,傳éž`如果修改自`郵件頭以下載方法。 + +## abort + +中止正在進行轉讓。Onerror 回檔傳éžä¸€å€‹ FileTransferError 物件具有 FileTransferError.ABORT_ERR 錯誤代碼。 + +### 示例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +當發生錯誤時,`FileTransferError` 物件將傳éžçµ¦éŒ¯èª¤å›žæª”。 + +### 屬性 + + * **code**: 下é¢åˆ—出的é å®šç¾©çš„錯誤代碼之一。(人數) + + * **source**: æºçš„ URL。(字串) + + * **target**: 到目標 URL。(字串) + + * **HTTP_status**: HTTP 狀態碼。從 HTTP 連接收到一個回應代碼時,此屬性æ‰å¯ç”¨ã€‚(人數) + + * **body**回應正文。此屬性åªèƒ½æ˜¯å¯ç”¨çš„當該 HTTP 連接收到答覆。(字串) + + * **exception**: è¦éº¼ e.getMessage 或 e.toString (字串) + +### å¸¸é‡ + + * 1 = `FileTransferError.FILE_NOT_FOUND_ERR` + * 2 = `FileTransferError.INVALID_URL_ERR` + * 3 = `FileTransferError.CONNECTION_ERR` + * 4 = `FileTransferError.ABORT_ERR` + * 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## å‘後相容性注æ„到 + +以å‰ç‰ˆæœ¬çš„é€™å€‹å¤–æŽ›ç¨‹å¼æ‰æœƒæŽ¥å—設備-çµ•å°æª”路徑作為æºå°æ–¼ä¸Šè¼‰ï¼Œæˆ–用於下載的目標。這些路徑通常會在表單 + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +為å‘å¾Œç›¸å®¹æ€§ï¼Œé€™äº›è·¯å¾‘ä»æœƒè¢«æŽ¥å—,和如果您的應用程å¼å·²éŒ„å¾—åƒé€™äº›åœ¨æŒä¹…性存儲的路徑,然後他們å¯ä»¥ç¹¼çºŒä½¿ç”¨ã€‚ + +é€™äº›è·¯å¾‘è¢«ä»¥å‰æš´éœ²åœ¨ `FileEntry` 和由檔外掛程å¼è¿”回的 `DirectoryEntry` 物件的 `fullPath` 屬性中。 新版本的檔的外掛程å¼ï¼Œä½†æ˜¯ï¼Œä¸å†å…¬é–‹é€™äº› JavaScript 的路徑。 + +如果您è¦å‡ç´šåˆ°æ–° (1.0.0 或更高版本) 版本的檔,和你以å‰ä¸€ç›´åœ¨ä½¿ç”¨ `entry.fullPath` ä½œç‚ºåƒæ•¸åˆ° `download()` 或 `upload()`ï¼Œé‚£éº¼ä½ å°‡éœ€è¦æ›´æ”¹ä»£ç¢¼ä»¥ä½¿ç”¨æª”案系統的 Url 來代替。 + +`FileEntry.toURL()` å’Œ `DirectoryEntry.toURL()` 返回的表單檔案 URL + + cdvfile://localhost/persistent/path/to/file + + +它å¯ä»¥ç”¨åœ¨ `download()` å’Œ `upload()` å…©ç¨®æ–¹æ³•ä¸­çš„çµ•å°æª”路徑ä½ç½®ã€‚ \ No newline at end of file diff --git a/plugins/cordova-plugin-file-transfer/doc/zh/index.md b/plugins/cordova-plugin-file-transfer/doc/zh/index.md new file mode 100644 index 0000000..b3af1eb --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/doc/zh/index.md @@ -0,0 +1,302 @@ + + +# cordova-plugin-file-transfer + +這個外掛程å¼å…許你上傳和下載檔案。 + +這個外掛程å¼å®šç¾©å…¨åŸŸ `FileTransfer`,`FileUploadOptions` 的建構函å¼ã€‚ + +雖然在全çƒç¯„åœå…§ï¼Œä»–們ä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(FileTransfer); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-file-transfer + + +## 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç€è¦½å™¨ +* ç«ç‹ç€è¦½å™¨çš„作業系統 * * +* iOS +* Windows Phone 7 å’Œ 8 * +* Windows 8 +* Windows + +* *䏿”¯æ´ `onprogress` 也 `abort()`* + +* * *䏿”¯æ´ `onprogress`* + +# FileTransfer + +`FileTransfer` 物件æä¾›ä¸€ç¨®ä½¿ç”¨ HTTP 多部分 POST è«‹æ±‚çš„æª”ä¸Šå‚³ï¼Œä¸‹è¼‰æª”æ¡ˆä»¥åŠæ–¹å¼ã€‚ + +## 屬性 + +* **onprogress**: 使用調用 `ProgressEvent` æ¯ç•¶ä¸€å¡Šæ–°çš„資料傳輸。*(函數)* + +## 方法 + +* **upload**: 將檔發é€åˆ°ä¼ºæœå™¨ã€‚ + +* **download**: 從伺æœå™¨ä¸Šä¸‹è¼‰æª”案。 + +* **abort**: 中止正在進行轉讓。 + +## upload + +**åƒæ•¸**: + +* **fileURL**: 表示檔在設備上的檔案系統 URL。 為å‘後相容性,這也å¯ä»¥å°‡è¨­å‚™ä¸Šçš„æª”的完整路徑。 (請åƒè¦‹ [å‘後相容性注æ„到] 下é¢) + +* **server**: 伺æœå™¨ä»¥æŽ¥æ”¶è©²æª”,由編碼的 URL`encodeURI()`. + +* **successCallback**: 一個通éŽä¸€å€‹ `FileUploadResult` 物件的回檔。*(函數)* + +* **errorCallback**: 如果發生錯誤,檢索 `FileUploadResult` 執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)* + +* **options**: å¯é¸åƒæ•¸*(物件)*。有效的金鑰: + + * **fileKey**: 表單元素的å稱。é è¨­å€¼ç‚º `file` 。() DOMString + * **fileName**: è¦ä¿å­˜åœ¨ä¼ºæœå™¨ä¸Šçš„æª”時使用的檔案å稱。é è¨­å€¼ç‚º `image.jpg` 。() DOMString + * **httpMethod**: HTTP 方法使用-`PUT` 或 `POST`。é è¨­å€¼ç‚º `POST`。() DOMString + * **mimeType**: è¦ä¸Šè¼‰çš„資料的 mime 類型。é è¨­è¨­ç½®ç‚º `image/jpeg`。() DOMString + * **params**: 一組è¦åœ¨ HTTP è¦æ±‚中傳éžçš„å¯é¸çš„éµå€¼å°ã€‚(物件) + * **chunkedMode**: 是å¦è¦åˆ†å¡Šçš„æµå¼è™•ç†æ¨¡å¼ä¸­çš„資料上載。é è¨­å€¼ç‚º `true`。(布林值) + * **headers**: 地圖的標頭å稱/標頭值。使用陣列來指定多個值。(物件) + +* **trustAllHosts**: å¯é¸åƒæ•¸ï¼Œé è¨­å€¼ç‚º `false` 。 如果設置為 `true` ,它接å—的所有安全證書。 這是有用的因為 android 系統拒絕自簽å的安全證書。 ä¸å»ºè­°ä¾›ç”Ÿç”¢ä½¿ç”¨ã€‚ æ”¯æ´ Android å’Œ iOS。 *(布林值)* + +### 示例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function (r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + var fail = function (error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey = "file"; + options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1); + options.mimeType = "text/plain"; + + var params = {}; + params.value1 = "test"; + params.value2 = "param"; + + options.params = params; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + + +### 與上傳的標頭和進度事件 (Android å’Œ iOS åªï¼‰ 的示例 + + function win(r) { + console.log("Code = " + r.responseCode); + console.log("Response = " + r.response); + console.log("Sent = " + r.bytesSent); + } + + function fail(error) { + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var uri = encodeURI("http://some.server.com/upload.php"); + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1); + options.mimeType="text/plain"; + + var headers={'headerParam':'headerValue'}; + + options.headers = headers; + + var ft = new FileTransfer(); + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total); + } else { + loadingStatus.increment(); + } + }; + ft.upload(fileURL, uri, win, fail, options); + + +## FileUploadResult + +`FileUploadResult` 物件將傳éžçµ¦è©² `檔案傳輸` 物件的 `upload()` 方法的æˆåŠŸå›žæª”ã€‚ + +### 屬性 + +* **bytesSent**: 作為上載的一部分發é€åˆ°ä¼ºæœå™¨çš„ä½å…ƒçµ„數。(é•·) + +* **responseCode**: 由伺æœå™¨è¿”回的 HTTP 回應代碼。(é•·) + +* **response**: 由伺æœå™¨è¿”回的 HTTP 回應。() DOMString + +* **headers**: 由伺æœå™¨çš„ HTTP 回應標頭。(物件) + + * ç›®å‰æ”¯æ´çš„ iOS åªã€‚ + +### iOS 的怪癖 + +* 䏿”¯æ´ `responseCode` 或`bytesSent`. + +## download + +**åƒæ•¸**: + +* **source**: è¦ä¸‹è¼‰çš„æª”,如由編碼的伺æœå™¨çš„ URL`encodeURI()`. + +* **target**: 表示檔在設備上的檔案系統 url。 為å‘後相容性,這也å¯ä»¥å°‡è¨­å‚™ä¸Šçš„æª”的完整路徑。 (請åƒè¦‹ [å‘後相容性注æ„到] 下é¢) + +* **successCallback**: 傳éžä¸€å€‹å›žæª” `FileEntry` 物件。*(函數)* + +* **errorCallback**: 如果檢索 `FileEntry` 時發生錯誤,則執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)* + +* **trustAllHosts**: å¯é¸åƒæ•¸ï¼Œé è¨­å€¼ç‚º `false` 。 如果設置為 `true` ,它å¯ä»¥æŽ¥å—的所有安全證書。 這是有用的因為 Android 拒絕自行簽署å¼å®‰å…¨è­‰æ›¸ã€‚ ä¸å»ºè­°ä¾›ç”Ÿç”¢ä½¿ç”¨ã€‚ 在 Android å’Œ iOS ä¸Šå—æ”¯æ´ã€‚ *(布林值)* + +* **options**: å¯é¸åƒæ•¸ï¼Œç›®å‰åªæ”¯æ´æ¨™é¡Œ (如授權 (基本驗證) 等)。 + +### 示例 + + // !! Assumes variable fileURL contains a valid URL to a path on the device, + // for example, cdvfile://localhost/persistent/path/to/downloads/ + + var fileTransfer = new FileTransfer(); + var uri = encodeURI("http://some.server.com/download.php"); + + fileTransfer.download( + uri, + fileURL, + function(entry) { + console.log("download complete: " + entry.toURL()); + }, + function(error) { + console.log("download error source " + error.source); + console.log("download error target " + error.target); + console.log("upload error code" + error.code); + }, + false, + { + headers: { + "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA==" + } + } + ); + + +## abort + +中止正在進行轉讓。Onerror 回檔傳éžä¸€å€‹ FileTransferError 物件具有 FileTransferError.ABORT_ERR 錯誤代碼。 + +### 示例 + + // !! Assumes variable fileURL contains a valid URL to a text file on the device, + // for example, cdvfile://localhost/persistent/path/to/file.txt + + var win = function(r) { + console.log("Should not be called."); + } + + var fail = function(error) { + // error.code == FileTransferError.ABORT_ERR + alert("An error has occurred: Code = " + error.code); + console.log("upload error source " + error.source); + console.log("upload error target " + error.target); + } + + var options = new FileUploadOptions(); + options.fileKey="file"; + options.fileName="myphoto.jpg"; + options.mimeType="image/jpeg"; + + var ft = new FileTransfer(); + ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options); + ft.abort(); + + +## FileTransferError + +當發生錯誤時,`FileTransferError` 物件將傳éžçµ¦éŒ¯èª¤å›žæª”。 + +### 屬性 + +* **code**: 下é¢åˆ—出的é å®šç¾©çš„錯誤代碼之一。(人數) + +* **source**: æºçš„ URL。(字串) + +* **target**: 到目標 URL。(字串) + +* **HTTP_status**: HTTP 狀態碼。從 HTTP 連接收到一個回應代碼時,此屬性æ‰å¯ç”¨ã€‚(人數) + +* **body**回應正文。此屬性åªèƒ½æ˜¯å¯ç”¨çš„當該 HTTP 連接收到答覆。(字串) + +* **exception**: è¦éº¼ e.getMessage 或 e.toString (字串) + +### å¸¸é‡ + +* 1 = `FileTransferError.FILE_NOT_FOUND_ERR` +* 2 = `FileTransferError.INVALID_URL_ERR` +* 3 = `FileTransferError.CONNECTION_ERR` +* 4 = `FileTransferError.ABORT_ERR` +* 5 = `FileTransferError.NOT_MODIFIED_ERR` + +## å‘後相容性注æ„到 + +以å‰ç‰ˆæœ¬çš„é€™å€‹å¤–æŽ›ç¨‹å¼æ‰æœƒæŽ¥å—設備-çµ•å°æª”路徑作為æºå°æ–¼ä¸Šè¼‰ï¼Œæˆ–用於下載的目標。這些路徑通常會在表單 + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +為å‘å¾Œç›¸å®¹æ€§ï¼Œé€™äº›è·¯å¾‘ä»æœƒè¢«æŽ¥å—,和如果您的應用程å¼å·²éŒ„å¾—åƒé€™äº›åœ¨æŒä¹…性存儲的路徑,然後他們å¯ä»¥ç¹¼çºŒä½¿ç”¨ã€‚ + +é€™äº›è·¯å¾‘è¢«ä»¥å‰æš´éœ²åœ¨ `FileEntry` 和由檔外掛程å¼è¿”回的 `DirectoryEntry` 物件的 `fullPath` 屬性中。 新版本的檔的外掛程å¼ï¼Œä½†æ˜¯ï¼Œä¸å†å…¬é–‹é€™äº› JavaScript 的路徑。 + +如果您è¦å‡ç´šåˆ°æ–° (1.0.0 或更高版本) 版本的檔,和你以å‰ä¸€ç›´åœ¨ä½¿ç”¨ `entry.fullPath` ä½œç‚ºåƒæ•¸åˆ° `download()` 或 `upload()`ï¼Œé‚£éº¼ä½ å°‡éœ€è¦æ›´æ”¹ä»£ç¢¼ä»¥ä½¿ç”¨æª”案系統的 Url 來代替。 + +`FileEntry.toURL()` å’Œ `DirectoryEntry.toURL()` 返回的表單檔案 URL + + cdvfile://localhost/persistent/path/to/file + + +它å¯ä»¥ç”¨åœ¨ `download()` å’Œ `upload()` å…©ç¨®æ–¹æ³•ä¸­çš„çµ•å°æª”路徑ä½ç½®ã€‚ diff --git a/plugins/cordova-plugin-file-transfer/package.json b/plugins/cordova-plugin-file-transfer/package.json new file mode 100644 index 0000000..82000be --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/package.json @@ -0,0 +1,56 @@ +{ + "name": "cordova-plugin-file-transfer", + "version": "1.5.1", + "description": "Cordova File Transfer Plugin", + "cordova": { + "id": "cordova-plugin-file-transfer", + "platforms": [ + "android", + "amazon-fireos", + "ubuntu", + "blackberry10", + "ios", + "wp7", + "wp8", + "windows8", + "windows", + "firefoxos", + "browser" + ] + }, + "scripts": { + "test": "npm run lint && npm run style", + "lint": "jshint www && jshint src && jshint tests", + "style": "jscs tests/tests.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-file-transfer" + }, + "keywords": [ + "cordova", + "file", + "transfer", + "ecosystem:cordova", + "cordova-android", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-blackberry10", + "cordova-ios", + "cordova-wp7", + "cordova-wp8", + "cordova-windows8", + "cordova-windows", + "cordova-firefoxos", + "cordova-browser" + ], + "peerDependencies": { + "cordova-plugin-file": "^3.0.0" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "devDependencies": { + "jscs": "^2.6.0", + "jshint": "^2.8.0" + } +} diff --git a/plugins/cordova-plugin-file-transfer/plugin.xml b/plugins/cordova-plugin-file-transfer/plugin.xml new file mode 100644 index 0000000..a855288 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/plugin.xml @@ -0,0 +1,170 @@ + + + + + File Transfer + Cordova File Transfer Plugin + Apache 2.0 + cordova,file,transfer + https://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer.git + https://issues.apache.org/jira/browse/CB/component/12320650 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-file-transfer/src/amazon/FileTransfer.java b/plugins/cordova-plugin-file-transfer/src/amazon/FileTransfer.java new file mode 100644 index 0000000..1563a39 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/amazon/FileTransfer.java @@ -0,0 +1,898 @@ +/* + 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.filetransfer; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URLConnection; +import java.net.URLDecoder; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Iterator; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.cordova.Config; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.CordovaResourceApi.OpenForReadResult; +import org.apache.cordova.PluginResult; +import org.apache.cordova.file.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.net.Uri; +import android.os.Build; +import android.util.Log; +import com.amazon.android.webkit.AmazonCookieManager; + +public class FileTransfer extends CordovaPlugin { + + private static final String LOG_TAG = "FileTransfer"; + private static final String LINE_START = "--"; + private static final String LINE_END = "\r\n"; + private static final String BOUNDARY = "+++++"; + + public static int FILE_NOT_FOUND_ERR = 1; + public static int INVALID_URL_ERR = 2; + public static int CONNECTION_ERR = 3; + public static int ABORTED_ERR = 4; + + private static HashMap activeRequests = new HashMap(); + private static final int MAX_BUFFER_SIZE = 16 * 1024; + + private static final class RequestContext { + String source; + String target; + File targetFile; + CallbackContext callbackContext; + InputStream currentInputStream; + OutputStream currentOutputStream; + boolean aborted; + RequestContext(String source, String target, CallbackContext callbackContext) { + this.source = source; + this.target = target; + this.callbackContext = callbackContext; + } + void sendPluginResult(PluginResult pluginResult) { + synchronized (this) { + if (!aborted) { + callbackContext.sendPluginResult(pluginResult); + } + } + } + } + + /** + * Adds an interface method to an InputStream to return the number of bytes + * read from the raw stream. This is used to track total progress against + * the HTTP Content-Length header value from the server. + */ + private static abstract class TrackingInputStream extends FilterInputStream { + public TrackingInputStream(final InputStream in) { + super(in); + } + public abstract long getTotalRawBytesRead(); + } + + private static class ExposedGZIPInputStream extends GZIPInputStream { + public ExposedGZIPInputStream(final InputStream in) throws IOException { + super(in); + } + public Inflater getInflater() { + return inf; + } + } + + /** + * Provides raw bytes-read tracking for a GZIP input stream. Reports the + * total number of compressed bytes read from the input, rather than the + * number of uncompressed bytes. + */ + private static class TrackingGZIPInputStream extends TrackingInputStream { + private ExposedGZIPInputStream gzin; + public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException { + super(gzin); + this.gzin = gzin; + } + public long getTotalRawBytesRead() { + return gzin.getInflater().getBytesRead(); + } + } + + /** + * Provides simple total-bytes-read tracking for an existing InputStream + */ + private static class SimpleTrackingInputStream extends TrackingInputStream { + private long bytesRead = 0; + public SimpleTrackingInputStream(InputStream stream) { + super(stream); + } + + private int updateBytesRead(int newBytesRead) { + if (newBytesRead != -1) { + bytesRead += newBytesRead; + } + return newBytesRead; + } + + @Override + public int read() throws IOException { + return updateBytesRead(super.read()); + } + + // Note: FilterInputStream delegates read(byte[] bytes) to the below method, + // so we don't override it or else double count (CB-5631). + @Override + public int read(byte[] bytes, int offset, int count) throws IOException { + return updateBytesRead(super.read(bytes, offset, count)); + } + + public long getTotalRawBytesRead() { + return bytesRead; + } + } + + @Override + public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { + if (action.equals("upload") || action.equals("download")) { + String source = args.getString(0); + String target = args.getString(1); + + if (action.equals("upload")) { + try { + source = URLDecoder.decode(source, "UTF-8"); + upload(source, target, args, callbackContext); + } catch (UnsupportedEncodingException e) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION, "UTF-8 error.")); + } + } else { + download(source, target, args, callbackContext); + } + return true; + } else if (action.equals("abort")) { + String objectId = args.getString(0); + abort(objectId); + callbackContext.success(); + return true; + } + return false; + } + + private static void addHeadersToRequest(URLConnection connection, JSONObject headers) { + try { + for (Iterator iter = headers.keys(); iter.hasNext(); ) { + String headerKey = iter.next().toString(); + JSONArray headerValues = headers.optJSONArray(headerKey); + if (headerValues == null) { + headerValues = new JSONArray(); + headerValues.put(headers.getString(headerKey)); + } + connection.setRequestProperty(headerKey, headerValues.getString(0)); + for (int i = 1; i < headerValues.length(); ++i) { + connection.addRequestProperty(headerKey, headerValues.getString(i)); + } + } + } catch (JSONException e1) { + // No headers to be manipulated! + } + } + + /** + * Uploads the specified file to the server URL provided using an HTTP multipart request. + * @param source Full path of the file on the file system + * @param target URL of the server to receive the file + * @param args JSON Array of args + * @param callbackContext callback id for optional progress reports + * + * args[2] fileKey Name of file request parameter + * args[3] fileName File name to be used on server + * args[4] mimeType Describes file content type + * args[5] params key:value pairs of user-defined parameters + * @return FileUploadResult containing result of upload request + */ + private void upload(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException { + Log.d(LOG_TAG, "upload " + source + " to " + target); + + // Setup the options + final String fileKey = getArgument(args, 2, "file"); + final String fileName = getArgument(args, 3, "image.jpg"); + final String mimeType = getArgument(args, 4, "image/jpeg"); + final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5); + final boolean trustEveryone = args.optBoolean(6); + // Always use chunked mode unless set to false as per API + final boolean chunkedMode = args.optBoolean(7) || args.isNull(7); + // Look for headers on the params map for backwards compatibility with older Cordova versions. + final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8); + final String objectId = args.getString(9); + final String httpMethod = getArgument(args, 10, "POST"); + + final CordovaResourceApi resourceApi = webView.getResourceApi(); + + Log.d(LOG_TAG, "fileKey: " + fileKey); + Log.d(LOG_TAG, "fileName: " + fileName); + Log.d(LOG_TAG, "mimeType: " + mimeType); + Log.d(LOG_TAG, "params: " + params); + Log.d(LOG_TAG, "trustEveryone: " + trustEveryone); + Log.d(LOG_TAG, "chunkedMode: " + chunkedMode); + Log.d(LOG_TAG, "headers: " + headers); + Log.d(LOG_TAG, "objectId: " + objectId); + Log.d(LOG_TAG, "httpMethod: " + httpMethod); + + final Uri targetUri = resourceApi.remapUri(Uri.parse(target)); + // Accept a path or a URI for the source. + Uri tmpSrc = Uri.parse(source); + final Uri sourceUri = resourceApi.remapUri( + tmpSrc.getScheme() != null ? tmpSrc : Uri.fromFile(new File(source))); + + int uriType = CordovaResourceApi.getUriType(targetUri); + final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS; + if (uriType != CordovaResourceApi.URI_TYPE_HTTP && !useHttps) { + JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0); + Log.e(LOG_TAG, "Unsupported URI: " + targetUri); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + return; + } + + final RequestContext context = new RequestContext(source, target, callbackContext); + synchronized (activeRequests) { + activeRequests.put(objectId, context); + } + + cordova.getThreadPool().execute(new Runnable() { + public void run() { + if (context.aborted) { + return; + } + HttpURLConnection conn = null; + HostnameVerifier oldHostnameVerifier = null; + SSLSocketFactory oldSocketFactory = null; + int totalBytes = 0; + int fixedLength = -1; + try { + // Create return object + FileUploadResult result = new FileUploadResult(); + FileProgressResult progress = new FileProgressResult(); + + //------------------ CLIENT REQUEST + // Open a HTTP connection to the URL based on protocol + conn = resourceApi.createHttpConnection(targetUri); + if (useHttps && trustEveryone) { + // Setup the HTTPS connection class to trust everyone + HttpsURLConnection https = (HttpsURLConnection)conn; + oldSocketFactory = trustAllHosts(https); + // Save the current hostnameVerifier + oldHostnameVerifier = https.getHostnameVerifier(); + // Setup the connection not to verify hostnames + https.setHostnameVerifier(DO_NOT_VERIFY); + } + + // Allow Inputs + conn.setDoInput(true); + + // Allow Outputs + conn.setDoOutput(true); + + // Don't use a cached copy. + conn.setUseCaches(false); + + // Use a post method. + conn.setRequestMethod(httpMethod); + + // if we specified a Content-Type header, don't do multipart form upload + boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type"); + if (multipartFormUpload) { + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + } + // Set the cookies on the response + String cookie = AmazonCookieManager.getInstance().getCookie(target); + if (cookie != null) { + conn.setRequestProperty("Cookie", cookie); + } + + // Handle the other headers + if (headers != null) { + addHeadersToRequest(conn, headers); + } + + /* + * Store the non-file portions of the multipart data as a string, so that we can add it + * to the contentSize, since it is part of the body of the HTTP request. + */ + StringBuilder beforeData = new StringBuilder(); + try { + for (Iterator iter = params.keys(); iter.hasNext();) { + Object key = iter.next(); + if(!String.valueOf(key).equals("headers")) + { + beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END); + beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"'); + beforeData.append(LINE_END).append(LINE_END); + beforeData.append(params.getString(key.toString())); + beforeData.append(LINE_END); + } + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + + beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END); + beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";"); + beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END); + beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END); + byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8"); + byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8"); + + + // Get a input stream of the file on the phone + OpenForReadResult readResult = resourceApi.openForRead(sourceUri); + + int stringLength = beforeDataBytes.length + tailParamsBytes.length; + if (readResult.length >= 0) { + fixedLength = (int)readResult.length; + if (multipartFormUpload) + fixedLength += stringLength; + progress.setLengthComputable(true); + progress.setTotal(fixedLength); + } + Log.d(LOG_TAG, "Content Length: " + fixedLength); + // setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices. + // http://code.google.com/p/android/issues/detail?id=3164 + // It also causes OOM if HTTPS is used, even on newer devices. + boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps); + useChunkedMode = useChunkedMode || (fixedLength == -1); + + if (useChunkedMode) { + conn.setChunkedStreamingMode(MAX_BUFFER_SIZE); + // Although setChunkedStreamingMode sets this header, setting it explicitly here works + // around an OutOfMemoryException when using https. + conn.setRequestProperty("Transfer-Encoding", "chunked"); + } else { + conn.setFixedLengthStreamingMode(fixedLength); + } + + conn.connect(); + + OutputStream sendStream = null; + try { + sendStream = conn.getOutputStream(); + synchronized (context) { + if (context.aborted) { + return; + } + context.currentOutputStream = sendStream; + } + + if (multipartFormUpload) { + //We don't want to change encoding, we just want this to write for all Unicode. + sendStream.write(beforeDataBytes); + totalBytes += beforeDataBytes.length; + } + // create a buffer of maximum size + int bytesAvailable = readResult.inputStream.available(); + int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE); + byte[] buffer = new byte[bufferSize]; + + // read file and write it into form... + int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize); + + long prevBytesRead = 0; + while (bytesRead > 0) { + result.setBytesSent(totalBytes); + sendStream.write(buffer, 0, bytesRead); + totalBytes += bytesRead; + if (totalBytes > prevBytesRead + 102400) { + prevBytesRead = totalBytes; + Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes"); + } + bytesAvailable = readResult.inputStream.available(); + bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE); + bytesRead = readResult.inputStream.read(buffer, 0, bufferSize); + + // Send a progress event. + progress.setLoaded(totalBytes); + PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject()); + progressResult.setKeepCallback(true); + context.sendPluginResult(progressResult); + } + + if (multipartFormUpload) { + // send multipart form data necessary after file data... + sendStream.write(tailParamsBytes); + totalBytes += tailParamsBytes.length; + } + sendStream.flush(); + } finally { + safeClose(readResult.inputStream); + safeClose(sendStream); + } + context.currentOutputStream = null; + Log.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength); + + //------------------ read the SERVER RESPONSE + String responseString; + int responseCode = conn.getResponseCode(); + Log.d(LOG_TAG, "response code: " + responseCode); + Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields()); + TrackingInputStream inStream = null; + try { + inStream = getInputStream(conn); + synchronized (context) { + if (context.aborted) { + return; + } + context.currentInputStream = inStream; + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, conn.getContentLength())); + byte[] buffer = new byte[1024]; + int bytesRead = 0; + // write bytes to file + while ((bytesRead = inStream.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + } + responseString = out.toString("UTF-8"); + } finally { + context.currentInputStream = null; + safeClose(inStream); + } + + Log.d(LOG_TAG, "got response from server"); + Log.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length()))); + + // send request and retrieve response + result.setResponseCode(responseCode); + result.setResponse(responseString); + + context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject())); + } catch (FileNotFoundException e) { + JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn); + Log.e(LOG_TAG, error.toString(), e); + context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + } catch (IOException e) { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn); + Log.e(LOG_TAG, error.toString(), e); + Log.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength + " bytes."); + context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + } catch (Throwable t) { + // Shouldn't happen, but will + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn); + Log.e(LOG_TAG, error.toString(), t); + context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + } finally { + synchronized (activeRequests) { + activeRequests.remove(objectId); + } + + if (conn != null) { + // Revert back to the proper verifier and socket factories + // Revert back to the proper verifier and socket factories + if (trustEveryone && useHttps) { + HttpsURLConnection https = (HttpsURLConnection) conn; + https.setHostnameVerifier(oldHostnameVerifier); + https.setSSLSocketFactory(oldSocketFactory); + } + } + } + } + }); + } + + private static void safeClose(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + + private static TrackingInputStream getInputStream(URLConnection conn) throws IOException { + String encoding = conn.getContentEncoding(); + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream())); + } + return new SimpleTrackingInputStream(conn.getInputStream()); + } + + // always verify the host - don't check for certificate + private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + // Create a trust manager that does not validate certificate chains + private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + } }; + + /** + * This function will install a trust manager that will blindly trust all SSL + * certificates. The reason this code is being added is to enable developers + * to do development using self signed SSL certificates on their web server. + * + * The standard HttpsURLConnection class will throw an exception on self + * signed certificates if this code is not run. + */ + private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) { + // Install the all-trusting trust manager + SSLSocketFactory oldFactory = connection.getSSLSocketFactory(); + try { + // Install our all trusting manager + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + SSLSocketFactory newFactory = sc.getSocketFactory(); + connection.setSSLSocketFactory(newFactory); + } catch (Exception e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return oldFactory; + } + + private static JSONObject createFileTransferError(int errorCode, String source, String target, URLConnection connection) { + + int httpStatus = 0; + StringBuilder bodyBuilder = new StringBuilder(); + String body = null; + if (connection != null) { + try { + if (connection instanceof HttpURLConnection) { + httpStatus = ((HttpURLConnection)connection).getResponseCode(); + InputStream err = ((HttpURLConnection) connection).getErrorStream(); + if(err != null) + { + BufferedReader reader = new BufferedReader(new InputStreamReader(err, "UTF-8")); + try { + String line = reader.readLine(); + while(line != null) { + bodyBuilder.append(line); + line = reader.readLine(); + if(line != null) { + bodyBuilder.append('\n'); + } + } + body = bodyBuilder.toString(); + } finally { + reader.close(); + } + } + } + // IOException can leave connection object in a bad state, so catch all exceptions. + } catch (Throwable e) { + Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e); + } + } + + return createFileTransferError(errorCode, source, target, body, httpStatus); + } + + /** + * Create an error object based on the passed in errorCode + * @param errorCode the error + * @return JSONObject containing the error + */ + private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus) { + JSONObject error = null; + try { + error = new JSONObject(); + error.put("code", errorCode); + error.put("source", source); + error.put("target", target); + if(body != null) + { + error.put("body", body); + } + if (httpStatus != null) { + error.put("http_status", httpStatus); + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return error; + } + + /** + * Convenience method to read a parameter from the list of JSON args. + * @param args the args passed to the Plugin + * @param position the position to retrieve the arg from + * @param defaultString the default to be used if the arg does not exist + * @return String with the retrieved value + */ + private static String getArgument(JSONArray args, int position, String defaultString) { + String arg = defaultString; + if (args.length() > position) { + arg = args.optString(position); + if (arg == null || "null".equals(arg)) { + arg = defaultString; + } + } + return arg; + } + + /** + * Downloads a file form a given URL and saves it to the specified directory. + * + * @param source URL of the server to receive the file + * @param target Full path of the file on the file system + */ + private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException { + Log.d(LOG_TAG, "download " + source + " to " + target); + + final CordovaResourceApi resourceApi = webView.getResourceApi(); + + final boolean trustEveryone = args.optBoolean(2); + final String objectId = args.getString(3); + final JSONObject headers = args.optJSONObject(4); + + final Uri sourceUri = resourceApi.remapUri(Uri.parse(source)); + // Accept a path or a URI for the source. + Uri tmpTarget = Uri.parse(target); + final Uri targetUri = resourceApi.remapUri( + tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target))); + + int uriType = CordovaResourceApi.getUriType(sourceUri); + final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS; + final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP; + if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) { + JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0); + Log.e(LOG_TAG, "Unsupported URI: " + targetUri); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + return; + } + + // TODO: refactor to also allow resources & content: + if (!isLocalTransfer && !Config.isUrlWhiteListed(source)) { + Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'"); + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + return; + } + + + final RequestContext context = new RequestContext(source, target, callbackContext); + synchronized (activeRequests) { + activeRequests.put(objectId, context); + } + + cordova.getThreadPool().execute(new Runnable() { + public void run() { + if (context.aborted) { + return; + } + HttpURLConnection connection = null; + HostnameVerifier oldHostnameVerifier = null; + SSLSocketFactory oldSocketFactory = null; + File file = null; + PluginResult result = null; + TrackingInputStream inputStream = null; + + OutputStream outputStream = null; + try { + OpenForReadResult readResult = null; + outputStream = resourceApi.openOutputStream(targetUri); + + file = resourceApi.mapUriToFile(targetUri); + context.targetFile = file; + + Log.d(LOG_TAG, "Download file:" + sourceUri); + + FileProgressResult progress = new FileProgressResult(); + + if (isLocalTransfer) { + readResult = resourceApi.openForRead(sourceUri); + if (readResult.length != -1) { + progress.setLengthComputable(true); + progress.setTotal(readResult.length); + } + inputStream = new SimpleTrackingInputStream(readResult.inputStream); + } else { + // connect to server + // Open a HTTP connection to the URL based on protocol + connection = resourceApi.createHttpConnection(sourceUri); + if (useHttps && trustEveryone) { + // Setup the HTTPS connection class to trust everyone + HttpsURLConnection https = (HttpsURLConnection)connection; + oldSocketFactory = trustAllHosts(https); + // Save the current hostnameVerifier + oldHostnameVerifier = https.getHostnameVerifier(); + // Setup the connection not to verify hostnames + https.setHostnameVerifier(DO_NOT_VERIFY); + } + + connection.setRequestMethod("GET"); + + // TODO: Make OkHttp use this AmazonCookieManager by default. + String cookie = AmazonCookieManager.getInstance().getCookie(sourceUri.toString()); + if(cookie != null) + { + connection.setRequestProperty("cookie", cookie); + } + + // This must be explicitly set for gzip progress tracking to work. + connection.setRequestProperty("Accept-Encoding", "gzip"); + + // Handle the other headers + if (headers != null) { + addHeadersToRequest(connection, headers); + } + + connection.connect(); + + if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) { + // Only trust content-length header if we understand + // the encoding -- identity or gzip + if (connection.getContentLength() != -1) { + progress.setLengthComputable(true); + progress.setTotal(connection.getContentLength()); + } + } + inputStream = getInputStream(connection); + } + + try { + synchronized (context) { + if (context.aborted) { + return; + } + context.currentInputStream = inputStream; + } + + // write bytes to file + byte[] buffer = new byte[MAX_BUFFER_SIZE]; + int bytesRead = 0; + while ((bytesRead = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, bytesRead); + // Send a progress event. + progress.setLoaded(inputStream.getTotalRawBytesRead()); + PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject()); + progressResult.setKeepCallback(true); + context.sendPluginResult(progressResult); + } + } finally { + context.currentInputStream = null; + safeClose(inputStream); + safeClose(outputStream); + } + + Log.d(LOG_TAG, "Saved file: " + target); + + // create FileEntry object + FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + if (filePlugin != null) { + JSONObject fileEntry = filePlugin.getEntryForFile(file); + if (fileEntry != null) { + result = new PluginResult(PluginResult.Status.OK, fileEntry); + } else { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection); + Log.e(LOG_TAG, "File plugin cannot represent download path"); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } + } else { + Log.e(LOG_TAG, "File plugin not found; cannot save downloaded file"); + result = new PluginResult(PluginResult.Status.ERROR, "File plugin not found; cannot save downloaded file"); + } + + } catch (FileNotFoundException e) { + JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection); + Log.e(LOG_TAG, error.toString(), e); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } catch (IOException e) { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection); + Log.e(LOG_TAG, error.toString(), e); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + result = new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } catch (Throwable e) { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection); + Log.e(LOG_TAG, error.toString(), e); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } finally { + safeClose(outputStream); + synchronized (activeRequests) { + activeRequests.remove(objectId); + } + + if (connection != null) { + // Revert back to the proper verifier and socket factories + if (trustEveryone && useHttps) { + HttpsURLConnection https = (HttpsURLConnection) connection; + https.setHostnameVerifier(oldHostnameVerifier); + https.setSSLSocketFactory(oldSocketFactory); + } + } + + if (result == null) { + result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, target, connection)); + } + // Remove incomplete download. + if (result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) { + file.delete(); + } + context.sendPluginResult(result); + } + } + }); + } + + /** + * Abort an ongoing upload or download. + */ + private void abort(String objectId) { + final RequestContext context; + synchronized (activeRequests) { + context = activeRequests.remove(objectId); + } + if (context != null) { + File file = context.targetFile; + if (file != null) { + file.delete(); + } + // Trigger the abort callback immediately to minimize latency between it and abort() being called. + JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, null, -1); + synchronized (context) { + context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error)); + context.aborted = true; + } + // Closing the streams can block, so execute on a background thread. + cordova.getThreadPool().execute(new Runnable() { + public void run() { + synchronized (context) { + safeClose(context.currentInputStream); + safeClose(context.currentOutputStream); + } + } + }); + } + } +} diff --git a/plugins/cordova-plugin-file-transfer/src/android/FileProgressResult.java b/plugins/cordova-plugin-file-transfer/src/android/FileProgressResult.java new file mode 100644 index 0000000..76a7b13 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/android/FileProgressResult.java @@ -0,0 +1,63 @@ +/* + 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.filetransfer; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Encapsulates in-progress status of uploading or downloading a file to a remote server. + */ +public class FileProgressResult { + + private boolean lengthComputable = false; // declares whether total is known + private long loaded = 0; // bytes sent so far + private long total = 0; // bytes total, if known + + public boolean getLengthComputable() { + return lengthComputable; + } + + public void setLengthComputable(boolean computable) { + this.lengthComputable = computable; + } + + public long getLoaded() { + return loaded; + } + + public void setLoaded(long bytes) { + this.loaded = bytes; + } + + public long getTotal() { + return total; + } + + public void setTotal(long bytes) { + this.total = bytes; + } + + public JSONObject toJSONObject() throws JSONException { + return new JSONObject( + "{loaded:" + loaded + + ",total:" + total + + ",lengthComputable:" + (lengthComputable ? "true" : "false") + "}"); + } +} diff --git a/plugins/cordova-plugin-file-transfer/src/android/FileTransfer.java b/plugins/cordova-plugin-file-transfer/src/android/FileTransfer.java new file mode 100644 index 0000000..b9b99dc --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/android/FileTransfer.java @@ -0,0 +1,1024 @@ +/* + 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.filetransfer; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.URLConnection; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Iterator; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.cordova.Config; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.CordovaResourceApi.OpenForReadResult; +import org.apache.cordova.PluginManager; +import org.apache.cordova.PluginResult; +import org.apache.cordova.Whitelist; +import org.apache.cordova.file.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.net.Uri; +import android.os.Build; +import android.util.Log; +import android.webkit.CookieManager; + +public class FileTransfer extends CordovaPlugin { + + private static final String LOG_TAG = "FileTransfer"; + private static final String LINE_START = "--"; + private static final String LINE_END = "\r\n"; + private static final String BOUNDARY = "+++++"; + + public static int FILE_NOT_FOUND_ERR = 1; + public static int INVALID_URL_ERR = 2; + public static int CONNECTION_ERR = 3; + public static int ABORTED_ERR = 4; + public static int NOT_MODIFIED_ERR = 5; + + private static HashMap activeRequests = new HashMap(); + private static final int MAX_BUFFER_SIZE = 16 * 1024; + + private static final class RequestContext { + String source; + String target; + File targetFile; + CallbackContext callbackContext; + HttpURLConnection connection; + boolean aborted; + RequestContext(String source, String target, CallbackContext callbackContext) { + this.source = source; + this.target = target; + this.callbackContext = callbackContext; + } + void sendPluginResult(PluginResult pluginResult) { + synchronized (this) { + if (!aborted) { + callbackContext.sendPluginResult(pluginResult); + } + } + } + } + + /** + * Adds an interface method to an InputStream to return the number of bytes + * read from the raw stream. This is used to track total progress against + * the HTTP Content-Length header value from the server. + */ + private static abstract class TrackingInputStream extends FilterInputStream { + public TrackingInputStream(final InputStream in) { + super(in); + } + public abstract long getTotalRawBytesRead(); + } + + private static class ExposedGZIPInputStream extends GZIPInputStream { + public ExposedGZIPInputStream(final InputStream in) throws IOException { + super(in); + } + public Inflater getInflater() { + return inf; + } + } + + /** + * Provides raw bytes-read tracking for a GZIP input stream. Reports the + * total number of compressed bytes read from the input, rather than the + * number of uncompressed bytes. + */ + private static class TrackingGZIPInputStream extends TrackingInputStream { + private ExposedGZIPInputStream gzin; + public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException { + super(gzin); + this.gzin = gzin; + } + public long getTotalRawBytesRead() { + return gzin.getInflater().getBytesRead(); + } + } + + /** + * Provides simple total-bytes-read tracking for an existing InputStream + */ + private static class SimpleTrackingInputStream extends TrackingInputStream { + private long bytesRead = 0; + public SimpleTrackingInputStream(InputStream stream) { + super(stream); + } + + private int updateBytesRead(int newBytesRead) { + if (newBytesRead != -1) { + bytesRead += newBytesRead; + } + return newBytesRead; + } + + @Override + public int read() throws IOException { + return updateBytesRead(super.read()); + } + + // Note: FilterInputStream delegates read(byte[] bytes) to the below method, + // so we don't override it or else double count (CB-5631). + @Override + public int read(byte[] bytes, int offset, int count) throws IOException { + return updateBytesRead(super.read(bytes, offset, count)); + } + + public long getTotalRawBytesRead() { + return bytesRead; + } + } + + @Override + public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { + if (action.equals("upload") || action.equals("download")) { + String source = args.getString(0); + String target = args.getString(1); + + if (action.equals("upload")) { + upload(source, target, args, callbackContext); + } else { + download(source, target, args, callbackContext); + } + return true; + } else if (action.equals("abort")) { + String objectId = args.getString(0); + abort(objectId); + callbackContext.success(); + return true; + } + return false; + } + + private static void addHeadersToRequest(URLConnection connection, JSONObject headers) { + try { + for (Iterator iter = headers.keys(); iter.hasNext(); ) { + /* RFC 2616 says that non-ASCII characters and control + * characters are not allowed in header names or values. + * Additionally, spaces are not allowed in header names. + * RFC 2046 Quoted-printable encoding may be used to encode + * arbitrary characters, but we donon- not do that encoding here. + */ + String headerKey = iter.next().toString(); + String cleanHeaderKey = headerKey.replaceAll("\\n","") + .replaceAll("\\s+","") + .replaceAll(":", "") + .replaceAll("[^\\x20-\\x7E]+", ""); + + JSONArray headerValues = headers.optJSONArray(headerKey); + if (headerValues == null) { + headerValues = new JSONArray(); + + /* RFC 2616 also says that any amount of consecutive linear + * whitespace within a header value can be replaced with a + * single space character, without affecting the meaning of + * that value. + */ + + String headerValue = headers.getString(headerKey); + String finalValue = headerValue.replaceAll("\\s+", " ").replaceAll("\\n"," ").replaceAll("[^\\x20-\\x7E]+", " "); + headerValues.put(finalValue); + } + + //Use the clean header key, not the one that we passed in + connection.setRequestProperty(cleanHeaderKey, headerValues.getString(0)); + for (int i = 1; i < headerValues.length(); ++i) { + connection.addRequestProperty(headerKey, headerValues.getString(i)); + } + } + } catch (JSONException e1) { + // No headers to be manipulated! + } + } + + private String getCookies(final String target) { + boolean gotCookie = false; + String cookie = null; + Class webViewClass = webView.getClass(); + try { + Method gcmMethod = webViewClass.getMethod("getCookieManager"); + Class iccmClass = gcmMethod.getReturnType(); + Method gcMethod = iccmClass.getMethod("getCookie", String.class); + + cookie = (String)gcMethod.invoke( + iccmClass.cast( + gcmMethod.invoke(webView) + ), target); + + gotCookie = true; + } catch (NoSuchMethodException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } catch (ClassCastException e) { + } + + if (!gotCookie && CookieManager.getInstance() != null) { + cookie = CookieManager.getInstance().getCookie(target); + } + + return cookie; + } + + /** + * Uploads the specified file to the server URL provided using an HTTP multipart request. + * @param source Full path of the file on the file system + * @param target URL of the server to receive the file + * @param args JSON Array of args + * @param callbackContext callback id for optional progress reports + * + * args[2] fileKey Name of file request parameter + * args[3] fileName File name to be used on server + * args[4] mimeType Describes file content type + * args[5] params key:value pairs of user-defined parameters + * @return FileUploadResult containing result of upload request + */ + private void upload(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException { + Log.d(LOG_TAG, "upload " + source + " to " + target); + + // Setup the options + final String fileKey = getArgument(args, 2, "file"); + final String fileName = getArgument(args, 3, "image.jpg"); + final String mimeType = getArgument(args, 4, "image/jpeg"); + final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5); + final boolean trustEveryone = args.optBoolean(6); + // Always use chunked mode unless set to false as per API + final boolean chunkedMode = args.optBoolean(7) || args.isNull(7); + // Look for headers on the params map for backwards compatibility with older Cordova versions. + final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8); + final String objectId = args.getString(9); + final String httpMethod = getArgument(args, 10, "POST"); + + final CordovaResourceApi resourceApi = webView.getResourceApi(); + + Log.d(LOG_TAG, "fileKey: " + fileKey); + Log.d(LOG_TAG, "fileName: " + fileName); + Log.d(LOG_TAG, "mimeType: " + mimeType); + Log.d(LOG_TAG, "params: " + params); + Log.d(LOG_TAG, "trustEveryone: " + trustEveryone); + Log.d(LOG_TAG, "chunkedMode: " + chunkedMode); + Log.d(LOG_TAG, "headers: " + headers); + Log.d(LOG_TAG, "objectId: " + objectId); + Log.d(LOG_TAG, "httpMethod: " + httpMethod); + + final Uri targetUri = resourceApi.remapUri(Uri.parse(target)); + // Accept a path or a URI for the source. + Uri tmpSrc = Uri.parse(source); + final Uri sourceUri = resourceApi.remapUri( + tmpSrc.getScheme() != null ? tmpSrc : Uri.fromFile(new File(source))); + + int uriType = CordovaResourceApi.getUriType(targetUri); + final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS; + if (uriType != CordovaResourceApi.URI_TYPE_HTTP && !useHttps) { + JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0, null); + Log.e(LOG_TAG, "Unsupported URI: " + targetUri); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + return; + } + + final RequestContext context = new RequestContext(source, target, callbackContext); + synchronized (activeRequests) { + activeRequests.put(objectId, context); + } + + cordova.getThreadPool().execute(new Runnable() { + public void run() { + if (context.aborted) { + return; + } + HttpURLConnection conn = null; + HostnameVerifier oldHostnameVerifier = null; + SSLSocketFactory oldSocketFactory = null; + int totalBytes = 0; + int fixedLength = -1; + try { + // Create return object + FileUploadResult result = new FileUploadResult(); + FileProgressResult progress = new FileProgressResult(); + + //------------------ CLIENT REQUEST + // Open a HTTP connection to the URL based on protocol + conn = resourceApi.createHttpConnection(targetUri); + if (useHttps && trustEveryone) { + // Setup the HTTPS connection class to trust everyone + HttpsURLConnection https = (HttpsURLConnection)conn; + oldSocketFactory = trustAllHosts(https); + // Save the current hostnameVerifier + oldHostnameVerifier = https.getHostnameVerifier(); + // Setup the connection not to verify hostnames + https.setHostnameVerifier(DO_NOT_VERIFY); + } + + // Allow Inputs + conn.setDoInput(true); + + // Allow Outputs + conn.setDoOutput(true); + + // Don't use a cached copy. + conn.setUseCaches(false); + + // Use a post method. + conn.setRequestMethod(httpMethod); + + // if we specified a Content-Type header, don't do multipart form upload + boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type"); + if (multipartFormUpload) { + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + } + + // Set the cookies on the response + String cookie = getCookies(target); + + if (cookie != null) { + conn.setRequestProperty("Cookie", cookie); + } + + // Handle the other headers + if (headers != null) { + addHeadersToRequest(conn, headers); + } + + /* + * Store the non-file portions of the multipart data as a string, so that we can add it + * to the contentSize, since it is part of the body of the HTTP request. + */ + StringBuilder beforeData = new StringBuilder(); + try { + for (Iterator iter = params.keys(); iter.hasNext();) { + Object key = iter.next(); + if(!String.valueOf(key).equals("headers")) + { + beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END); + beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"'); + beforeData.append(LINE_END).append(LINE_END); + beforeData.append(params.getString(key.toString())); + beforeData.append(LINE_END); + } + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + + beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END); + beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";"); + beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END); + beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END); + byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8"); + byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8"); + + + // Get a input stream of the file on the phone + OpenForReadResult readResult = resourceApi.openForRead(sourceUri); + + int stringLength = beforeDataBytes.length + tailParamsBytes.length; + if (readResult.length >= 0) { + fixedLength = (int)readResult.length; + if (multipartFormUpload) + fixedLength += stringLength; + progress.setLengthComputable(true); + progress.setTotal(fixedLength); + } + Log.d(LOG_TAG, "Content Length: " + fixedLength); + // setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices. + // http://code.google.com/p/android/issues/detail?id=3164 + // It also causes OOM if HTTPS is used, even on newer devices. + boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps); + useChunkedMode = useChunkedMode || (fixedLength == -1); + + if (useChunkedMode) { + conn.setChunkedStreamingMode(MAX_BUFFER_SIZE); + // Although setChunkedStreamingMode sets this header, setting it explicitly here works + // around an OutOfMemoryException when using https. + conn.setRequestProperty("Transfer-Encoding", "chunked"); + } else { + conn.setFixedLengthStreamingMode(fixedLength); + } + + conn.connect(); + + OutputStream sendStream = null; + try { + sendStream = conn.getOutputStream(); + synchronized (context) { + if (context.aborted) { + return; + } + context.connection = conn; + } + + if (multipartFormUpload) { + //We don't want to change encoding, we just want this to write for all Unicode. + sendStream.write(beforeDataBytes); + totalBytes += beforeDataBytes.length; + } + + // create a buffer of maximum size + int bytesAvailable = readResult.inputStream.available(); + int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE); + byte[] buffer = new byte[bufferSize]; + + // read file and write it into form... + int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize); + + long prevBytesRead = 0; + while (bytesRead > 0) { + totalBytes += bytesRead; + result.setBytesSent(totalBytes); + sendStream.write(buffer, 0, bytesRead); + if (totalBytes > prevBytesRead + 102400) { + prevBytesRead = totalBytes; + Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes"); + } + bytesAvailable = readResult.inputStream.available(); + bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE); + bytesRead = readResult.inputStream.read(buffer, 0, bufferSize); + + // Send a progress event. + progress.setLoaded(totalBytes); + PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject()); + progressResult.setKeepCallback(true); + context.sendPluginResult(progressResult); + } + + if (multipartFormUpload) { + // send multipart form data necessary after file data... + sendStream.write(tailParamsBytes); + totalBytes += tailParamsBytes.length; + } + sendStream.flush(); + } finally { + safeClose(readResult.inputStream); + safeClose(sendStream); + } + synchronized (context) { + context.connection = null; + } + Log.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength); + + //------------------ read the SERVER RESPONSE + String responseString; + int responseCode = conn.getResponseCode(); + Log.d(LOG_TAG, "response code: " + responseCode); + Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields()); + TrackingInputStream inStream = null; + try { + inStream = getInputStream(conn); + synchronized (context) { + if (context.aborted) { + return; + } + context.connection = conn; + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, conn.getContentLength())); + byte[] buffer = new byte[1024]; + int bytesRead = 0; + // write bytes to file + while ((bytesRead = inStream.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + } + responseString = out.toString("UTF-8"); + } finally { + synchronized (context) { + context.connection = null; + } + safeClose(inStream); + } + + Log.d(LOG_TAG, "got response from server"); + Log.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length()))); + + // send request and retrieve response + result.setResponseCode(responseCode); + result.setResponse(responseString); + + context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject())); + } catch (FileNotFoundException e) { + JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn, e); + Log.e(LOG_TAG, error.toString(), e); + context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + } catch (IOException e) { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn, e); + Log.e(LOG_TAG, error.toString(), e); + Log.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength + " bytes."); + context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + } catch (Throwable t) { + // Shouldn't happen, but will + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn, t); + Log.e(LOG_TAG, error.toString(), t); + context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + } finally { + synchronized (activeRequests) { + activeRequests.remove(objectId); + } + + if (conn != null) { + // Revert back to the proper verifier and socket factories + // Revert back to the proper verifier and socket factories + if (trustEveryone && useHttps) { + HttpsURLConnection https = (HttpsURLConnection) conn; + https.setHostnameVerifier(oldHostnameVerifier); + https.setSSLSocketFactory(oldSocketFactory); + } + } + } + } + }); + } + + private static void safeClose(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + + private static TrackingInputStream getInputStream(URLConnection conn) throws IOException { + String encoding = conn.getContentEncoding(); + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream())); + } + return new SimpleTrackingInputStream(conn.getInputStream()); + } + + // always verify the host - don't check for certificate + private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + // Create a trust manager that does not validate certificate chains + private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + } }; + + /** + * This function will install a trust manager that will blindly trust all SSL + * certificates. The reason this code is being added is to enable developers + * to do development using self signed SSL certificates on their web server. + * + * The standard HttpsURLConnection class will throw an exception on self + * signed certificates if this code is not run. + */ + private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) { + // Install the all-trusting trust manager + SSLSocketFactory oldFactory = connection.getSSLSocketFactory(); + try { + // Install our all trusting manager + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + SSLSocketFactory newFactory = sc.getSocketFactory(); + connection.setSSLSocketFactory(newFactory); + } catch (Exception e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return oldFactory; + } + + private static JSONObject createFileTransferError(int errorCode, String source, String target, URLConnection connection, Throwable throwable) { + + int httpStatus = 0; + StringBuilder bodyBuilder = new StringBuilder(); + String body = null; + if (connection != null) { + try { + if (connection instanceof HttpURLConnection) { + httpStatus = ((HttpURLConnection)connection).getResponseCode(); + InputStream err = ((HttpURLConnection) connection).getErrorStream(); + if(err != null) + { + BufferedReader reader = new BufferedReader(new InputStreamReader(err, "UTF-8")); + try { + String line = reader.readLine(); + while(line != null) { + bodyBuilder.append(line); + line = reader.readLine(); + if(line != null) { + bodyBuilder.append('\n'); + } + } + body = bodyBuilder.toString(); + } finally { + reader.close(); + } + } + } + // IOException can leave connection object in a bad state, so catch all exceptions. + } catch (Throwable e) { + Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e); + } + } + + return createFileTransferError(errorCode, source, target, body, httpStatus, throwable); + } + + /** + * Create an error object based on the passed in errorCode + * @param errorCode the error + * @return JSONObject containing the error + */ + private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus, Throwable throwable) { + JSONObject error = null; + try { + error = new JSONObject(); + error.put("code", errorCode); + error.put("source", source); + error.put("target", target); + if(body != null) + { + error.put("body", body); + } + if (httpStatus != null) { + error.put("http_status", httpStatus); + } + if (throwable != null) { + String msg = throwable.getMessage(); + if (msg == null || "".equals(msg)) { + msg = throwable.toString(); + } + error.put("exception", msg); + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return error; + } + + /** + * Convenience method to read a parameter from the list of JSON args. + * @param args the args passed to the Plugin + * @param position the position to retrieve the arg from + * @param defaultString the default to be used if the arg does not exist + * @return String with the retrieved value + */ + private static String getArgument(JSONArray args, int position, String defaultString) { + String arg = defaultString; + if (args.length() > position) { + arg = args.optString(position); + if (arg == null || "null".equals(arg)) { + arg = defaultString; + } + } + return arg; + } + + /** + * Downloads a file form a given URL and saves it to the specified directory. + * + * @param source URL of the server to receive the file + * @param target Full path of the file on the file system + */ + private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException { + Log.d(LOG_TAG, "download " + source + " to " + target); + + final CordovaResourceApi resourceApi = webView.getResourceApi(); + + final boolean trustEveryone = args.optBoolean(2); + final String objectId = args.getString(3); + final JSONObject headers = args.optJSONObject(4); + + final Uri sourceUri = resourceApi.remapUri(Uri.parse(source)); + // Accept a path or a URI for the source. + Uri tmpTarget = Uri.parse(target); + final Uri targetUri = resourceApi.remapUri( + tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target))); + + int uriType = CordovaResourceApi.getUriType(sourceUri); + final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS; + final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP; + if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) { + JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0, null); + Log.e(LOG_TAG, "Unsupported URI: " + sourceUri); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + return; + } + + /* This code exists for compatibility between 3.x and 4.x versions of Cordova. + * Previously the CordovaWebView class had a method, getWhitelist, which would + * return a Whitelist object. Since the fixed whitelist is removed in Cordova 4.x, + * the correct call now is to shouldAllowRequest from the plugin manager. + */ + Boolean shouldAllowRequest = null; + if (isLocalTransfer) { + shouldAllowRequest = true; + } + if (shouldAllowRequest == null) { + try { + Method gwl = webView.getClass().getMethod("getWhitelist"); + Whitelist whitelist = (Whitelist)gwl.invoke(webView); + shouldAllowRequest = whitelist.isUrlWhiteListed(source); + } catch (NoSuchMethodException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + } + if (shouldAllowRequest == null) { + try { + Method gpm = webView.getClass().getMethod("getPluginManager"); + PluginManager pm = (PluginManager)gpm.invoke(webView); + Method san = pm.getClass().getMethod("shouldAllowRequest", String.class); + shouldAllowRequest = (Boolean)san.invoke(pm, source); + } catch (NoSuchMethodException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + } + + if (!Boolean.TRUE.equals(shouldAllowRequest)) { + Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'"); + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401, null); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); + return; + } + + + final RequestContext context = new RequestContext(source, target, callbackContext); + synchronized (activeRequests) { + activeRequests.put(objectId, context); + } + + cordova.getThreadPool().execute(new Runnable() { + public void run() { + if (context.aborted) { + return; + } + HttpURLConnection connection = null; + HostnameVerifier oldHostnameVerifier = null; + SSLSocketFactory oldSocketFactory = null; + File file = null; + PluginResult result = null; + TrackingInputStream inputStream = null; + boolean cached = false; + + OutputStream outputStream = null; + try { + OpenForReadResult readResult = null; + + file = resourceApi.mapUriToFile(targetUri); + context.targetFile = file; + + Log.d(LOG_TAG, "Download file:" + sourceUri); + + FileProgressResult progress = new FileProgressResult(); + + if (isLocalTransfer) { + readResult = resourceApi.openForRead(sourceUri); + if (readResult.length != -1) { + progress.setLengthComputable(true); + progress.setTotal(readResult.length); + } + inputStream = new SimpleTrackingInputStream(readResult.inputStream); + } else { + // connect to server + // Open a HTTP connection to the URL based on protocol + connection = resourceApi.createHttpConnection(sourceUri); + if (useHttps && trustEveryone) { + // Setup the HTTPS connection class to trust everyone + HttpsURLConnection https = (HttpsURLConnection)connection; + oldSocketFactory = trustAllHosts(https); + // Save the current hostnameVerifier + oldHostnameVerifier = https.getHostnameVerifier(); + // Setup the connection not to verify hostnames + https.setHostnameVerifier(DO_NOT_VERIFY); + } + + connection.setRequestMethod("GET"); + + // TODO: Make OkHttp use this CookieManager by default. + String cookie = getCookies(sourceUri.toString()); + + if(cookie != null) + { + connection.setRequestProperty("cookie", cookie); + } + + // This must be explicitly set for gzip progress tracking to work. + connection.setRequestProperty("Accept-Encoding", "gzip"); + + // Handle the other headers + if (headers != null) { + addHeadersToRequest(connection, headers); + } + + connection.connect(); + if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { + cached = true; + connection.disconnect(); + Log.d(LOG_TAG, "Resource not modified: " + source); + JSONObject error = createFileTransferError(NOT_MODIFIED_ERR, source, target, connection, null); + result = new PluginResult(PluginResult.Status.ERROR, error); + } else { + if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) { + // Only trust content-length header if we understand + // the encoding -- identity or gzip + if (connection.getContentLength() != -1) { + progress.setLengthComputable(true); + progress.setTotal(connection.getContentLength()); + } + } + inputStream = getInputStream(connection); + } + } + + if (!cached) { + try { + synchronized (context) { + if (context.aborted) { + return; + } + context.connection = connection; + } + + // write bytes to file + byte[] buffer = new byte[MAX_BUFFER_SIZE]; + int bytesRead = 0; + outputStream = resourceApi.openOutputStream(targetUri); + while ((bytesRead = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, bytesRead); + // Send a progress event. + progress.setLoaded(inputStream.getTotalRawBytesRead()); + PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject()); + progressResult.setKeepCallback(true); + context.sendPluginResult(progressResult); + } + } finally { + synchronized (context) { + context.connection = null; + } + safeClose(inputStream); + safeClose(outputStream); + } + + Log.d(LOG_TAG, "Saved file: " + target); + + + // create FileEntry object + Class webViewClass = webView.getClass(); + PluginManager pm = null; + try { + Method gpm = webViewClass.getMethod("getPluginManager"); + pm = (PluginManager) gpm.invoke(webView); + } catch (NoSuchMethodException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + if (pm == null) { + try { + Field pmf = webViewClass.getField("pluginManager"); + pm = (PluginManager)pmf.get(webView); + } catch (NoSuchFieldException e) { + } catch (IllegalAccessException e) { + } + } + file = resourceApi.mapUriToFile(targetUri); + context.targetFile = file; + FileUtils filePlugin = (FileUtils) pm.getPlugin("File"); + if (filePlugin != null) { + JSONObject fileEntry = filePlugin.getEntryForFile(file); + if (fileEntry != null) { + result = new PluginResult(PluginResult.Status.OK, fileEntry); + } else { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection, null); + Log.e(LOG_TAG, "File plugin cannot represent download path"); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } + } else { + Log.e(LOG_TAG, "File plugin not found; cannot save downloaded file"); + result = new PluginResult(PluginResult.Status.ERROR, "File plugin not found; cannot save downloaded file"); + } + } + } catch (FileNotFoundException e) { + JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection, e); + Log.e(LOG_TAG, error.toString(), e); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } catch (IOException e) { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection, e); + Log.e(LOG_TAG, error.toString(), e); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + result = new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } catch (Throwable e) { + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection, e); + Log.e(LOG_TAG, error.toString(), e); + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); + } finally { + synchronized (activeRequests) { + activeRequests.remove(objectId); + } + + if (connection != null) { + // Revert back to the proper verifier and socket factories + if (trustEveryone && useHttps) { + HttpsURLConnection https = (HttpsURLConnection) connection; + https.setHostnameVerifier(oldHostnameVerifier); + https.setSSLSocketFactory(oldSocketFactory); + } + } + + if (result == null) { + result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, target, connection, null)); + } + // Remove incomplete download. + if (!cached && result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) { + file.delete(); + } + context.sendPluginResult(result); + } + } + }); + } + + /** + * Abort an ongoing upload or download. + */ + private void abort(String objectId) { + final RequestContext context; + synchronized (activeRequests) { + context = activeRequests.remove(objectId); + } + if (context != null) { + // Closing the streams can block, so execute on a background thread. + cordova.getThreadPool().execute(new Runnable() { + public void run() { + synchronized (context) { + File file = context.targetFile; + if (file != null) { + file.delete(); + } + // Trigger the abort callback immediately to minimize latency between it and abort() being called. + JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, null, -1, null); + context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error)); + context.aborted = true; + if (context.connection != null) { + try { + context.connection.disconnect(); + } catch (Exception e) { + Log.e(LOG_TAG, "CB-8431 Catch workaround for fatal exception", e); + } + } + } + } + }); + } + } +} diff --git a/plugins/cordova-plugin-file-transfer/src/android/FileUploadResult.java b/plugins/cordova-plugin-file-transfer/src/android/FileUploadResult.java new file mode 100644 index 0000000..c24ea78 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/android/FileUploadResult.java @@ -0,0 +1,73 @@ +/* + 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.filetransfer; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Encapsulates the result and/or status of uploading a file to a remote server. + */ +public class FileUploadResult { + + private long bytesSent = 0; // bytes sent + private int responseCode = -1; // HTTP response code + private String response = null; // HTTP response + private String objectId = null; // FileTransfer object id + + public long getBytesSent() { + return bytesSent; + } + + public void setBytesSent(long bytes) { + this.bytesSent = bytes; + } + + public int getResponseCode() { + return responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public String getResponse() { + return response; + } + + public void setResponse(String response) { + this.response = response; + } + + public String getObjectId() { + return objectId; + } + + public void setObjectId(String objectId) { + this.objectId = objectId; + } + + public JSONObject toJSONObject() throws JSONException { + return new JSONObject( + "{bytesSent:" + bytesSent + + ",responseCode:" + responseCode + + ",response:" + JSONObject.quote(response) + + ",objectId:" + JSONObject.quote(objectId) + "}"); + } +} diff --git a/plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.h b/plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.h new file mode 100644 index 0000000..bb5fa13 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.h @@ -0,0 +1,88 @@ +/* + 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 +#import +#import "CDVFile.h" + +enum CDVFileTransferError { + FILE_NOT_FOUND_ERR = 1, + INVALID_URL_ERR = 2, + CONNECTION_ERR = 3, + CONNECTION_ABORTED = 4, + NOT_MODIFIED = 5 +}; +typedef int CDVFileTransferError; + +enum CDVFileTransferDirection { + CDV_TRANSFER_UPLOAD = 1, + CDV_TRANSFER_DOWNLOAD = 2, +}; +typedef int CDVFileTransferDirection; + +// Magic value within the options dict used to set a cookie. +extern NSString* const kOptionsKeyCookie; + +@interface CDVFileTransfer : CDVPlugin {} + +- (void)upload:(CDVInvokedUrlCommand*)command; +- (void)download:(CDVInvokedUrlCommand*)command; +- (NSString*)escapePathComponentForUrlString:(NSString*)urlString; + +// Visible for testing. +- (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData; +- (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target; + +- (NSMutableDictionary*)createFileTransferError:(int)code + AndSource:(NSString*)source + AndTarget:(NSString*)target + AndHttpStatus:(int)httpStatus + AndBody:(NSString*)body; +@property (nonatomic, strong) NSOperationQueue* queue; +@property (readonly) NSMutableDictionary* activeTransfers; +@end + +@class CDVFileTransferEntityLengthRequest; + +@interface CDVFileTransferDelegate : NSObject {} + +- (void)updateBytesExpected:(long long)newBytesExpected; +- (void)cancelTransfer:(NSURLConnection*)connection; + +@property (strong) NSMutableData* responseData; // atomic +@property (nonatomic, strong) NSDictionary* responseHeaders; +@property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskID; +@property (nonatomic, strong) CDVFileTransfer* command; +@property (nonatomic, assign) CDVFileTransferDirection direction; +@property (nonatomic, strong) NSURLConnection* connection; +@property (nonatomic, copy) NSString* callbackId; +@property (nonatomic, copy) NSString* objectId; +@property (nonatomic, copy) NSString* source; +@property (nonatomic, copy) NSString* target; +@property (nonatomic, copy) NSURL* targetURL; +@property (nonatomic, copy) NSString* mimeType; +@property (assign) int responseCode; // atomic +@property (nonatomic, assign) long long bytesTransfered; +@property (nonatomic, assign) long long bytesExpected; +@property (nonatomic, assign) BOOL trustAllHosts; +@property (strong) NSFileHandle* targetFileHandle; +@property (nonatomic, strong) CDVFileTransferEntityLengthRequest* entityLengthRequest; +@property (nonatomic, strong) CDVFile *filePlugin; + +@end diff --git a/plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.m b/plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.m new file mode 100644 index 0000000..4775a6d --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.m @@ -0,0 +1,845 @@ +/* + 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 +#import "CDVFileTransfer.h" +#import "CDVLocalFilesystem.h" + +#import +#import +#import +#import + +#ifndef DLog +#ifdef DEBUG + #define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) +#else + #define DLog(...) +#endif +#endif + +@interface CDVFileTransfer () +// Sets the requests headers for the request. +- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req; +// Creates a delegate to handle an upload. +- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command; +// Creates an NSData* for the file for the given upload arguments. +- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command; +@end + +// Buffer size to use for streaming uploads. +static const NSUInteger kStreamBufferSize = 32768; +// Magic value within the options dict used to set a cookie. +NSString* const kOptionsKeyCookie = @"__cookie"; +// Form boundary for multi-part requests. +NSString* const kFormBoundary = @"+++++org.apache.cordova.formBoundary"; + +// Writes the given data to the stream in a blocking way. +// If successful, returns bytesToWrite. +// If the stream was closed on the other end, returns 0. +// If there was an error, returns -1. +static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) +{ + UInt8* bytes = (UInt8*)[data bytes]; + long long bytesToWrite = [data length]; + long long totalBytesWritten = 0; + + while (totalBytesWritten < bytesToWrite) { + CFIndex result = CFWriteStreamWrite(stream, + bytes + totalBytesWritten, + bytesToWrite - totalBytesWritten); + if (result < 0) { + CFStreamError error = CFWriteStreamGetError(stream); + NSLog(@"WriteStreamError domain: %ld error: %ld", error.domain, (long)error.error); + return result; + } else if (result == 0) { + return result; + } + totalBytesWritten += result; + } + + return totalBytesWritten; +} + +@implementation CDVFileTransfer +@synthesize activeTransfers; + +- (void)pluginInitialize { + activeTransfers = [[NSMutableDictionary alloc] init]; +} + +- (NSString*)escapePathComponentForUrlString:(NSString*)urlString +{ + NSRange schemeAndHostRange = [urlString rangeOfString:@"://.*?/" options:NSRegularExpressionSearch]; + + if (schemeAndHostRange.length == 0) { + return urlString; + } + + NSInteger schemeAndHostEndIndex = NSMaxRange(schemeAndHostRange); + NSString* schemeAndHost = [urlString substringToIndex:schemeAndHostEndIndex]; + NSString* pathComponent = [urlString substringFromIndex:schemeAndHostEndIndex]; + pathComponent = [pathComponent stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + return [schemeAndHost stringByAppendingString:pathComponent]; +} + +- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req +{ + [req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"]; + + NSString* userAgent = [self.commandDelegate userAgent]; + if (userAgent) { + [req setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + + for (NSString* headerName in headers) { + id value = [headers objectForKey:headerName]; + if (!value || (value == [NSNull null])) { + value = @"null"; + } + + // First, remove an existing header if one exists. + [req setValue:nil forHTTPHeaderField:headerName]; + + if (![value isKindOfClass:[NSArray class]]) { + value = [NSArray arrayWithObject:value]; + } + + // Then, append all header values. + for (id __strong subValue in value) { + // Convert from an NSNumber -> NSString. + if ([subValue respondsToSelector:@selector(stringValue)]) { + subValue = [subValue stringValue]; + } + if ([subValue isKindOfClass:[NSString class]]) { + [req addValue:subValue forHTTPHeaderField:headerName]; + } + } + } +} + +- (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData +{ + // arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode] + // however, params is a JavaScript object and during marshalling is put into the options dict, + // thus debug and chunkedMode are the 6th and 7th arguments + NSString* target = [command argumentAtIndex:0]; + NSString* server = [command argumentAtIndex:1]; + NSString* fileKey = [command argumentAtIndex:2 withDefault:@"file"]; + NSString* fileName = [command argumentAtIndex:3 withDefault:@"image.jpg"]; + NSString* mimeType = [command argumentAtIndex:4 withDefault:@"image/jpeg"]; + NSDictionary* options = [command argumentAtIndex:5 withDefault:nil]; + // BOOL trustAllHosts = [[command argumentAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs + BOOL chunkedMode = [[command argumentAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue]; + NSDictionary* headers = [command argumentAtIndex:8 withDefault:nil]; + // Allow alternative http method, default to POST. JS side checks + // for allowed methods, currently PUT or POST (forces POST for + // unrecognised values) + NSString* httpMethod = [command argumentAtIndex:10 withDefault:@"POST"]; + CDVPluginResult* result = nil; + CDVFileTransferError errorCode = 0; + + // NSURL does not accepts URLs with spaces in the path. We escape the path in order + // to be more lenient. + NSURL* url = [NSURL URLWithString:server]; + + if (!url) { + errorCode = INVALID_URL_ERR; + NSLog(@"File Transfer Error: Invalid server URL %@", server); + } else if (!fileData) { + errorCode = FILE_NOT_FOUND_ERR; + } + + if (errorCode > 0) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:target AndTarget:server]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + return nil; + } + + NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url]; + + [req setHTTPMethod:httpMethod]; + + // Magic value to set a cookie + if ([options objectForKey:kOptionsKeyCookie]) { + [req setValue:[options objectForKey:kOptionsKeyCookie] forHTTPHeaderField:@"Cookie"]; + [req setHTTPShouldHandleCookies:NO]; + } + + // if we specified a Content-Type header, don't do multipart form upload + BOOL multipartFormUpload = [headers objectForKey:@"Content-Type"] == nil; + if (multipartFormUpload) { + NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kFormBoundary]; + [req setValue:contentType forHTTPHeaderField:@"Content-Type"]; + } + [self applyRequestHeaders:headers toRequest:req]; + + NSData* formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]; + NSMutableData* postBodyBeforeFile = [NSMutableData data]; + + for (NSString* key in options) { + id val = [options objectForKey:key]; + if (!val || (val == [NSNull null]) || [key isEqualToString:kOptionsKeyCookie]) { + continue; + } + // if it responds to stringValue selector (eg NSNumber) get the NSString + if ([val respondsToSelector:@selector(stringValue)]) { + val = [val stringValue]; + } + // finally, check whether it is a NSString (for dataUsingEncoding selector below) + if (![val isKindOfClass:[NSString class]]) { + continue; + } + + [postBodyBeforeFile appendData:formBoundaryData]; + [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBodyBeforeFile appendData:[val dataUsingEncoding:NSUTF8StringEncoding]]; + [postBodyBeforeFile appendData:[@"\r\n" dataUsingEncoding : NSUTF8StringEncoding]]; + } + + [postBodyBeforeFile appendData:formBoundaryData]; + [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]]; + if (mimeType != nil) { + [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]]; + } + [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]]; + + DLog(@"fileData length: %d", [fileData length]); + NSData* postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]; + + long long totalPayloadLength = [fileData length]; + if (multipartFormUpload) { + totalPayloadLength += [postBodyBeforeFile length] + [postBodyAfterFile length]; + } + + [req setValue:[[NSNumber numberWithLongLong:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"]; + + if (chunkedMode) { + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + CFStreamCreateBoundPair(NULL, &readStream, &writeStream, kStreamBufferSize); + [req setHTTPBodyStream:CFBridgingRelease(readStream)]; + + [self.commandDelegate runInBackground:^{ + if (CFWriteStreamOpen(writeStream)) { + if (multipartFormUpload) { + NSData* chunks[] = { postBodyBeforeFile, fileData, postBodyAfterFile }; + int numChunks = sizeof(chunks) / sizeof(chunks[0]); + + for (int i = 0; i < numChunks; ++i) { + // Allow uploading of an empty file + if (chunks[i].length == 0) { + continue; + } + + CFIndex result = WriteDataToStream(chunks[i], writeStream); + if (result <= 0) { + break; + } + } + } else { + WriteDataToStream(fileData, writeStream); + } + } else { + NSLog(@"FileTransfer: Failed to open writeStream"); + } + CFWriteStreamClose(writeStream); + CFRelease(writeStream); + }]; + } else { + if (multipartFormUpload) { + [postBodyBeforeFile appendData:fileData]; + [postBodyBeforeFile appendData:postBodyAfterFile]; + [req setHTTPBody:postBodyBeforeFile]; + } else { + [req setHTTPBody:fileData]; + } + } + return req; +} + +- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command +{ + NSString* source = [command argumentAtIndex:0]; + NSString* server = [command argumentAtIndex:1]; + BOOL trustAllHosts = [[command argumentAtIndex:6 withDefault:[NSNumber numberWithBool:NO]] boolValue]; // allow self-signed certs + NSString* objectId = [command argumentAtIndex:9]; + + CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init]; + + delegate.command = self; + delegate.callbackId = command.callbackId; + delegate.direction = CDV_TRANSFER_UPLOAD; + delegate.objectId = objectId; + delegate.source = source; + delegate.target = server; + delegate.trustAllHosts = trustAllHosts; + delegate.filePlugin = [self.commandDelegate getCommandInstance:@"File"]; + + return delegate; +} + +- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command +{ + NSString* source = (NSString*)[command argumentAtIndex:0]; + NSString* server = [command argumentAtIndex:1]; + NSError* __autoreleasing err = nil; + + if ([source hasPrefix:@"data:"] && [source rangeOfString:@"base64"].location != NSNotFound) { + NSRange commaRange = [source rangeOfString: @","]; + if (commaRange.location == NSNotFound) { + // Return error is there is no comma + __weak CDVFileTransfer* weakSelf = self; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[weakSelf createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:server]]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + return; + } + + if (commaRange.location + 1 > source.length - 1) { + // Init as an empty data + NSData *fileData = [[NSData alloc] init]; + [self uploadData:fileData command:command]; + return; + } + + NSData *fileData = [[NSData alloc] initWithBase64EncodedString:[source substringFromIndex:(commaRange.location + 1)] options:NSDataBase64DecodingIgnoreUnknownCharacters]; + [self uploadData:fileData command:command]; + return; + } + + CDVFilesystemURL *sourceURL = [CDVFilesystemURL fileSystemURLWithString:source]; + NSObject *fs; + if (sourceURL) { + // Try to get a CDVFileSystem which will handle this file. + // This requires talking to the current CDVFile plugin. + fs = [[self.commandDelegate getCommandInstance:@"File"] filesystemForURL:sourceURL]; + } + if (fs) { + __weak CDVFileTransfer* weakSelf = self; + [fs readFileAtURL:sourceURL start:0 end:-1 callback:^(NSData *fileData, NSString *mimeType, CDVFileError err) { + if (err) { + // We couldn't find the asset. Send the appropriate error. + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[weakSelf createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]]; + [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + [weakSelf uploadData:fileData command:command]; + } + }]; + return; + } else { + // Extract the path part out of a file: URL. + NSString* filePath = [source hasPrefix:@"/"] ? [source copy] : [(NSURL *)[NSURL URLWithString:source] path]; + if (filePath == nil) { + // We couldn't find the asset. Send the appropriate error. + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + return; + } + + // Memory map the file so that it can be read efficiently even if it is large. + NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err]; + + if (err != nil) { + NSLog(@"Error opening file %@: %@", source, err); + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + [self uploadData:fileData command:command]; + } + } +} + +- (void)upload:(CDVInvokedUrlCommand*)command +{ + // fileData and req are split into helper functions to ease the unit testing of delegateForUpload. + // First, get the file data. This method will call `uploadData:command`. + [self fileDataForUploadCommand:command]; +} + +- (void)uploadData:(NSData*)fileData command:(CDVInvokedUrlCommand*)command +{ + NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData]; + + if (req == nil) { + return; + } + CDVFileTransferDelegate* delegate = [self delegateForUploadCommand:command]; + delegate.connection = [[NSURLConnection alloc] initWithRequest:req delegate:delegate startImmediately:NO]; + if (self.queue == nil) { + self.queue = [[NSOperationQueue alloc] init]; + } + [delegate.connection setDelegateQueue:self.queue]; + + // sets a background task ID for the transfer object. + delegate.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + [delegate cancelTransfer:delegate.connection]; + }]; + + @synchronized (activeTransfers) { + activeTransfers[delegate.objectId] = delegate; + } + [delegate.connection start]; +} + +- (void)abort:(CDVInvokedUrlCommand*)command +{ + NSString* objectId = [command argumentAtIndex:0]; + + @synchronized (activeTransfers) { + CDVFileTransferDelegate* delegate = activeTransfers[objectId]; + if (delegate != nil) { + [delegate cancelTransfer:delegate.connection]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:CONNECTION_ABORTED AndSource:delegate.source AndTarget:delegate.target]]; + [self.commandDelegate sendPluginResult:result callbackId:delegate.callbackId]; + } + } +} + +- (void)download:(CDVInvokedUrlCommand*)command +{ + DLog(@"File Transfer downloading file..."); + NSString* source = [command argumentAtIndex:0]; + NSString* target = [command argumentAtIndex:1]; + BOOL trustAllHosts = [[command argumentAtIndex:2 withDefault:[NSNumber numberWithBool:NO]] boolValue]; // allow self-signed certs + NSString* objectId = [command argumentAtIndex:3]; + NSDictionary* headers = [command argumentAtIndex:4 withDefault:nil]; + + CDVPluginResult* result = nil; + CDVFileTransferError errorCode = 0; + + NSURL* targetURL; + + if ([target hasPrefix:@"/"]) { + /* Backwards-compatibility: + * Check here to see if it looks like the user passed in a raw filesystem path. (Perhaps they had the path saved, and were previously using it with the old version of File). If so, normalize it by removing empty path segments, and check with File to see if any of the installed filesystems will handle it. If so, then we will end up with a filesystem url to use for the remainder of this operation. + */ + target = [target stringByReplacingOccurrencesOfString:@"//" withString:@"/"]; + targetURL = [[self.commandDelegate getCommandInstance:@"File"] fileSystemURLforLocalPath:target].url; + } else { + targetURL = [NSURL URLWithString:target]; + } + + NSURL* sourceURL = [NSURL URLWithString:source]; + + if (!sourceURL) { + errorCode = INVALID_URL_ERR; + NSLog(@"File Transfer Error: Invalid server URL %@", source); + } else if (![targetURL isFileURL]) { + CDVFilesystemURL *fsURL = [CDVFilesystemURL fileSystemURLWithString:target]; + if (!fsURL) { + errorCode = FILE_NOT_FOUND_ERR; + NSLog(@"File Transfer Error: Invalid file path or URL %@", target); + } + } + + if (errorCode > 0) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:source AndTarget:target]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + return; + } + + NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:sourceURL]; + [self applyRequestHeaders:headers toRequest:req]; + + CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init]; + delegate.command = self; + delegate.direction = CDV_TRANSFER_DOWNLOAD; + delegate.callbackId = command.callbackId; + delegate.objectId = objectId; + delegate.source = source; + delegate.target = [targetURL absoluteString]; + delegate.targetURL = targetURL; + delegate.trustAllHosts = trustAllHosts; + delegate.filePlugin = [self.commandDelegate getCommandInstance:@"File"]; + delegate.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + [delegate cancelTransfer:delegate.connection]; + }]; + + delegate.connection = [[NSURLConnection alloc] initWithRequest:req delegate:delegate startImmediately:NO]; + + if (self.queue == nil) { + self.queue = [[NSOperationQueue alloc] init]; + } + [delegate.connection setDelegateQueue:self.queue]; + + @synchronized (activeTransfers) { + activeTransfers[delegate.objectId] = delegate; + } + // Downloads can take time + // sending this to a new thread calling the download_async method + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), + ^(void) { [delegate.connection start];} + ); +} + +- (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target +{ + NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:3]; + + [result setObject:[NSNumber numberWithInt:code] forKey:@"code"]; + if (source != nil) { + [result setObject:source forKey:@"source"]; + } + if (target != nil) { + [result setObject:target forKey:@"target"]; + } + NSLog(@"FileTransferError %@", result); + + return result; +} + +- (NSMutableDictionary*)createFileTransferError:(int)code + AndSource:(NSString*)source + AndTarget:(NSString*)target + AndHttpStatus:(int)httpStatus + AndBody:(NSString*)body +{ + NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:5]; + + [result setObject:[NSNumber numberWithInt:code] forKey:@"code"]; + if (source != nil) { + [result setObject:source forKey:@"source"]; + } + if (target != nil) { + [result setObject:target forKey:@"target"]; + } + [result setObject:[NSNumber numberWithInt:httpStatus] forKey:@"http_status"]; + if (body != nil) { + [result setObject:body forKey:@"body"]; + } + NSLog(@"FileTransferError %@", result); + + return result; +} + +- (void)onReset { + @synchronized (activeTransfers) { + while ([activeTransfers count] > 0) { + CDVFileTransferDelegate* delegate = [activeTransfers allValues][0]; + [delegate cancelTransfer:delegate.connection]; + } + } +} + +@end + +@interface CDVFileTransferEntityLengthRequest : NSObject { + NSURLConnection* _connection; + CDVFileTransferDelegate* __weak _originalDelegate; +} + +- (CDVFileTransferEntityLengthRequest*)initWithOriginalRequest:(NSURLRequest*)originalRequest andDelegate:(CDVFileTransferDelegate*)originalDelegate; + +@end + +@implementation CDVFileTransferEntityLengthRequest + +- (CDVFileTransferEntityLengthRequest*)initWithOriginalRequest:(NSURLRequest*)originalRequest andDelegate:(CDVFileTransferDelegate*)originalDelegate +{ + if (self) { + DLog(@"Requesting entity length for GZIPped content..."); + + NSMutableURLRequest* req = [originalRequest mutableCopy]; + [req setHTTPMethod:@"HEAD"]; + [req setValue:@"identity" forHTTPHeaderField:@"Accept-Encoding"]; + + _originalDelegate = originalDelegate; + _connection = [NSURLConnection connectionWithRequest:req delegate:self]; + } + return self; +} + +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response +{ + DLog(@"HEAD request returned; content-length is %lld", [response expectedContentLength]); + [_originalDelegate updateBytesExpected:[response expectedContentLength]]; +} + +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data +{} + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection +{} + +@end + +@implementation CDVFileTransferDelegate + +@synthesize callbackId, connection = _connection, source, target, responseData, responseHeaders, command, bytesTransfered, bytesExpected, direction, responseCode, objectId, targetFileHandle, filePlugin; + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection +{ + NSString* uploadResponse = nil; + NSString* downloadResponse = nil; + NSMutableDictionary* uploadResult; + CDVPluginResult* result = nil; + + NSLog(@"File Transfer Finished with response code %d", self.responseCode); + + if (self.direction == CDV_TRANSFER_UPLOAD) { + uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + if (uploadResponse == nil) { + uploadResponse = [[NSString alloc] initWithData: self.responseData encoding:NSISOLatin1StringEncoding]; + } + + if ((self.responseCode >= 200) && (self.responseCode < 300)) { + // create dictionary to return FileUploadResult object + uploadResult = [NSMutableDictionary dictionaryWithCapacity:3]; + if (uploadResponse != nil) { + [uploadResult setObject:uploadResponse forKey:@"response"]; + [uploadResult setObject:self.responseHeaders forKey:@"headers"]; + } + [uploadResult setObject:[NSNumber numberWithLongLong:self.bytesTransfered] forKey:@"bytesSent"]; + [uploadResult setObject:[NSNumber numberWithInt:self.responseCode] forKey:@"responseCode"]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadResult]; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:uploadResponse]]; + } + } + if (self.direction == CDV_TRANSFER_DOWNLOAD) { + if (self.targetFileHandle) { + [self.targetFileHandle closeFile]; + self.targetFileHandle = nil; + DLog(@"File Transfer Download success"); + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self.filePlugin makeEntryForURL:self.targetURL]]; + } else { + downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + if (downloadResponse == nil) { + downloadResponse = [[NSString alloc] initWithData: self.responseData encoding:NSISOLatin1StringEncoding]; + } + + CDVFileTransferError errorCode = self.responseCode == 404 ? FILE_NOT_FOUND_ERR + : (self.responseCode == 304 ? NOT_MODIFIED : CONNECTION_ERR); + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:errorCode AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]]; + } + } + + [self.command.commandDelegate sendPluginResult:result callbackId:callbackId]; + + // remove connection for activeTransfers + @synchronized (command.activeTransfers) { + [command.activeTransfers removeObjectForKey:objectId]; + // remove background id task in case our upload was done in the background + [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID]; + self.backgroundTaskID = UIBackgroundTaskInvalid; + } +} + +- (void)removeTargetFile +{ + NSFileManager* fileMgr = [NSFileManager defaultManager]; + + NSString *targetPath = [self targetFilePath]; + if ([fileMgr fileExistsAtPath:targetPath]) + { + [fileMgr removeItemAtPath:targetPath error:nil]; + } +} + +- (void)cancelTransfer:(NSURLConnection*)connection +{ + [connection cancel]; + @synchronized (self.command.activeTransfers) { + CDVFileTransferDelegate* delegate = self.command.activeTransfers[self.objectId]; + [self.command.activeTransfers removeObjectForKey:self.objectId]; + [[UIApplication sharedApplication] endBackgroundTask:delegate.backgroundTaskID]; + delegate.backgroundTaskID = UIBackgroundTaskInvalid; + } + + if (self.direction == CDV_TRANSFER_DOWNLOAD) { + [self removeTargetFile]; + } +} + +- (void)cancelTransferWithError:(NSURLConnection*)connection errorMessage:(NSString*)errorMessage +{ + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[self.command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:self.source AndTarget:self.target AndHttpStatus:self.responseCode AndBody:errorMessage]]; + + NSLog(@"File Transfer Error: %@", errorMessage); + [self cancelTransfer:connection]; + [self.command.commandDelegate sendPluginResult:result callbackId:callbackId]; +} + +- (NSString *)targetFilePath +{ + NSString *path = nil; + CDVFilesystemURL *sourceURL = [CDVFilesystemURL fileSystemURLWithString:self.target]; + if (sourceURL && sourceURL.fileSystemName != nil) { + // This requires talking to the current CDVFile plugin + NSObject *fs = [self.filePlugin filesystemForURL:sourceURL]; + path = [fs filesystemPathForURL:sourceURL]; + } else { + // Extract the path part out of a file: URL. + path = [self.target hasPrefix:@"/"] ? [self.target copy] : [(NSURL *)[NSURL URLWithString:self.target] path]; + } + return path; +} + +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response +{ + NSError* __autoreleasing error = nil; + + self.mimeType = [response MIMEType]; + self.targetFileHandle = nil; + + // required for iOS 4.3, for some reason; response is + // a plain NSURLResponse, not the HTTP subclass + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + + self.responseCode = (int)[httpResponse statusCode]; + self.bytesExpected = [response expectedContentLength]; + self.responseHeaders = [httpResponse allHeaderFields]; + if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode == 200) && (self.bytesExpected == NSURLResponseUnknownLength)) { + // Kick off HEAD request to server to get real length + // bytesExpected will be updated when that response is returned + self.entityLengthRequest = [[CDVFileTransferEntityLengthRequest alloc] initWithOriginalRequest:connection.currentRequest andDelegate:self]; + } + } else if ([response.URL isFileURL]) { + NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:[response.URL path] error:nil]; + self.responseCode = 200; + self.bytesExpected = [attr[NSFileSize] longLongValue]; + } else { + self.responseCode = 200; + self.bytesExpected = NSURLResponseUnknownLength; + } + if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode >= 200) && (self.responseCode < 300)) { + // Download response is okay; begin streaming output to file + NSString *filePath = [self targetFilePath]; + if (filePath == nil) { + // We couldn't find the asset. Send the appropriate error. + [self cancelTransferWithError:connection errorMessage:[NSString stringWithFormat:@"Could not create target file"]]; + return; + } + + NSString* parentPath = [filePath stringByDeletingLastPathComponent]; + + // create parent directories if needed + if ([[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:&error] == NO) { + if (error) { + [self cancelTransferWithError:connection errorMessage:[NSString stringWithFormat:@"Could not create path to save downloaded file: %@", [error localizedDescription]]]; + } else { + [self cancelTransferWithError:connection errorMessage:@"Could not create path to save downloaded file"]; + } + return; + } + // create target file + if ([[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil] == NO) { + [self cancelTransferWithError:connection errorMessage:@"Could not create target file"]; + return; + } + // open target file for writing + self.targetFileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; + if (self.targetFileHandle == nil) { + [self cancelTransferWithError:connection errorMessage:@"Could not open target file for writing"]; + } + DLog(@"Streaming to file %@", filePath); + } +} + +- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error +{ + NSString* body = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:body]]; + + NSLog(@"File Transfer Error: %@", [error localizedDescription]); + + [self cancelTransfer:connection]; + [self.command.commandDelegate sendPluginResult:result callbackId:callbackId]; +} + +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data +{ + self.bytesTransfered += data.length; + if (self.targetFileHandle) { + [self.targetFileHandle writeData:data]; + } else { + [self.responseData appendData:data]; + } + [self updateProgress]; +} + +- (void)updateBytesExpected:(long long)newBytesExpected +{ + DLog(@"Updating bytesExpected to %lld", newBytesExpected); + self.bytesExpected = newBytesExpected; + [self updateProgress]; +} + +- (void)updateProgress +{ + if (self.direction == CDV_TRANSFER_DOWNLOAD) { + BOOL lengthComputable = (self.bytesExpected != NSURLResponseUnknownLength); + // If the response is GZipped, and we have an outstanding HEAD request to get + // the length, then hold off on sending progress events. + if (!lengthComputable && (self.entityLengthRequest != nil)) { + return; + } + NSMutableDictionary* downloadProgress = [NSMutableDictionary dictionaryWithCapacity:3]; + [downloadProgress setObject:[NSNumber numberWithBool:lengthComputable] forKey:@"lengthComputable"]; + [downloadProgress setObject:[NSNumber numberWithLongLong:self.bytesTransfered] forKey:@"loaded"]; + [downloadProgress setObject:[NSNumber numberWithLongLong:self.bytesExpected] forKey:@"total"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:downloadProgress]; + [result setKeepCallbackAsBool:true]; + [self.command.commandDelegate sendPluginResult:result callbackId:callbackId]; + } +} + +- (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +{ + if (self.direction == CDV_TRANSFER_UPLOAD) { + NSMutableDictionary* uploadProgress = [NSMutableDictionary dictionaryWithCapacity:3]; + + [uploadProgress setObject:[NSNumber numberWithBool:true] forKey:@"lengthComputable"]; + [uploadProgress setObject:[NSNumber numberWithLongLong:totalBytesWritten] forKey:@"loaded"]; + [uploadProgress setObject:[NSNumber numberWithLongLong:totalBytesExpectedToWrite] forKey:@"total"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadProgress]; + [result setKeepCallbackAsBool:true]; + [self.command.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + self.bytesTransfered = totalBytesWritten; +} + +// for self signed certificates +- (void)connection:(NSURLConnection*)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge +{ + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if (self.trustAllHosts) { + NSURLCredential* credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; + } + [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; + } else { + [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; + } +} + +- (id)init +{ + if ((self = [super init])) { + self.responseData = [NSMutableData data]; + self.targetFileHandle = nil; + } + return self; +} + +@end diff --git a/plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.cpp b/plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.cpp new file mode 100644 index 0000000..5b1adea --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.cpp @@ -0,0 +1,265 @@ +/* + * + * Copyright 2013 Canonical Ltd. + * + * 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. + * +*/ + +#include "file-transfer.h" +#include +#include + +static void SetHeaders(QNetworkRequest &request, const QVariantMap &headers) { + for (const QString &key: headers.keys()) { + QVariant val = *headers.find(key); + QString value = val.toString(); + if (val.userType() == QMetaType::QVariantList || val.userType() == QMetaType::QStringList) { + QList list = val.toList(); + for (QVariant v: list) { + if (value.size()) + value += ", "; + value += v.toString(); + } + } + request.setRawHeader(key.toUtf8(), value.toUtf8()); + } +} + +void FileTransfer::download(int scId, int ecId, const QString& url, const QString &target, bool /*trustAllHost*/, int id, const QVariantMap &headers) { + QSharedPointer request(new FileTransferRequest(_manager, scId, ecId, id, this)); + + assert(_id2request.find(id) == _id2request.end()); + + _id2request.insert(id, request); + + request->connect(request.data(), &FileTransferRequest::done, [&]() { + auto it = _id2request.find(id); + while (it != _id2request.end() && it.key() == id) { + if (it.value().data() == request.data()) { + _id2request.erase(it); + break; + } + it++; + } + }); + request->download(url, target, headers); +} + +void FileTransfer::upload(int scId, int ecId, const QString &fileURI, const QString& url, const QString& fileKey, const QString& fileName, const QString& mimeType, + const QVariantMap & params, bool /*trustAllHosts*/, bool /*chunkedMode*/, const QVariantMap &headers, int id, const QString &/*httpMethod*/) { + QSharedPointer request(new FileTransferRequest(_manager, scId, ecId, id, this)); + + assert(_id2request.find(id) == _id2request.end()); + + _id2request.insert(id, request); + + request->connect(request.data(), &FileTransferRequest::done, [&]() { + auto it = _id2request.find(id); + while (it != _id2request.end() && it.key() == id) { + if (it.value().data() == request.data()) { + _id2request.erase(it); + break; + } + it++; + } + }); + request->upload(url, fileURI, fileKey, fileName, mimeType, params, headers); +} + +void FileTransfer::abort(int scId, int ecId, int id) { + Q_UNUSED(scId) + Q_UNUSED(ecId) + + auto it = _id2request.find(id); + while (it != _id2request.end() && it.key() == id) { + (*it)->abort(); + it++; + } +} + +void FileTransferRequest::download(const QString& uri, const QString &targetURI, const QVariantMap &headers) { + QUrl url(uri); + QNetworkRequest request; + + QSharedPointer filePlugin(_plugin->cordova()->getPlugin()); + + if (!filePlugin.data()) + return; + + if (!url.isValid()) { + QVariantMap map; + map.insert("code", INVALID_URL_ERR); + map.insert("source", uri); + map.insert("target", targetURI); + _plugin->cb(_ecId, map); + emit done(); + return; + } + + request.setUrl(url); + if (url.password().size() || url.userName().size()) { + QString headerData = "Basic " + (url.userName() + ":" + url.password()).toLocal8Bit().toBase64(); + request.setRawHeader("Authorization", headerData.toLocal8Bit()); + } + SetHeaders(request, headers); + _reply = QSharedPointer(_manager.get(request)); + + _reply->connect(_reply.data(), &QNetworkReply::finished, [this, targetURI, uri, filePlugin]() { + if (!_scId || _reply->error() != QNetworkReply::NoError) + return; + + QPair f1(dynamic_cast(filePlugin.data())->resolveURI(targetURI)); + + QFile res(f1.second.absoluteFilePath()); + if (!f1.first || !res.open(QIODevice::WriteOnly)) { + QVariantMap map; + map.insert("code", INVALID_URL_ERR); + map.insert("source", uri); + map.insert("target", targetURI); + _plugin->cb(_ecId, map); + emit done(); + return; + } + res.write(_reply->readAll()); + + _plugin->cb(_scId, dynamic_cast(filePlugin.data())->file2map(f1.second)); + + emit done(); + }); + _reply->connect(_reply.data(), SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError))); + _reply->connect(_reply.data(), SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(progress(qint64, qint64))); +} + +void FileTransferRequest::upload(const QString& _url, const QString& fileURI, QString fileKey, QString fileName, QString mimeType, const QVariantMap ¶ms, const QVariantMap &headers) { + QUrl url(_url); + QNetworkRequest request; + + QSharedPointer filePlugin(_plugin->cordova()->getPlugin()); + + if (!filePlugin.data()) + return; + + if (!url.isValid()) { + QVariantMap map; + map.insert("code", INVALID_URL_ERR); + map.insert("source", fileURI); + map.insert("target", _url); + _plugin->cb(_ecId, map); + emit done(); + return; + } + + QPair f1(dynamic_cast(filePlugin.data())->resolveURI(fileURI)); + QFile file(f1.second.absoluteFilePath()); + if (!f1.first || !file.open(QIODevice::ReadOnly)) { + QVariantMap map; + map.insert("code", FILE_NOT_FOUND_ERR); + map.insert("source", fileURI); + map.insert("target", _url); + _plugin->cb(_ecId, map); + emit done(); + return; + } + QString content{file.readAll()}; + + request.setUrl(url); + if (url.password().size() || url.userName().size()) { + QString headerData = "Basic " + (url.userName() + ":" + url.password()).toLocal8Bit().toBase64(); + request.setRawHeader("Authorization", headerData.toLocal8Bit()); + } + SetHeaders(request, headers); + + QString boundary = QString("CORDOVA-QT-%1A").arg(qrand()); + while (content.contains(boundary)) { + boundary += QString("B%1A").arg(qrand()); + } + + request.setHeader(QNetworkRequest::ContentTypeHeader, QString("multipart/form-data; boundary=") + boundary); + + fileKey.replace("\"", ""); + fileName.replace("\"", ""); + mimeType.replace("\"", ""); + QString part = "--" + boundary + "\r\n"; + + part += "Content-Disposition: form-data; name=\"" + fileKey +"\"; filename=\"" + fileName + "\"\r\n"; + part += "Content-Type: " + mimeType + "\r\n\r\n"; + part += content + "\r\n"; + + for (QString key: params.keys()) { + part += "--" + boundary + "\r\n"; + part += "Content-Disposition: form-data; name=\"" + key + "\";\r\n\r\n"; + part += params.find(key)->toString(); + part += "\r\n"; + } + + part += QString("--") + boundary + "--" + "\r\n"; + + _reply = QSharedPointer(_manager.post(request, QByteArray(part.toUtf8()))); + + _reply->connect(_reply.data(), &QNetworkReply::finished, [this, content]() { + if (_reply->error() != QNetworkReply::NoError) + return; + int status = 200; + QVariant statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + + if (statusCode.isValid()) { + status = statusCode.toInt(); + } + + QVariantMap map; + map.insert("responseCode", status); + map.insert("response", QString(_reply->readAll())); + map.insert("bytesSent", content.size()); + _plugin->cb(_scId, map); + emit done(); + }); + _reply->connect(_reply.data(), SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError))); + _reply->connect(_reply.data(), SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(progress(qint64, qint64))); +} + +void FileTransferRequest::abort() { + QVariantMap map; + map.insert("code", ABORT_ERR); + _plugin->cb(_ecId, map); + _scId = 0; + emit done(); +} + +void FileTransferRequest::error(QNetworkReply::NetworkError code) { + Q_UNUSED(code); + + int status = 404; + QVariant statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + if (statusCode.isValid()) { + status = statusCode.toInt(); + } + + QVariantMap map; + map.insert("http_status", status); + map.insert("body", QString(_reply->readAll())); + map.insert("code", CONNECTION_ERR); + _plugin->cb(_ecId, map); + emit done(); +} + +void FileTransferRequest::progress(qint64 bytesReceived, qint64 bytesTotal) { + QVariantMap map; + map.insert("lengthComputable", true); + map.insert("total", bytesTotal); + map.insert("loaded", bytesReceived); + + if (bytesReceived && bytesTotal && _scId) + _plugin->callbackWithoutRemove(_scId, CordovaInternal::format(map)); +} diff --git a/plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.h b/plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.h new file mode 100644 index 0000000..75822cb --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.h @@ -0,0 +1,103 @@ +/* + * + * Copyright 2013 Canonical Ltd. + * + * 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. + * +*/ + +#ifndef FILE_TRANSFER_H_SDASDASDAS +#define FILE_TRANSFER_H_SDASDASDAS + +#include +#include + +#include + +class FileTransfer; + +class FileTransferRequest: public QObject { + Q_OBJECT + + QNetworkAccessManager &_manager; + int _scId, _ecId; + int _id; + QSharedPointer _reply; + + enum FileTransferError { + FILE_NOT_FOUND_ERR = 1, + INVALID_URL_ERR = 2, + CONNECTION_ERR = 3, + ABORT_ERR = 4 + }; + +public: + FileTransferRequest(QNetworkAccessManager &manager, int scId, int ecId, int id, FileTransfer *plugin): + _manager(manager), + _scId(scId), + _ecId(ecId), + _id(id), + _plugin(plugin) { + } + + void download(const QString& url, const QString &targetURI, const QVariantMap &headers); + void upload(const QString& _url, const QString& fileURI, QString fileKey, QString fileName, QString mimeType, const QVariantMap ¶ms, const QVariantMap &headers); + void abort(); + +signals: + void done(); + +private slots: + void progress(qint64 bytesReceived, qint64 bytesTotal); + void error(QNetworkReply::NetworkError code); +private: + FileTransfer *_plugin; + Q_DISABLE_COPY(FileTransferRequest); +}; + +class FileTransfer : public CPlugin { + Q_OBJECT +public: + explicit FileTransfer(Cordova *cordova): CPlugin(cordova) { + } + + Cordova* cordova() { + return m_cordova; + } + + virtual const QString fullName() override { + return FileTransfer::fullID(); + } + + virtual const QString shortName() override { + return "FileTransfer"; + } + + static const QString fullID() { + return "FileTransfer"; + } + +public slots: + void abort(int scId, int ecId, int id); + void download(int scId, int ecId, const QString& url, const QString &target, bool /*trustAllHost*/, int id, const QVariantMap &/*headers*/); + void upload(int scId, int ecId, const QString &filePath, const QString& url, const QString& fileKey, const QString& fileName, const QString& mimeType, + const QVariantMap & params, bool /*trustAllHosts*/, bool /*chunkedMode*/, const QVariantMap &headers, int id, const QString &httpMethod); + +private: + QNetworkAccessManager _manager; + QMultiMap > _id2request; + int lastRequestId; +}; + +#endif diff --git a/plugins/cordova-plugin-file-transfer/src/windows/FileTransferProxy.js b/plugins/cordova-plugin-file-transfer/src/windows/FileTransferProxy.js new file mode 100644 index 0000000..56df315 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/windows/FileTransferProxy.js @@ -0,0 +1,578 @@ +/* + * + * 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. + * +*/ + +/*jshint -W030 */ +/*global Windows, WinJS*/ +/*global module, require*/ + +var FTErr = require('./FileTransferError'), + ProgressEvent = require('cordova-plugin-file.ProgressEvent'), + FileUploadResult = require('cordova-plugin-file.FileUploadResult'), + FileProxy = require('cordova-plugin-file.FileProxy'); + +var appData = Windows.Storage.ApplicationData.current; + +var LINE_START = "--"; +var LINE_END = "\r\n"; +var BOUNDARY = '+++++'; + +var fileTransferOps = []; + +// Some private helper functions, hidden by the module +function cordovaPathToNative(path) { + + var cleanPath = String(path); + // turn / into \\ + cleanPath = cleanPath.replace(/\//g, '\\'); + // turn \\ into \ + cleanPath = cleanPath.replace(/\\\\/g, '\\'); + // strip end \\ characters + cleanPath = cleanPath.replace(/\\+$/g, ''); + return cleanPath; +} + +function nativePathToCordova(path) { + return String(path).replace(/\\/g, '/'); +} + +function alreadyCancelled(opId) { + var op = fileTransferOps[opId]; + return op && op.state === FileTransferOperation.CANCELLED; +} + +function doUpload (upload, uploadId, filePath, server, successCallback, errorCallback) { + if (alreadyCancelled(uploadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); + return; + } + + // update internal TransferOperation object with newly created promise + var uploadOperation = upload.startAsync(); + fileTransferOps[uploadId].promise = uploadOperation; + + uploadOperation.then( + function (result) { + // Update TransferOperation object with new state, delete promise property + // since it is not actual anymore + var currentUploadOp = fileTransferOps[uploadId]; + if (currentUploadOp) { + currentUploadOp.state = FileTransferOperation.DONE; + currentUploadOp.promise = null; + } + + var response = result.getResponseInformation(); + var ftResult = new FileUploadResult(result.progress.bytesSent, response.statusCode, ''); + + // if server's response doesn't contain any data, then resolve operation now + if (result.progress.bytesReceived === 0) { + successCallback(ftResult); + return; + } + + // otherwise create a data reader, attached to response stream to get server's response + var reader = new Windows.Storage.Streams.DataReader(result.getResultStreamAt(0)); + reader.loadAsync(result.progress.bytesReceived).then(function (size) { + ftResult.response = reader.readString(size); + successCallback(ftResult); + reader.close(); + }); + }, + function (error) { + var source = nativePathToCordova(filePath); + + // Handle download error here. + // Wrap this routines into promise due to some async methods + var getTransferError = new WinJS.Promise(function (resolve) { + if (error.message === 'Canceled') { + // If download was cancelled, message property will be specified + resolve(new FTErr(FTErr.ABORT_ERR, source, server, null, null, error)); + } else { + // in the other way, try to get response property + var response = upload.getResponseInformation(); + if (!response) { + resolve(new FTErr(FTErr.CONNECTION_ERR, source, server)); + } else { + var reader = new Windows.Storage.Streams.DataReader(upload.getResultStreamAt(0)); + reader.loadAsync(upload.progress.bytesReceived).then(function (size) { + var responseText = reader.readString(size); + resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, server, response.statusCode, responseText, error)); + reader.close(); + }); + } + } + }); + + // Update TransferOperation object with new state, delete promise property + // since it is not actual anymore + var currentUploadOp = fileTransferOps[uploadId]; + if (currentUploadOp) { + currentUploadOp.state = FileTransferOperation.CANCELLED; + currentUploadOp.promise = null; + } + + // Report the upload error back + getTransferError.then(function (transferError) { + errorCallback(transferError); + }); + }, + function (evt) { + var progressEvent = new ProgressEvent('progress', { + loaded: evt.progress.bytesSent, + total: evt.progress.totalBytesToSend, + target: evt.resultFile + }); + progressEvent.lengthComputable = true; + successCallback(progressEvent, { keepCallback: true }); + } + ); +} + +function FileTransferOperation(state, promise) { + this.state = state; + this.promise = promise; +} + +FileTransferOperation.PENDING = 0; +FileTransferOperation.DONE = 1; +FileTransferOperation.CANCELLED = 2; + +var HTTP_E_STATUS_NOT_MODIFIED = -2145844944; + +module.exports = { + +/* +exec(win, fail, 'FileTransfer', 'upload', +[filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]); +*/ + upload: function (successCallback, errorCallback, options) { + var filePath = options[0]; + var server = options[1]; + var fileKey = options[2] || 'source'; + var fileName = options[3]; + var mimeType = options[4]; + var params = options[5]; + // var trustAllHosts = options[6]; // todo + // var chunkedMode = options[7]; // todo + var headers = options[8] || {}; + var uploadId = options[9]; + var httpMethod = options[10]; + + var isMultipart = typeof headers["Content-Type"] === 'undefined'; + + function stringToByteArray(str) { + var byteCharacters = atob(str); + var byteNumbers = new Array(byteCharacters.length); + for (var i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + return new Uint8Array(byteNumbers); + } + + if (!filePath || (typeof filePath !== 'string')) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, null, server)); + return; + } + + if (filePath.indexOf("data:") === 0 && filePath.indexOf("base64") !== -1) { + // First a DataWriter object is created, backed by an in-memory stream where + // the data will be stored. + var writer = Windows.Storage.Streams.DataWriter(new Windows.Storage.Streams.InMemoryRandomAccessStream()); + writer.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.utf8; + writer.byteOrder = Windows.Storage.Streams.ByteOrder.littleEndian; + + var commaIndex = filePath.indexOf(","); + if (commaIndex === -1) { + errorCallback(new FTErr(FTErr.INVALID_URL_ERR, fileName, server, null, null, "No comma in data: URI")); + return; + } + + // Create internal download operation object + fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null); + + var fileDataString = filePath.substr(commaIndex + 1); + + // setting request headers for uploader + var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); + uploader.method = httpMethod; + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + uploader.setRequestHeader(header, headers[header]); + } + } + + if (isMultipart) { + // adding params supplied to request payload + var multipartParams = ''; + for (var key in params) { + if (params.hasOwnProperty(key)) { + multipartParams += LINE_START + BOUNDARY + LINE_END; + multipartParams += "Content-Disposition: form-data; name=\"" + key + "\""; + multipartParams += LINE_END + LINE_END; + multipartParams += params[key]; + multipartParams += LINE_END; + } + } + + var multipartFile = LINE_START + BOUNDARY + LINE_END; + multipartFile += "Content-Disposition: form-data; name=\"file\";"; + multipartFile += " filename=\"" + fileName + "\"" + LINE_END; + multipartFile += "Content-Type: " + mimeType + LINE_END + LINE_END; + + var bound = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END; + + uploader.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + writer.writeString(multipartParams); + writer.writeString(multipartFile); + writer.writeBytes(stringToByteArray(fileDataString)); + writer.writeString(bound); + } else { + writer.writeBytes(stringToByteArray(fileDataString)); + } + + var stream; + + // The call to store async sends the actual contents of the writer + // to the backing stream. + writer.storeAsync().then(function () { + // For the in-memory stream implementation we are using, the flushAsync call + // is superfluous, but other types of streams may require it. + return writer.flushAsync(); + }).then(function () { + // We detach the stream to prolong its useful lifetime. Were we to fail + // to detach the stream, the call to writer.close() would close the underlying + // stream, preventing its subsequent use by the DataReader below. Most clients + // of DataWriter will have no reason to use the underlying stream after + // writer.close() is called, and will therefore have no reason to call + // writer.detachStream(). Note that once we detach the stream, we assume + // responsibility for closing the stream subsequently; after the stream + // has been detached, a call to writer.close() will have no effect on the stream. + stream = writer.detachStream(); + // Make sure the stream is read from the beginning in the reader + // we are creating below. + stream.seek(0); + // Most DataWriter clients will not call writer.detachStream(), + // and furthermore will be working with a file-backed or network-backed stream, + // rather than an in-memory-stream. In such cases, it would be particularly + // important to call writer.close(). Doing so is always a best practice. + writer.close(); + + if (alreadyCancelled(uploadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); + return; + } + + // create download object. This will throw an exception if URL is malformed + var uri = new Windows.Foundation.Uri(server); + + var createUploadOperation; + try { + createUploadOperation = uploader.createUploadFromStreamAsync(uri, stream); + } catch (e) { + errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); + return; + } + + createUploadOperation.then( + function (upload) { + doUpload(upload, uploadId, filePath, server, successCallback, errorCallback); + }, + function (err) { + var errorObj = new FTErr(FTErr.INVALID_URL_ERR); + errorObj.exception = err; + errorCallback(errorObj); + }); + }); + + return; + } + + if (filePath.substr(0, 8) === "file:///") { + filePath = appData.localFolder.path + filePath.substr(8).split("/").join("\\"); + } else if (filePath.indexOf('ms-appdata:///') === 0) { + // Handle 'ms-appdata' scheme + filePath = filePath.replace('ms-appdata:///local', appData.localFolder.path) + .replace('ms-appdata:///temp', appData.temporaryFolder.path); + } else if (filePath.indexOf('cdvfile://') === 0) { + filePath = filePath.replace('cdvfile://localhost/persistent', appData.localFolder.path) + .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path); + } + + // normalize path separators + filePath = cordovaPathToNative(filePath); + + // Create internal download operation object + fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null); + + Windows.Storage.StorageFile.getFileFromPathAsync(filePath) + .then(function (storageFile) { + + if (!fileName) { + fileName = storageFile.name; + } + if (!mimeType) { + // use the actual content type of the file, probably this should be the default way. + // other platforms probably can't look this up. + mimeType = storageFile.contentType; + } + + if (alreadyCancelled(uploadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); + return; + } + + // setting request headers for uploader + var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); + uploader.method = httpMethod; + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + uploader.setRequestHeader(header, headers[header]); + } + } + + // create download object. This will throw an exception if URL is malformed + var uri = new Windows.Foundation.Uri(server); + + var createUploadOperation; + try { + if (isMultipart) { + // adding params supplied to request payload + var transferParts = []; + for (var key in params) { + // Create content part for params only if value is specified because CreateUploadAsync fails otherwise + if (params.hasOwnProperty(key) && params[key] !== null && params[key] !== undefined && params[key].toString() !== "") { + var contentPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(); + contentPart.setHeader("Content-Disposition", "form-data; name=\"" + key + "\""); + contentPart.setText(params[key]); + transferParts.push(contentPart); + } + } + + // Adding file to upload to request payload + var fileToUploadPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, fileName); + fileToUploadPart.setFile(storageFile); + transferParts.push(fileToUploadPart); + + createUploadOperation = uploader.createUploadAsync(uri, transferParts); + } else { + createUploadOperation = WinJS.Promise.wrap(uploader.createUpload(uri, storageFile)); + } + } catch (e) { + errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); + return; + } + + createUploadOperation.then( + function (upload) { + doUpload(upload, uploadId, filePath, server, successCallback, errorCallback); + }, + function (err) { + var errorObj = new FTErr(FTErr.INVALID_URL_ERR); + errorObj.exception = err; + errorCallback(errorObj); + } + ); + }, function (err) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, server, null, null, err)); + }); + }, + + // [source, target, trustAllHosts, id, headers] + download:function(successCallback, errorCallback, options) { + var source = options[0]; + var target = options[1]; + var downloadId = options[3]; + var headers = options[4] || {}; + + if (!target) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR)); + return; + } + if (target.substr(0, 8) === "file:///") { + target = appData.localFolder.path + target.substr(8).split("/").join("\\"); + } else if (target.indexOf('ms-appdata:///') === 0) { + // Handle 'ms-appdata' scheme + target = target.replace('ms-appdata:///local', appData.localFolder.path) + .replace('ms-appdata:///temp', appData.temporaryFolder.path); + } else if (target.indexOf('cdvfile://') === 0) { + target = target.replace('cdvfile://localhost/persistent', appData.localFolder.path) + .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path); + } + target = cordovaPathToNative(target); + + var path = target.substr(0, target.lastIndexOf("\\")); + var fileName = target.substr(target.lastIndexOf("\\") + 1); + if (path === null || fileName === null) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR)); + return; + } + // Download to a temp file to avoid the file deletion on 304 + // CB-7006 Empty file is created on file transfer if server response is 304 + var tempFileName = '~' + fileName; + + var download = null; + + // Create internal download operation object + fileTransferOps[downloadId] = new FileTransferOperation(FileTransferOperation.PENDING, null); + + var downloadCallback = function(storageFolder) { + storageFolder.createFileAsync(tempFileName, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (storageFile) { + + if (alreadyCancelled(downloadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, source, target)); + return; + } + + // if download isn't cancelled, contunue with creating and preparing download operation + var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader(); + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + downloader.setRequestHeader(header, headers[header]); + } + } + + // create download object. This will throw an exception if URL is malformed + try { + var uri = Windows.Foundation.Uri(source); + download = downloader.createDownload(uri, storageFile); + } catch (e) { + // so we handle this and call errorCallback + errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); + return; + } + + var downloadOperation = download.startAsync(); + // update internal TransferOperation object with newly created promise + fileTransferOps[downloadId].promise = downloadOperation; + + downloadOperation.then(function () { + + // Update TransferOperation object with new state, delete promise property + // since it is not actual anymore + var currentDownloadOp = fileTransferOps[downloadId]; + if (currentDownloadOp) { + currentDownloadOp.state = FileTransferOperation.DONE; + currentDownloadOp.promise = null; + } + + storageFile.renameAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(function () { + var nativeURI = storageFile.path.replace(appData.localFolder.path, 'ms-appdata:///local') + .replace(appData.temporaryFolder.path, 'ms-appdata:///temp') + .replace(/\\/g, '/'); + + // Passing null as error callback here because downloaded file should exist in any case + // otherwise the error callback will be hit during file creation in another place + FileProxy.resolveLocalFileSystemURI(successCallback, null, [nativeURI]); + }, function(error) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); + }); + }, function(error) { + + var getTransferError = new WinJS.Promise(function (resolve) { + // Handle download error here. If download was cancelled, + // message property will be specified + if (error.message === 'Canceled') { + resolve(new FTErr(FTErr.ABORT_ERR, source, target, null, null, error)); + } else if (error && error.number === HTTP_E_STATUS_NOT_MODIFIED) { + resolve(new FTErr(FTErr.NOT_MODIFIED_ERR, source, target, 304, null, error)); + } else { + // in the other way, try to get response property + var response = download.getResponseInformation(); + if (!response) { + resolve(new FTErr(FTErr.CONNECTION_ERR, source, target)); + } else { + var reader = new Windows.Storage.Streams.DataReader(download.getResultStreamAt(0)); + reader.loadAsync(download.progress.bytesReceived).then(function (bytesLoaded) { + var payload = reader.readString(bytesLoaded); + resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, payload, error)); + }); + } + } + }); + getTransferError.then(function (fileTransferError) { + + // Update TransferOperation object with new state, delete promise property + // since it is not actual anymore + var currentDownloadOp = fileTransferOps[downloadId]; + if (currentDownloadOp) { + currentDownloadOp.state = FileTransferOperation.CANCELLED; + currentDownloadOp.promise = null; + } + + // Cleanup, remove incompleted file + storageFile.deleteAsync().then(function() { + errorCallback(fileTransferError); + }); + }); + + }, function(evt) { + + var progressEvent = new ProgressEvent('progress', { + loaded: evt.progress.bytesReceived, + total: evt.progress.totalBytesToReceive, + target: evt.resultFile + }); + // when bytesReceived == 0, BackgroundDownloader has not yet differentiated whether it could get file length or not, + // when totalBytesToReceive == 0, BackgroundDownloader is unable to get file length + progressEvent.lengthComputable = (evt.progress.bytesReceived > 0) && (evt.progress.totalBytesToReceive > 0); + + successCallback(progressEvent, { keepCallback: true }); + }); + }, function(error) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); + }); + }; + + var fileNotFoundErrorCallback = function(error) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); + }; + + Windows.Storage.StorageFolder.getFolderFromPathAsync(path).then(downloadCallback, function (error) { + // Handle non-existent directory + if (error.number === -2147024894) { + var parent = path.substr(0, path.lastIndexOf('\\')), + folderNameToCreate = path.substr(path.lastIndexOf('\\') + 1); + + Windows.Storage.StorageFolder.getFolderFromPathAsync(parent).then(function(parentFolder) { + parentFolder.createFolderAsync(folderNameToCreate).then(downloadCallback, fileNotFoundErrorCallback); + }, fileNotFoundErrorCallback); + } else { + fileNotFoundErrorCallback(); + } + }); + }, + + abort: function (successCallback, error, options) { + var fileTransferOpId = options[0]; + + // Try to find transferOperation with id specified, and cancel its' promise + var currentOp = fileTransferOps[fileTransferOpId]; + if (currentOp) { + currentOp.state = FileTransferOperation.CANCELLED; + currentOp.promise && currentOp.promise.cancel(); + } else if (typeof fileTransferOpId !== 'undefined') { + // Create the operation in cancelled state to be aborted right away + fileTransferOps[fileTransferOpId] = new FileTransferOperation(FileTransferOperation.CANCELLED, null); + } + } + +}; + +require("cordova/exec/proxy").add("FileTransfer",module.exports); diff --git a/plugins/cordova-plugin-file-transfer/src/wp/FileTransfer.cs b/plugins/cordova-plugin-file-transfer/src/wp/FileTransfer.cs new file mode 100644 index 0000000..4be46e8 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/src/wp/FileTransfer.cs @@ -0,0 +1,994 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using Microsoft.Phone.Controls; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.IsolatedStorage; +using System.Linq; +using System.Net; +using System.Runtime.Serialization; +using System.Windows; +using System.Security; +using System.Diagnostics; +using System.Threading.Tasks; +using WPCordovaClassLib.Cordova.JSON; +using System.Reflection; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class FileTransfer : BaseCommand + { + public class DownloadRequestState + { + // This class stores the State of the request. + public HttpWebRequest request; + public TransferOptions options; + public bool isCancelled; + + public DownloadRequestState() + { + request = null; + options = null; + isCancelled = false; + } + } + + public class TransferOptions + { + /// File path to upload OR File path to download to + public string FilePath { get; set; } + + public string Url { get; set; } + /// Flag to recognize if we should trust every host (only in debug environments) + public bool TrustAllHosts { get; set; } + public string Id { get; set; } + public string Headers { get; set; } + public string CallbackId { get; set; } + public bool ChunkedMode { get; set; } + /// Server address + public string Server { get; set; } + /// File key + public string FileKey { get; set; } + /// File name on the server + public string FileName { get; set; } + /// File Mime type + public string MimeType { get; set; } + /// Additional options + public string Params { get; set; } + public string Method { get; set; } + + public TransferOptions() + { + FileKey = "file"; + FileName = "image.jpg"; + MimeType = "image/jpeg"; + } + } + + /// + /// Boundary symbol + /// + private string Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x"); + + // Error codes + public const int FileNotFoundError = 1; + public const int InvalidUrlError = 2; + public const int ConnectionError = 3; + public const int AbortError = 4; // not really an error, but whatevs + + private static Dictionary InProcDownloads = new Dictionary(); + + // Private instance of the main WebBrowser instance + // NOTE: Any access to this object needs to occur on the UI thread via the Dispatcher + private WebBrowser browser; + + + + /// + /// Uploading response info + /// + [DataContract] + public class FileUploadResult + { + /// + /// Amount of sent bytes + /// + [DataMember(Name = "bytesSent")] + public long BytesSent { get; set; } + + /// + /// Server response code + /// + [DataMember(Name = "responseCode")] + public long ResponseCode { get; set; } + + /// + /// Server response + /// + [DataMember(Name = "response", EmitDefaultValue = false)] + public string Response { get; set; } + + /// + /// Creates FileUploadResult object with response values + /// + /// Amount of sent bytes + /// Server response code + /// Server response + public FileUploadResult(long bytesSent, long responseCode, string response) + { + this.BytesSent = bytesSent; + this.ResponseCode = responseCode; + this.Response = response; + } + } + /// + /// Represents transfer error codes for callback + /// + [DataContract] + public class FileTransferError + { + /// + /// Error code + /// + [DataMember(Name = "code", IsRequired = true)] + public int Code { get; set; } + + /// + /// The source URI + /// + [DataMember(Name = "source", IsRequired = true)] + public string Source { get; set; } + + /// + /// The target URI + /// + /// + [DataMember(Name = "target", IsRequired = true)] + public string Target { get; set; } + + [DataMember(Name = "body", IsRequired = true)] + public string Body { get; set; } + + /// + /// The http status code response from the remote URI + /// + [DataMember(Name = "http_status", IsRequired = true)] + public int HttpStatus { get; set; } + + /// + /// Creates FileTransferError object + /// + /// Error code + public FileTransferError(int errorCode) + { + this.Code = errorCode; + this.Source = null; + this.Target = null; + this.HttpStatus = 0; + this.Body = ""; + } + public FileTransferError(int errorCode, string source, string target, int status, string body = "") + { + this.Code = errorCode; + this.Source = source; + this.Target = target; + this.HttpStatus = status; + this.Body = body; + } + } + + /// + /// Represents a singular progress event to be passed back to javascript + /// + [DataContract] + public class FileTransferProgress + { + /// + /// Is the length of the response known? + /// + [DataMember(Name = "lengthComputable", IsRequired = true)] + public bool LengthComputable { get; set; } + /// + /// amount of bytes loaded + /// + [DataMember(Name = "loaded", IsRequired = true)] + public long BytesLoaded { get; set; } + /// + /// Total bytes + /// + [DataMember(Name = "total", IsRequired = false)] + public long BytesTotal { get; set; } + + public FileTransferProgress(long bTotal = 0, long bLoaded = 0) + { + LengthComputable = bTotal > 0; + BytesLoaded = bLoaded; + BytesTotal = bTotal; + } + } + + /// + /// Represents a request header passed from Javascript to upload/download operations + /// + [DataContract] + protected struct Header + { + [DataMember(Name = "name")] + public string Name; + + [DataMember(Name = "value")] + public string Value; + } + + private static MethodInfo JsonDeserializeUsingJsonNet; + + public FileTransfer() + { + if (JsonDeserializeUsingJsonNet == null) + { + var method = typeof(JsonHelper).GetMethod("Deserialize", new Type[] { typeof(string), typeof(bool) }); + if (method != null) + { + JsonDeserializeUsingJsonNet = method.MakeGenericMethod(new Type[] { typeof(Header[]) }); + } + } + } + + /// Helper method to copy all relevant cookies from the WebBrowser control into a header on + /// the HttpWebRequest + /// + /// The source browser to copy the cookies from + /// The destination HttpWebRequest to add the cookie header to + /// Nothing + private async Task CopyCookiesFromWebBrowser(HttpWebRequest webRequest) + { + var tcs = new TaskCompletionSource(); + + // Accessing WebBrowser needs to happen on the UI thread + Deployment.Current.Dispatcher.BeginInvoke(() => + { + // Get the WebBrowser control + if (this.browser == null) + { + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + if (frame != null) + { + PhoneApplicationPage page = frame.Content as PhoneApplicationPage; + if (page != null) + { + CordovaView cView = page.FindName("CordovaView") as CordovaView; + if (cView != null) + { + this.browser = cView.Browser; + } + } + } + } + + try + { + // Only copy the cookies if the scheme and host match (to avoid any issues with secure/insecure cookies) + // NOTE: since the returned CookieCollection appears to munge the original cookie's domain value in favor of the actual Source domain, + // we can't know for sure whether the cookies would be applicable to any other hosts, so best to play it safe and skip for now. + if (this.browser != null && this.browser.Source.IsAbsoluteUri == true && + this.browser.Source.Scheme == webRequest.RequestUri.Scheme && this.browser.Source.Host == webRequest.RequestUri.Host) + { + string cookieHeader = ""; + string requestPath = webRequest.RequestUri.PathAndQuery; + CookieCollection cookies = this.browser.GetCookies(); + + // Iterate over the cookies and add to the header + foreach (Cookie cookie in cookies) + { + // Check that the path is allowed, first + // NOTE: Path always seems to be empty for now, even if the cookie has a path set by the server. + if (cookie.Path.Length == 0 || requestPath.IndexOf(cookie.Path, StringComparison.InvariantCultureIgnoreCase) == 0) + { + cookieHeader += cookie.Name + "=" + cookie.Value + "; "; + } + } + + // Finally, set the header if we found any cookies + if (cookieHeader.Length > 0) + { + webRequest.Headers["Cookie"] = cookieHeader; + } + } + } + catch (Exception) + { + // Swallow the exception + } + + // Complete the task + tcs.SetResult(Type.Missing); + }); + + await tcs.Task; + } + + /// + /// Upload options + /// + //private TransferOptions uploadOptions; + + /// + /// Bytes sent + /// + private long bytesSent; + + /// + /// sends a file to a server + /// + /// Upload options + /// exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]); + public void upload(string options) + { + options = options.Replace("{}", ""); // empty objects screw up the Deserializer + string callbackId = ""; + + TransferOptions uploadOptions = null; + HttpWebRequest webRequest = null; + + try + { + try + { + string[] args = JSON.JsonHelper.Deserialize(options); + uploadOptions = new TransferOptions(); + uploadOptions.FilePath = args[0]; + uploadOptions.Server = args[1]; + uploadOptions.FileKey = args[2]; + uploadOptions.FileName = args[3]; + uploadOptions.MimeType = args[4]; + uploadOptions.Params = args[5]; + + bool trustAll = false; + bool.TryParse(args[6],out trustAll); + uploadOptions.TrustAllHosts = trustAll; + + bool doChunked = false; + bool.TryParse(args[7], out doChunked); + uploadOptions.ChunkedMode = doChunked; + + //8 : Headers + //9 : id + //10: method + + uploadOptions.Headers = args[8]; + uploadOptions.Id = args[9]; + uploadOptions.Method = args[10]; + + uploadOptions.CallbackId = callbackId = args[11]; + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + Uri serverUri; + try + { + serverUri = new Uri(uploadOptions.Server); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, uploadOptions.Server, null, 0))); + return; + } + webRequest = (HttpWebRequest)WebRequest.Create(serverUri); + webRequest.ContentType = "multipart/form-data; boundary=" + Boundary; + webRequest.Method = uploadOptions.Method; + + DownloadRequestState reqState = new DownloadRequestState(); + InProcDownloads[uploadOptions.Id] = reqState; + reqState.options = uploadOptions; + reqState.request = webRequest; + + try + { + // Associate cookies with the request + // This is an async call, so we need to await it in order to preserve proper control flow + Task cookieTask = CopyCookiesFromWebBrowser(webRequest); + cookieTask.Wait(); + } + catch (AggregateException ae) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(FileTransfer.ConnectionError, uploadOptions.FilePath, uploadOptions.Server, 0, ae.InnerException.Message))); + return; + } + + if (!string.IsNullOrEmpty(uploadOptions.Headers)) + { + Dictionary headers = parseHeaders(uploadOptions.Headers); + if (headers != null) + { + foreach (string key in headers.Keys) + { + webRequest.Headers[key] = headers[key]; + } + } + } + + webRequest.BeginGetRequestStream(uploadCallback, reqState); + } + catch (Exception /*ex*/) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)),callbackId); + } + } + + // example : "{\"Authorization\":\"Basic Y29yZG92YV91c2VyOmNvcmRvdmFfcGFzc3dvcmQ=\"}" + protected Dictionary parseHeaders(string jsonHeaders) + { + try + { + if (FileTransfer.JsonDeserializeUsingJsonNet != null) + { + return ((Header[])FileTransfer.JsonDeserializeUsingJsonNet.Invoke(null, new object[] { jsonHeaders, true })) + .ToDictionary(header => header.Name, header => header.Value); + } + else + { + return JsonHelper.Deserialize(jsonHeaders) + .ToDictionary(header => header.Name, header => header.Value); + } + } + catch (Exception) + { + Debug.WriteLine("Failed to parseHeaders from string :: " + jsonHeaders); + } + return new Dictionary(); + } + + public void download(string options) + { + TransferOptions downloadOptions = null; + HttpWebRequest webRequest = null; + string callbackId; + + try + { + // source, target, trustAllHosts, this._id, headers + string[] optionStrings = JSON.JsonHelper.Deserialize(options); + + downloadOptions = new TransferOptions(); + downloadOptions.Url = optionStrings[0]; + downloadOptions.FilePath = optionStrings[1]; + + bool trustAll = false; + bool.TryParse(optionStrings[2],out trustAll); + downloadOptions.TrustAllHosts = trustAll; + + downloadOptions.Id = optionStrings[3]; + downloadOptions.Headers = optionStrings[4]; + downloadOptions.CallbackId = callbackId = optionStrings[5]; + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + try + { + // is the URL a local app file? + if (downloadOptions.Url.StartsWith("x-wmapp0") || downloadOptions.Url.StartsWith("file:")) + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + string cleanUrl = downloadOptions.Url.Replace("x-wmapp0:", "").Replace("file:", "").Replace("//",""); + + // pre-emptively create any directories in the FilePath that do not exist + string directoryName = getDirectoryName(downloadOptions.FilePath); + if (!string.IsNullOrEmpty(directoryName) && !isoFile.DirectoryExists(directoryName)) + { + isoFile.CreateDirectory(directoryName); + } + + // just copy from one area of iso-store to another ... + if (isoFile.FileExists(downloadOptions.Url)) + { + isoFile.CopyFile(downloadOptions.Url, downloadOptions.FilePath); + } + else + { + // need to unpack resource from the dll + Uri uri = new Uri(cleanUrl, UriKind.Relative); + var resource = Application.GetResourceStream(uri); + + if (resource != null) + { + // create the file destination + if (!isoFile.FileExists(downloadOptions.FilePath)) + { + var destFile = isoFile.CreateFile(downloadOptions.FilePath); + destFile.Close(); + } + + using (FileStream fileStream = new IsolatedStorageFileStream(downloadOptions.FilePath, FileMode.Open, FileAccess.Write, isoFile)) + { + long totalBytes = resource.Stream.Length; + int bytesRead = 0; + using (BinaryReader reader = new BinaryReader(resource.Stream)) + { + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + int BUFFER_SIZE = 1024; + byte[] buffer; + + while (true) + { + buffer = reader.ReadBytes(BUFFER_SIZE); + // fire a progress event ? + bytesRead += buffer.Length; + if (buffer.Length > 0) + { + writer.Write(buffer); + DispatchFileTransferProgress(bytesRead, totalBytes, callbackId); + } + else + { + writer.Close(); + reader.Close(); + fileStream.Close(); + break; + } + } + } + } + } + } + } + } + + File.FileEntry entry = File.FileEntry.GetEntry(downloadOptions.FilePath); + if (entry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, File.NOT_FOUND_ERR), callbackId); + } + + return; + } + else + { + // otherwise it is web-bound, we will actually download it + //Debug.WriteLine("Creating WebRequest for url : " + downloadOptions.Url); + webRequest = (HttpWebRequest)WebRequest.Create(downloadOptions.Url); + } + } + catch (Exception /*ex*/) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(InvalidUrlError, downloadOptions.Url, null, 0))); + return; + } + + if (downloadOptions != null && webRequest != null) + { + DownloadRequestState state = new DownloadRequestState(); + state.options = downloadOptions; + state.request = webRequest; + InProcDownloads[downloadOptions.Id] = state; + + try + { + // Associate cookies with the request + // This is an async call, so we need to await it in order to preserve proper control flow + Task cookieTask = CopyCookiesFromWebBrowser(webRequest); + cookieTask.Wait(); + } + catch (AggregateException ae) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(FileTransfer.ConnectionError, downloadOptions.Url, downloadOptions.FilePath, 0, ae.InnerException.Message))); + return; + } + + if (!string.IsNullOrEmpty(downloadOptions.Headers)) + { + Dictionary headers = parseHeaders(downloadOptions.Headers); + foreach (string key in headers.Keys) + { + webRequest.Headers[key] = headers[key]; + } + } + + try + { + webRequest.BeginGetResponse(new AsyncCallback(downloadCallback), state); + } + catch (WebException) + { + // eat it + } + // dispatch an event for progress ( 0 ) + lock (state) + { + if (!state.isCancelled) + { + var plugRes = new PluginResult(PluginResult.Status.OK, new FileTransferProgress()); + plugRes.KeepCallback = true; + plugRes.CallbackId = callbackId; + DispatchCommandResult(plugRes, callbackId); + } + } + } + } + + public void abort(string options) + { + Debug.WriteLine("Abort :: " + options); + string[] optionStrings = JSON.JsonHelper.Deserialize(options); + string id = optionStrings[0]; + string callbackId = optionStrings[1]; + + if (id != null && InProcDownloads.ContainsKey(id)) + { + DownloadRequestState state = InProcDownloads[id]; + if (!state.isCancelled) + { // prevent multiple callbacks for the same abort + state.isCancelled = true; + if (!state.request.HaveResponse) + { + state.request.Abort(); + InProcDownloads.Remove(id); + //callbackId = state.options.CallbackId; + //state = null; + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(FileTransfer.AbortError)), + state.options.CallbackId); + } + } + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION), callbackId); // TODO: is it an IO exception? + } + } + + private void DispatchFileTransferProgress(long bytesLoaded, long bytesTotal, string callbackId, bool keepCallback = true) + { + Debug.WriteLine("DispatchFileTransferProgress : " + callbackId); + // send a progress change event + FileTransferProgress progEvent = new FileTransferProgress(bytesTotal); + progEvent.BytesLoaded = bytesLoaded; + PluginResult plugRes = new PluginResult(PluginResult.Status.OK, progEvent); + plugRes.KeepCallback = keepCallback; + plugRes.CallbackId = callbackId; + DispatchCommandResult(plugRes, callbackId); + } + + /// + /// + /// + /// + private void downloadCallback(IAsyncResult asynchronousResult) + { + DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState; + HttpWebRequest request = reqState.request; + + string callbackId = reqState.options.CallbackId; + try + { + HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult); + + // send a progress change event + DispatchFileTransferProgress(0, response.ContentLength, callbackId); + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + // create any directories in the path that do not exist + string directoryName = getDirectoryName(reqState.options.FilePath); + if (!string.IsNullOrEmpty(directoryName) && !isoFile.DirectoryExists(directoryName)) + { + isoFile.CreateDirectory(directoryName); + } + + // create the file if not exists + if (!isoFile.FileExists(reqState.options.FilePath)) + { + var file = isoFile.CreateFile(reqState.options.FilePath); + file.Close(); + } + + using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, FileAccess.Write, isoFile)) + { + long totalBytes = response.ContentLength; + int bytesRead = 0; + using (BinaryReader reader = new BinaryReader(response.GetResponseStream())) + { + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + int BUFFER_SIZE = 1024; + byte[] buffer; + + while (true) + { + buffer = reader.ReadBytes(BUFFER_SIZE); + // fire a progress event ? + bytesRead += buffer.Length; + if (buffer.Length > 0 && !reqState.isCancelled) + { + writer.Write(buffer); + DispatchFileTransferProgress(bytesRead, totalBytes, callbackId); + } + else + { + writer.Close(); + reader.Close(); + fileStream.Close(); + break; + } + System.Threading.Thread.Sleep(1); + } + } + } + } + if (reqState.isCancelled) + { + isoFile.DeleteFile(reqState.options.FilePath); + } + } + + if (reqState.isCancelled) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(AbortError)), + callbackId); + } + else + { + File.FileEntry entry = new File.FileEntry(reqState.options.FilePath); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId); + } + } + catch (IsolatedStorageException) + { + // Trying to write the file somewhere within the IsoStorage. + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)), + callbackId); + } + catch (SecurityException) + { + // Trying to write the file somewhere not allowed. + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)), + callbackId); + } + catch (WebException webex) + { + // TODO: probably need better work here to properly respond with all http status codes back to JS + // Right now am jumping through hoops just to detect 404. + HttpWebResponse response = (HttpWebResponse)webex.Response; + if ((webex.Status == WebExceptionStatus.ProtocolError && response.StatusCode == HttpStatusCode.NotFound) + || webex.Status == WebExceptionStatus.UnknownError) + { + // Weird MSFT detection of 404... seriously... just give us the f(*&#$@ status code as a number ffs!!! + // "Numbers for HTTP status codes? Nah.... let's create our own set of enums/structs to abstract that stuff away." + // FACEPALM + // Or just cast it to an int, whiner ... -jm + int statusCode = (int)response.StatusCode; + string body = ""; + + using (Stream streamResponse = response.GetResponseStream()) + { + using (StreamReader streamReader = new StreamReader(streamResponse)) + { + body = streamReader.ReadToEnd(); + } + } + FileTransferError ftError = new FileTransferError(ConnectionError, null, null, statusCode, body); + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ftError), + callbackId); + } + else + { + lock (reqState) + { + if (!reqState.isCancelled) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(ConnectionError)), + callbackId); + } + else + { + Debug.WriteLine("It happened"); + } + } + } + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(FileNotFoundError)), + callbackId); + } + + //System.Threading.Thread.Sleep(1000); + if (InProcDownloads.ContainsKey(reqState.options.Id)) + { + InProcDownloads.Remove(reqState.options.Id); + } + } + + /// + /// Read file from Isolated Storage and sends it to server + /// + /// + private void uploadCallback(IAsyncResult asynchronousResult) + { + DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState; + HttpWebRequest webRequest = reqState.request; + string callbackId = reqState.options.CallbackId; + + try + { + using (Stream requestStream = (webRequest.EndGetRequestStream(asynchronousResult))) + { + string lineStart = "--"; + string lineEnd = Environment.NewLine; + byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(lineStart + Boundary + lineEnd); + string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"" + lineEnd + lineEnd + "{1}" + lineEnd; + + if (!string.IsNullOrEmpty(reqState.options.Params)) + { + Dictionary paramMap = parseHeaders(reqState.options.Params); + foreach (string key in paramMap.Keys) + { + requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); + string formItem = string.Format(formdataTemplate, key, paramMap[key]); + byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem); + requestStream.Write(formItemBytes, 0, formItemBytes.Length); + } + } + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(reqState.options.FilePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError, reqState.options.Server, reqState.options.FilePath, 0))); + return; + } + + byte[] endRequest = System.Text.Encoding.UTF8.GetBytes(lineEnd + lineStart + Boundary + lineStart + lineEnd); + long totalBytesToSend = 0; + + using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, isoFile)) + { + string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + lineEnd + "Content-Type: {2}" + lineEnd + lineEnd; + string header = string.Format(headerTemplate, reqState.options.FileKey, reqState.options.FileName, reqState.options.MimeType); + byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header); + + byte[] buffer = new byte[4096]; + int bytesRead = 0; + //sent bytes needs to be reseted before new upload + bytesSent = 0; + totalBytesToSend = fileStream.Length; + + requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); + + requestStream.Write(headerBytes, 0, headerBytes.Length); + + while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) + { + if (!reqState.isCancelled) + { + requestStream.Write(buffer, 0, bytesRead); + bytesSent += bytesRead; + DispatchFileTransferProgress(bytesSent, totalBytesToSend, callbackId); + System.Threading.Thread.Sleep(1); + } + else + { + throw new Exception("UploadCancelledException"); + } + } + } + + requestStream.Write(endRequest, 0, endRequest.Length); + } + } + // webRequest + + webRequest.BeginGetResponse(ReadCallback, reqState); + } + catch (Exception /*ex*/) + { + if (!reqState.isCancelled) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)), callbackId); + } + } + } + + /// + /// Reads response into FileUploadResult + /// + /// + private void ReadCallback(IAsyncResult asynchronousResult) + { + DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState; + try + { + HttpWebRequest webRequest = reqState.request; + string callbackId = reqState.options.CallbackId; + + if (InProcDownloads.ContainsKey(reqState.options.Id)) + { + InProcDownloads.Remove(reqState.options.Id); + } + + using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult)) + { + using (Stream streamResponse = response.GetResponseStream()) + { + using (StreamReader streamReader = new StreamReader(streamResponse)) + { + string responseString = streamReader.ReadToEnd(); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileUploadResult(bytesSent, (long)response.StatusCode, responseString))); + }); + } + } + } + } + catch (WebException webex) + { + // TODO: probably need better work here to properly respond with all http status codes back to JS + // Right now am jumping through hoops just to detect 404. + if ((webex.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)webex.Response).StatusCode == HttpStatusCode.NotFound) + || webex.Status == WebExceptionStatus.UnknownError) + { + int statusCode = (int)((HttpWebResponse)webex.Response).StatusCode; + FileTransferError ftError = new FileTransferError(ConnectionError, null, null, statusCode); + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ftError), reqState.options.CallbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, + new FileTransferError(ConnectionError)), + reqState.options.CallbackId); + } + } + catch (Exception /*ex*/) + { + FileTransferError transferError = new FileTransferError(ConnectionError, reqState.options.Server, reqState.options.FilePath, 403); + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, transferError), reqState.options.CallbackId); + } + } + + // Gets the full path without the filename + private string getDirectoryName(String filePath) + { + string directoryName; + try + { + directoryName = filePath.Substring(0, filePath.LastIndexOf('/')); + } + catch + { + directoryName = ""; + } + return directoryName; + } + } +} diff --git a/plugins/cordova-plugin-file-transfer/tests/plugin.xml b/plugins/cordova-plugin-file-transfer/tests/plugin.xml new file mode 100644 index 0000000..e5ac9e7 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/tests/plugin.xml @@ -0,0 +1,30 @@ + + + + + Cordova File Transfer Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-file-transfer/tests/tests.js b/plugins/cordova-plugin-file-transfer/tests/tests.js new file mode 100644 index 0000000..42fc680 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/tests/tests.js @@ -0,0 +1,1601 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ + +/* global exports, cordova, FileTransfer, FileTransferError, FileUploadOptions, LocalFileSystem */ + +/* jshint jasmine: true */ + +exports.defineAutoTests = function () { + + "use strict"; + + // constants + var ONE_SECOND = 1000; // in milliseconds + var GRACE_TIME_DELTA = 600; // in milliseconds + var DEFAULT_FILESYSTEM_SIZE = 1024 * 50; // filesystem size in bytes + var UNKNOWN_HOST = "http://foobar.apache.org"; + var HEADERS_ECHO = "http://whatheaders.com"; // NOTE: this site is very useful! + var DOWNLOAD_TIMEOUT = 7 * ONE_SECOND; + var WINDOWS_UNKNOWN_HOST_TIMEOUT = 35 * ONE_SECOND; + var UPLOAD_TIMEOUT = 7 * ONE_SECOND; + var ABORT_DELAY = 100; // for abort() tests + var LATIN1_SYMBOLS = '¥§©ÆÖÑøøø¼'; + var DATA_URI_PREFIX = "data:image/png;base64,"; + var DATA_URI_CONTENT = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; + var DATA_URI_CONTENT_LENGTH = 85; // bytes. (This is the raw file size: used https://en.wikipedia.org/wiki/File:Red-dot-5px.png from https://en.wikipedia.org/wiki/Data_URI_scheme) + + // config for upload test server + // NOTE: + // more info at https://github.com/apache/cordova-labs/tree/cordova-filetransfer + var SERVER = "http://cordova-vm.apache.org:5000"; + var SERVER_WITH_CREDENTIALS = "http://cordova_user:cordova_password@cordova-vm.apache.org:5000"; + + // flags + var isWindows = cordova.platformId === "windows8" || cordova.platformId === "windows"; + var isWP8 = cordova.platformId === "windowsphone"; + var isBrowser = cordova.platformId === "browser"; + var isIE = isBrowser && navigator.userAgent.indexOf("Trident") >= 0; + var isIos = cordova.platformId === "ios"; + + // tests + describe("FileTransferError", function () { + + it("should exist", function () { + expect(FileTransferError).toBeDefined(); + }); + + it("should be constructable", function () { + var transferError = new FileTransferError(); + expect(transferError).toBeDefined(); + }); + + it("filetransfer.spec.3 should expose proper constants", function () { + + expect(FileTransferError.FILE_NOT_FOUND_ERR).toBeDefined(); + expect(FileTransferError.INVALID_URL_ERR).toBeDefined(); + expect(FileTransferError.CONNECTION_ERR).toBeDefined(); + expect(FileTransferError.ABORT_ERR).toBeDefined(); + expect(FileTransferError.NOT_MODIFIED_ERR).toBeDefined(); + + expect(FileTransferError.FILE_NOT_FOUND_ERR).toBe(1); + expect(FileTransferError.INVALID_URL_ERR).toBe(2); + expect(FileTransferError.CONNECTION_ERR).toBe(3); + expect(FileTransferError.ABORT_ERR).toBe(4); + expect(FileTransferError.NOT_MODIFIED_ERR).toBe(5); + }); + }); + + describe("FileUploadOptions", function () { + + it("should exist", function () { + expect(FileUploadOptions).toBeDefined(); + }); + + it("should be constructable", function () { + var transferOptions = new FileUploadOptions(); + expect(transferOptions).toBeDefined(); + }); + }); + + describe("FileTransfer", function () { + this.persistentRoot = null; + this.tempRoot = null; + + // named callbacks + var unexpectedCallbacks = { + httpFail: function () {}, + httpWin: function () {}, + fileSystemFail: function () {}, + fileSystemWin: function () {}, + fileOperationFail: function () {}, + fileOperationWin: function () {}, + }; + + var expectedCallbacks = { + unsupportedOperation: function (response) { + console.log("spec called unsupported functionality; response:", response); + }, + }; + + // helpers + var deleteFile = function (fileSystem, name, done) { + fileSystem.getFile(name, null, + function (fileEntry) { + fileEntry.remove( + function () { + done(); + }, + function () { + throw new Error("failed to delete: '" + name + "'"); + } + ); + }, + function () { + done(); + } + ); + }; + + var writeFile = function (fileSystem, name, content, success, done) { + var fileOperationFail = function() { + unexpectedCallbacks.fileOperationFail(); + done(); + }; + + fileSystem.getFile(name, { create: true }, + function (fileEntry) { + fileEntry.createWriter(function (writer) { + + writer.onwrite = function () { + success(fileEntry); + }; + + writer.onabort = function (evt) { + throw new Error("aborted creating test file '" + name + "': " + evt); + }; + + writer.error = function (evt) { + throw new Error("aborted creating test file '" + name + "': " + evt); + }; + + if (cordova.platformId === "browser") { + var blob = new Blob([content + "\n"], { type: "text/plain" }); + writer.write(blob); + } else { + writer.write(content + "\n"); + } + + }, fileOperationFail); + }, + function () { + throw new Error("could not create test file '" + name + "'"); + } + ); + }; + + // according to documentation, wp8 does not support onProgress: + // https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#supported-platforms + var wp8OnProgressHandler = function () {}; + + var defaultOnProgressHandler = function (event) { + if (event.lengthComputable) { + expect(event.loaded).toBeGreaterThan(1); + expect(event.total).toBeGreaterThan(0); + expect(event.total).not.toBeLessThan(event.loaded); + expect(event.lengthComputable).toBe(true, "lengthComputable"); + } else { + // In IE, when lengthComputable === false, event.total somehow is equal to 2^64 + if (isIE) { + expect(event.total).toBe(Math.pow(2, 64)); + } else { + // iOS returns -1, and other platforms return 0 + expect(event.total).toBeLessThan(1); + } + } + }; + + var getMalformedUrl = function () { + if (cordova.platformId === "android" || cordova.platformId === "amazon-fireos") { + // bad protocol causes a MalformedUrlException on Android + return "httpssss://example.com"; + } else { + // iOS doesn't care about protocol, space in hostname causes error + return "httpssss://exa mple.com"; + } + }; + + // NOTE: + // there are several beforeEach calls, one per async call; since calling done() + // signifies a completed async call, each async call needs its own done(), and + // therefore its own beforeEach + beforeEach(function (done) { + var specContext = this; + + window.requestFileSystem(LocalFileSystem.PERSISTENT, DEFAULT_FILESYSTEM_SIZE, + function (fileSystem) { + specContext.persistentRoot = fileSystem.root; + done(); + }, + function () { + throw new Error("Failed to initialize persistent file system."); + } + ); + }); + + beforeEach(function (done) { + var specContext = this; + + window.requestFileSystem(LocalFileSystem.TEMPORARY, DEFAULT_FILESYSTEM_SIZE, + function (fileSystem) { + specContext.tempRoot = fileSystem.root; + done(); + }, + function () { + throw new Error("Failed to initialize temporary file system."); + } + ); + }); + + // spy on all named callbacks + beforeEach(function() { + + // ignore the actual implementations of the unexpected callbacks + for (var callback in unexpectedCallbacks) { + if (unexpectedCallbacks.hasOwnProperty(callback)) { + spyOn(unexpectedCallbacks, callback); + } + } + + // but run the implementations of the expected callbacks + for (callback in expectedCallbacks) { // jshint ignore: line + if (expectedCallbacks.hasOwnProperty(callback)) { + spyOn(expectedCallbacks, callback).and.callThrough(); + } + } + }); + + // at the end, check that none of the unexpected callbacks got called, + // and act on the expected callbacks + afterEach(function() { + for (var callback in unexpectedCallbacks) { + if (unexpectedCallbacks.hasOwnProperty(callback)) { + expect(unexpectedCallbacks[callback]).not.toHaveBeenCalled(); + } + } + + if (expectedCallbacks.unsupportedOperation.calls.any()) { + pending(); + } + }); + + it("should initialise correctly", function() { + expect(this.persistentRoot).toBeDefined(); + expect(this.tempRoot).toBeDefined(); + }); + + it("should exist", function () { + expect(FileTransfer).toBeDefined(); + }); + + it("filetransfer.spec.1 should be constructable", function () { + var transfer = new FileTransfer(); + expect(transfer).toBeDefined(); + }); + + it("filetransfer.spec.2 should expose proper functions", function () { + + var transfer = new FileTransfer(); + + expect(transfer.upload).toBeDefined(); + expect(transfer.download).toBeDefined(); + + expect(transfer.upload).toEqual(jasmine.any(Function)); + expect(transfer.download).toEqual(jasmine.any(Function)); + }); + + describe("methods", function() { + this.transfer = null; + this.root = null; + this.fileName = null; + this.localFilePath = null; + + beforeEach(function() { + + this.transfer = new FileTransfer(); + + // assign onprogress handler + this.transfer.onprogress = isWP8 ? wp8OnProgressHandler : defaultOnProgressHandler; + + // spy on the onprogress handler, but still call through to it + spyOn(this.transfer, "onprogress").and.callThrough(); + + this.root = this.persistentRoot; + this.fileName = "testFile.txt"; + this.localFilePath = this.root.toURL() + this.fileName; + }); + + // NOTE: + // if download tests are failing, check the + // URL white list for the following URLs: + // - 'httpssss://example.com' + // - 'apache.org', with subdomains="true" + // - 'cordova-filetransfer.jitsu.com' + describe("download", function () { + + // helpers + var verifyDownload = function (fileEntry, specContext) { + expect(fileEntry.name).toBe(specContext.fileName); + }; + + // delete the downloaded file + afterEach(function (done) { + deleteFile(this.root, this.fileName, done); + }); + + it("ensures that test file does not exist", function (done) { + deleteFile(this.root, this.fileName, done); + }); + + it("filetransfer.spec.4 should download a file", function (done) { + + var fileURL = SERVER + "/robots.txt"; + var specContext = this; + + var fileWin = function (blob) { + + if (specContext.transfer.onprogress.calls.any()) { + var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; + expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); + } else { + console.log("no progress events were emitted"); + } + + done(); + }; + + var fileSystemFail = function() { + unexpectedCallbacks.fileSystemFail(); + done(); + }; + + var downloadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + var downloadWin = function (entry) { + + verifyDownload(entry, specContext); + + // verify the FileEntry representing this file + entry.file(fileWin, fileSystemFail); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.5 should download a file using http basic auth", function (done) { + + var fileURL = SERVER_WITH_CREDENTIALS + "/download_basic_auth"; + var specContext = this; + + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; + + var downloadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.6 should get 401 status on http basic auth failure", function (done) { + + // NOTE: + // using server without credentials + var fileURL = SERVER + "/download_basic_auth"; + + var downloadFail = function (error) { + expect(error.http_status).toBe(401); + expect(error.http_status).not.toBe(404, "Ensure " + fileURL + " is in the white list"); + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail, null, + { + headers: { + "If-Modified-Since": "Thu, 19 Mar 2015 00:00:00 GMT" + } + }); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.7 should download a file using file:// (when hosted from file://)", function (done) { + + // for Windows platform it's ms-appdata:/// by default, not file:// + if (isWindows) { + pending(); + return; + } + + var fileURL = window.location.protocol + "//" + window.location.pathname.replace(/ /g, "%20"); + var specContext = this; + + if (!/^file:/.exec(fileURL) && cordova.platformId !== "blackberry10") { + if (cordova.platformId === "windowsphone") { + expect(fileURL).toMatch(/^x-wmapp0:/); + } + done(); + return; + } + + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; + + var downloadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.8 should download a file using https://", function (done) { + + var fileURL = "https://www.apache.org/licenses/"; + var specContext = this; + + var downloadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + var fileOperationFail = function() { + unexpectedCallbacks.fileOperationFail(); + done(); + }; + + var fileSystemFail = function() { + unexpectedCallbacks.fileSystemFail(); + done(); + }; + + var fileWin = function (file) { + + var reader = new FileReader(); + + reader.onerror = fileOperationFail; + reader.onload = function () { + expect(reader.result).toMatch(/The Apache Software Foundation/); + done(); + }; + + reader.readAsText(file); + }; + + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + entry.file(fileWin, fileSystemFail); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.11 should call the error callback on abort()", function (done) { + + var fileURL = "http://cordova.apache.org/downloads/BlueZedEx.mp3"; + fileURL = fileURL + "?q=" + (new Date()).getTime(); + var specContext = this; + + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, done); + setTimeout(function() { + specContext.transfer.abort(); + }, ABORT_DELAY); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.9 should not leave partial file due to abort", function (done) { + + var fileURL = "http://cordova.apache.org/downloads/logos_2.zip"; + var specContext = this; + + var fileSystemWin = function() { + unexpectedCallbacks.fileSystemWin(); + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + var downloadFail = function (error) { + + var result = (error.code === FileTransferError.ABORT_ERR || error.code === FileTransferError.CONNECTION_ERR)? true: false; + if (!result) { + fail("Expected " + error.code + " to be " + FileTransferError.ABORT_ERR + " or " + FileTransferError.CONNECTION_ERR); + } + expect(specContext.transfer.onprogress).toHaveBeenCalled(); + + // check that there is no file + specContext.root.getFile(specContext.fileName, null, fileSystemWin, done); + }; + + // abort at the first onprogress event + specContext.transfer.onprogress = function (event) { + if (event.loaded > 0) { + specContext.transfer.abort(); + } + }; + + spyOn(specContext.transfer, "onprogress").and.callThrough(); + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.10 should be stopped by abort()", function (done) { + + var fileURL = "http://cordova.apache.org/downloads/BlueZedEx.mp3"; + fileURL = fileURL + "?q=" + (new Date()).getTime(); + var specContext = this; + + expect(specContext.transfer.abort).not.toThrow(); // should be a no-op. + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + var downloadFail = function (error) { + + expect(error.code).toBe(FileTransferError.ABORT_ERR); + + // delay calling done() to wait for the bogus abort() + setTimeout(done, GRACE_TIME_DELTA * 2); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + setTimeout(function() { + specContext.transfer.abort(); + }, ABORT_DELAY); + + // call abort() again, after a time greater than the grace period + setTimeout(function () { + expect(specContext.transfer.abort).not.toThrow(); + }, GRACE_TIME_DELTA); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.12 should get http status on failure", function (done) { + + var fileURL = SERVER + "/404"; + + var downloadFail = function (error) { + + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + expect(error.http_status).toBe(404); + + // wp8 does not make difference between 404 and unknown host + if (isWP8) { + expect(error.code).toBe(FileTransferError.CONNECTION_ERR); + } else { + expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); + } + + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.13 should get http body on failure", function (done) { + + var fileURL = SERVER + "/404"; + + var downloadFail = function (error) { + + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + expect(error.http_status).toBe(404); + + expect(error.body).toBeDefined(); + expect(error.body).toMatch("You requested a 404"); + + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.14 should handle malformed urls", function (done) { + + var fileURL = getMalformedUrl(); + + var downloadFail = function (error) { + + // Note: Android needs the bad protocol to be added to the access list + // won't match because ^https?:// is prepended to the regex + // The bad protocol must begin with http to avoid automatic prefix + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + expect(error.code).toBe(FileTransferError.INVALID_URL_ERR); + + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }); + + it("filetransfer.spec.15 should handle unknown host", function (done) { + var fileURL = UNKNOWN_HOST; + + var downloadFail = function (error) { + expect(error.code).toBe(FileTransferError.CONNECTION_ERR); + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + // turn off the onprogress handler + this.transfer.onprogress = function () {}; + + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, isWindows ? WINDOWS_UNKNOWN_HOST_TIMEOUT : DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.16 should handle bad file path", function (done) { + var fileURL = SERVER; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.download(fileURL, "c:\\54321", downloadWin, done); + }); + + it("filetransfer.spec.17 progress should work with gzip encoding", function (done) { + + // lengthComputable false on bb10 when downloading gzip + if (cordova.platformId === "blackberry10") { + pending(); + return; + } + + var fileURL = "http://www.apache.org/"; + var specContext = this; + + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; + + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin,downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.30 downloaded file entries should have a toNativeURL method", function (done) { + + if (cordova.platformId === "browser") { + pending(); + return; + } + + var fileURL = SERVER + "/robots.txt"; + + var downloadWin = function (entry) { + + expect(entry.toNativeURL).toBeDefined(); + expect(entry.toNativeURL).toEqual(jasmine.any(Function)); + + var nativeURL = entry.toNativeURL(); + + expect(nativeURL).toBeTruthy(); + expect(nativeURL).toEqual(jasmine.any(String)); + + if (isWindows) { + expect(nativeURL.substring(0, 14)).toBe("ms-appdata:///"); + } else if (isWP8) { + expect(nativeURL.substring(0, 1)).toBe("/"); + } else { + expect(nativeURL.substring(0, 7)).toBe("file://"); + } + + done(); + }; + + var downloadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.28 (compatibility) should be able to download a file using local paths", function (done) { + + var fileURL = SERVER + "/robots.txt"; + var specContext = this; + + var unsupported = function (response) { + expectedCallbacks.unsupportedOperation(response); + done(); + }; + + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; + + var internalFilePath; + if (specContext.root.toInternalURL) { + internalFilePath = specContext.root.toInternalURL() + specContext.fileName; + } else { + internalFilePath = specContext.localFilePath; + } + + var downloadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // This is an undocumented interface to File which exists only for testing + // backwards compatibilty. By obtaining the raw filesystem path of the download + // location, we can pass that to transfer.download() to make sure that previously-stored + // paths are still valid. + cordova.exec(function (localPath) { + specContext.transfer.download(fileURL, localPath, downloadWin, downloadFail); + }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]); + }); + + it("filetransfer.spec.33 should properly handle 304", function (done) { + + if (isWP8) { + pending(); + return; + } + + var imageURL = "http://apache.org/images/feather-small.gif"; + var lastModified = new Date(); + + var downloadFail = function (error) { + expect(error.http_status).toBe(304); + expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR); + done(); + }; + + var downloadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.download(imageURL + "?q=" + lastModified.getTime(), this.localFilePath, downloadWin, downloadFail, null, + { + headers: { + "If-Modified-Since": lastModified.toUTCString() + } + }); + }, DOWNLOAD_TIMEOUT); + + it("filetransfer.spec.35 304 should not result in the deletion of a cached file", function (done) { + + if (isWP8) { + pending(); + return; + } + + var imageURL = "http://apache.org/images/feather-small.gif"; + var lastModified = new Date(); + var specContext = this; + + var fileOperationFail = function() { + unexpectedCallbacks.fileOperationFail(); + done(); + }; + + var fileSystemFail = function() { + unexpectedCallbacks.fileSystemFail(); + done(); + }; + + var httpWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + var httpFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + var downloadFail = function (error) { + expect(error.http_status).toBe(304); + expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR); + + specContext.persistentRoot.getFile(specContext.fileName, { create: false }, + function (entry) { + var fileWin = function (file) { + var reader = new FileReader(); + + reader.onerror = fileOperationFail; + reader.onloadend = function () { + + expect(reader.result).toBeTruthy(); + if (reader.result !== null) { + expect(reader.result.length).toBeGreaterThan(0); + } + + done(); + }; + + reader.readAsBinaryString(file); + }; + + entry.file(fileWin, fileSystemFail); + }, + function (err) { + expect("Could not open test file '" + specContext.fileName + "': " + JSON.stringify(err)).not.toBeDefined(); + done(); + } + ); + }; + + // Adding parameters to the requests to avoid caching on iOS, which leads to 200 + // instead of 304 in result of the second request. (a similar issue is described in CB-8606, CB-10088) + specContext.transfer.download(imageURL + "?q=" + lastModified.getTime(), specContext.localFilePath, function () { + specContext.transfer.download(imageURL + "?q=" + (lastModified.getTime() + 1), specContext.localFilePath, httpWin, downloadFail, null, + { + headers: { + "If-Modified-Since": lastModified.toUTCString() + } + }); + }, httpFail); + }, DOWNLOAD_TIMEOUT * 2); + + it("filetransfer.spec.36 should handle non-UTF8 encoded download response", function (done) { + + // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840 + if (!isIos) { + pending(); + } + + var fileURL = SERVER + '/download_non_utf'; + var specContext = this; + + var fileOperationFail = function() { + unexpectedCallbacks.fileOperationFail(); + done(); + }; + + var fileSystemFail = function() { + unexpectedCallbacks.fileSystemFail(); + done(); + }; + + var httpFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + var fileWin = function (blob) { + + if (specContext.transfer.onprogress.calls.any()) { + var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; + expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); + } else { + console.log("no progress events were emitted"); + } + + expect(blob.size).toBeGreaterThan(0); + + var reader = new FileReader(); + + reader.onerror = fileOperationFail; + reader.onloadend = function () { + expect(reader.result.indexOf(LATIN1_SYMBOLS)).not.toBe(-1); + done(); + }; + + reader.readAsBinaryString(blob); + }; + + var downloadWin = function (entry) { + + verifyDownload(entry, specContext); + + // verify the FileEntry representing this file + entry.file(fileWin, fileSystemFail); + }; + + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, httpFail); + }, UPLOAD_TIMEOUT); + }); + + describe("upload", function() { + this.uploadParams = null; + this.uploadOptions = null; + this.fileName = null; + this.fileContents = null; + this.localFilePath = null; + + // helpers + var verifyUpload = function (uploadResult, specContext) { + + expect(uploadResult.bytesSent).toBeGreaterThan(0); + expect(uploadResult.responseCode).toBe(200); + + var obj = null; + try { + obj = JSON.parse(uploadResult.response); + expect(obj.fields).toBeDefined(); + expect(obj.fields.value1).toBe("test"); + expect(obj.fields.value2).toBe("param"); + } catch (e) { + expect(obj).not.toBeNull("returned data from server should be valid json"); + } + + expect(specContext.transfer.onprogress).toHaveBeenCalled(); + }; + + beforeEach(function(done) { + var specContext = this; + + specContext.fileName = "fileToUpload.txt"; + specContext.fileContents = "upload test file"; + + specContext.uploadParams = {}; + specContext.uploadParams.value1 = "test"; + specContext.uploadParams.value2 = "param"; + + specContext.uploadOptions = new FileUploadOptions(); + specContext.uploadOptions.fileKey = "file"; + specContext.uploadOptions.fileName = specContext.fileName; + specContext.uploadOptions.mimeType = "text/plain"; + specContext.uploadOptions.params = specContext.uploadParams; + + var fileWin = function (entry) { + specContext.localFilePath = entry.toURL(); + done(); + }; + + // create a file to upload + writeFile(specContext.root, specContext.fileName, specContext.fileContents, fileWin, done); + }); + + // delete the uploaded file + afterEach(function (done) { + deleteFile(this.root, this.fileName, done); + }); + + it("filetransfer.spec.18 should be able to upload a file", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var uploadWin = function (uploadResult) { + + verifyUpload(uploadResult, specContext); + + if (cordova.platformId === "ios") { + expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); + expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); + } + + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.19 should be able to upload a file with http basic auth", function (done) { + + var fileURL = SERVER_WITH_CREDENTIALS + "/upload_basic_auth"; + var specContext = this; + + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.21 should be stopped by abort()", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var uploadFail = function (e) { + expect(e.code).toBe(FileTransferError.ABORT_ERR); + + // delay calling done() to wait for the bogus abort() + setTimeout(done, GRACE_TIME_DELTA * 2); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + var fileWin = function () { + + expect(specContext.transfer.abort).not.toThrow(); + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + setTimeout(function() { + specContext.transfer.abort(); + }, ABORT_DELAY); + + setTimeout(function () { + expect(specContext.transfer.abort).not.toThrow(); + }, GRACE_TIME_DELTA); + }; + + writeFile(specContext.root, specContext.fileName, new Array(200000).join("aborttest!"), fileWin, done); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.22 should get http status and body on failure", function (done) { + + var fileURL = SERVER + "/403"; + + var uploadFail = function (error) { + expect(error.http_status).toBe(403); + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + done(); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.24 should handle malformed urls", function (done) { + + var fileURL = getMalformedUrl(); + + var uploadFail = function (error) { + expect(error.code).toBe(FileTransferError.INVALID_URL_ERR); + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + done(); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); + }); + + it("filetransfer.spec.25 should handle unknown host", function (done) { + + var fileURL = UNKNOWN_HOST; + + var uploadFail = function (error) { + expect(error.code).toBe(FileTransferError.CONNECTION_ERR); + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + done(); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.25 should handle missing file", function (done) { + + var fileURL = SERVER + "/upload"; + + var uploadFail = function (error) { + expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + done(); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.upload("does_not_exist.txt", fileURL, uploadWin, uploadFail); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.26 should handle bad file path", function (done) { + + var fileURL = SERVER + "/upload"; + + var uploadFail = function (error) { + expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + done(); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + this.transfer.upload("c:\\54321", fileURL, uploadWin, uploadFail); + }); + + it("filetransfer.spec.27 should be able to set custom headers", function (done) { + + var fileURL = HEADERS_ECHO; + + var uploadWin = function (uploadResult) { + + expect(uploadResult.bytesSent).toBeGreaterThan(0); + expect(uploadResult.responseCode).toBe(200); + expect(uploadResult.response).toBeDefined(); + + var responseHtml = decodeURIComponent(uploadResult.response); + + expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i); + expect(responseHtml).toMatch(/CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i, "Should allow array values"); + + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + this.uploadOptions.headers = { + "CustomHeader1": "CustomValue1", + "CustomHeader2": ["CustomValue2", "CustomValue3"], + }; + + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.29 (compatibility) should be able to upload a file using local paths", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var unsupported = function (response) { + expectedCallbacks.unsupportedOperation(response); + done(); + }; + + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + var internalFilePath; + if (specContext.root.toInternalURL) { + internalFilePath = specContext.root.toInternalURL() + specContext.fileName; + } else { + internalFilePath = specContext.localFilePath; + } + + // This is an undocumented interface to File which exists only for testing + // backwards compatibilty. By obtaining the raw filesystem path of the download + // location, we can pass that to transfer.download() to make sure that previously-stored + // paths are still valid. + cordova.exec(function (localPath) { + specContext.transfer.upload(localPath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.31 should be able to upload a file using PUT method", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var uploadWin = function (uploadResult) { + + verifyUpload(uploadResult, specContext); + + if (cordova.platformId === "ios") { + expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); + expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); + } + + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + specContext.uploadOptions.httpMethod = "PUT"; + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.32 should be able to upload a file (non-multipart)", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var uploadWin = function (uploadResult) { + + expect(uploadResult.bytesSent).toBeGreaterThan(0); + expect(uploadResult.responseCode).toBe(200); + expect(uploadResult.response).toBeDefined(); + if (uploadResult.response) { + expect(uploadResult.response).toEqual(specContext.fileContents + "\n"); + } + expect(specContext.transfer.onprogress).toHaveBeenCalled(); + + if (cordova.platformId === "ios") { + expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); + expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); + } + + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // Content-Type header disables multipart + specContext.uploadOptions.headers = { + "Content-Type": "text/plain" + }; + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.34 should not delete a file on upload error", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var uploadFail = function (e) { + expect(e.code).toBe(FileTransferError.ABORT_ERR); + + // check that the file is there + specContext.root.getFile(specContext.fileName, null, function(entry) { + expect(entry).toBeDefined(); + // delay calling done() to wait for the bogus abort() + setTimeout(done, GRACE_TIME_DELTA * 2); + }, function(err) { + expect(err).not.toBeDefined(err && err.code); + done(); + }); + }; + + var uploadWin = function() { + unexpectedCallbacks.httpWin(); + done(); + }; + + var fileWin = function () { + + expect(specContext.transfer.abort).not.toThrow(); + + // abort at the first onprogress event + specContext.transfer.onprogress = function (event) { + if (event.loaded > 0) { + specContext.transfer.abort(); + } + }; + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }; + + writeFile(specContext.root, specContext.fileName, new Array(100000).join("aborttest!"), fileWin, done); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.37 should handle non-UTF8 encoded upload response", function (done) { + + // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840 + if (!isIos) { + pending(); + } + + var fileURL = SERVER + '/upload_non_utf'; + var specContext = this; + + var uploadWin = function (uploadResult) { + + verifyUpload(uploadResult, specContext); + + var obj = null; + try { + obj = JSON.parse(uploadResult.response); + expect(obj.latin1Symbols).toBe(LATIN1_SYMBOLS); + } catch (e) { + expect(obj).not.toBeNull("returned data from server should be valid json"); + } + + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } + + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.38 should be able to upload a file using data: source uri", function (done) { + + var fileURL = SERVER + "/upload"; + var specContext = this; + + var uploadWin = function (uploadResult) { + + verifyUpload(uploadResult,specContext); + + var obj = null; + try { + obj = JSON.parse(uploadResult.response); + expect(obj.files.file.size).toBe(DATA_URI_CONTENT_LENGTH); + } catch (e) { + expect(obj).not.toBeNull("returned data from server should be valid json"); + } + + if (cordova.platformId === "ios") { + expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); + expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); + } + + done(); + }; + + var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT; + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(dataUri, fileURL, uploadWin, function (err) { + console.error('err: ' + JSON.stringify(err)); + expect(err).not.toBeDefined(); + done(); + }, specContext.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.39 should be able to upload a file using data: source uri (non-multipart)", function (done) { + + var fileURL = SERVER + "/upload"; + + var uploadWin = function (uploadResult) { + + expect(uploadResult.responseCode).toBe(200); + expect(uploadResult.bytesSent).toBeGreaterThan(0); + + if (cordova.platformId === "ios") { + expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); + expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); + } + + done(); + }; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // Content-Type header disables multipart + this.uploadOptions.headers = { + "Content-Type": "image/png" + }; + + var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT; + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(dataUri, fileURL, uploadWin, uploadFail, this.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.40 should not fail to upload a file using data: source uri when the data is empty", function (done) { + + var fileURL = SERVER + "/upload"; + + var dataUri = DATA_URI_PREFIX; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); + }, UPLOAD_TIMEOUT); + + it("filetransfer.spec.41 should not fail to upload a file using data: source uri when the data is empty (non-multipart)", function (done) { + + var fileURL = SERVER + "/upload"; + + // Content-Type header disables multipart + this.uploadOptions.headers = { + "Content-Type": "image/png" + }; + + // turn off the onprogress handler + this.transfer.onprogress = function () { }; + + var dataUri = DATA_URI_PREFIX; + + var uploadFail = function() { + unexpectedCallbacks.httpFail(); + done(); + }; + + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); + }, UPLOAD_TIMEOUT); + }); + }); + }); +}; + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +exports.defineManualTests = function (contentEl, createActionButton) { + + "use strict"; + + var imageURL = "http://apache.org/images/feather-small.gif"; + var videoURL = "http://techslides.com/demos/sample-videos/small.mp4"; + + function clearResults() { + var results = document.getElementById("info"); + results.innerHTML = ""; + } + + function downloadImg(source, urlFn, element, directory) { + var filename = source.substring(source.lastIndexOf("/") + 1); + filename = (directory || "") + filename; + + function download(fileSystem) { + var ft = new FileTransfer(); + console.log("Starting download"); + + var progress = document.getElementById("loadingStatus"); + progress.value = 0; + + ft.onprogress = function(progressEvent) { + if (progressEvent.lengthComputable) { + var currPercents = parseInt(100 * (progressEvent.loaded / progressEvent.total), 10); + if (currPercents > progress.value) { + progress.value = currPercents; + } + } else { + progress.value = null; + } + }; + + ft.download(source, fileSystem.root.toURL() + filename, function (entry) { + console.log("Download complete"); + element.src = urlFn(entry); + console.log("Src URL is " + element.src); + console.log("Inserting element"); + document.getElementById("info").appendChild(element); + }, function (e) { console.log("ERROR: ft.download " + e.code); }); + } + console.log("Requesting filesystem"); + clearResults(); + window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) { + console.log("Checking for existing file"); + if (typeof directory !== "undefined") { + console.log("Checking for existing directory."); + fileSystem.root.getDirectory(directory, {}, function (dirEntry) { + dirEntry.removeRecursively(function () { + download(fileSystem); + }, function () { console.log("ERROR: dirEntry.removeRecursively"); }); + }, function () { + download(fileSystem); + }); + } else { + fileSystem.root.getFile(filename, { create: false }, function (entry) { + console.log("Removing existing file"); + entry.remove(function () { + download(fileSystem); + }, function () { console.log("ERROR: entry.remove"); }); + }, function () { + download(fileSystem); + }); + } + }, function () { console.log("ERROR: requestFileSystem"); }); + } + + /******************************************************************************/ + + var progress_tag = ""; + var file_transfer_tests = "

Image File Transfer Tests

" + + "

The following tests should display an image of the Apache feather in the status box

" + + "
" + + "
" + + "
" + + "

Video File Transfer Tests

" + + "

The following tests should display a video in the status box. The video should play when play is pressed

" + + "
" + + "
"; + + contentEl.innerHTML = "
" + "
" + progress_tag + + file_transfer_tests; + + createActionButton("Download and display img (cdvfile)", function () { + downloadImg(imageURL, function (entry) { return entry.toInternalURL(); }, new Image()); + }, "cdv_image"); + + createActionButton("Download and display img (native)", function () { + downloadImg(imageURL, function (entry) { return entry.toURL(); }, new Image()); + }, "native_image"); + + createActionButton("Download to a non-existent dir (should work)", function () { + downloadImg(imageURL, function (entry) { return entry.toURL(); }, new Image(), "/nonExistentDirTest/"); + }, "non-existent_dir"); + + createActionButton("Download and play video (cdvfile)", function () { + var videoElement = document.createElement("video"); + videoElement.controls = "controls"; + downloadImg(videoURL, function (entry) { return entry.toInternalURL(); }, videoElement); + }, "cdv_video"); + + createActionButton("Download and play video (native)", function () { + var videoElement = document.createElement("video"); + videoElement.controls = "controls"; + downloadImg(videoURL, function (entry) { return entry.toURL(); }, videoElement); + }, "native_video"); +}; diff --git a/plugins/cordova-plugin-file-transfer/www/FileTransfer.js b/plugins/cordova-plugin-file-transfer/www/FileTransfer.js new file mode 100644 index 0000000..80cf91c --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/FileTransfer.js @@ -0,0 +1,239 @@ +/* + * + * 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 cordova, FileSystem */ + +var argscheck = require('cordova/argscheck'), + exec = require('cordova/exec'), + FileTransferError = require('./FileTransferError'), + ProgressEvent = require('cordova-plugin-file.ProgressEvent'); + +function newProgressEvent(result) { + var pe = new ProgressEvent(); + pe.lengthComputable = result.lengthComputable; + pe.loaded = result.loaded; + pe.total = result.total; + return pe; +} + +function getUrlCredentials(urlString) { + var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/, + credentials = credentialsPattern.exec(urlString); + + return credentials && credentials[1]; +} + +function getBasicAuthHeader(urlString) { + var header = null; + + + // This is changed due to MS Windows doesn't support credentials in http uris + // so we detect them by regexp and strip off from result url + // Proof: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/a327cf3c-f033-4a54-8b7f-03c56ba3203f/windows-foundation-uri-security-problem + + if (window.btoa) { + var credentials = getUrlCredentials(urlString); + if (credentials) { + var authHeader = "Authorization"; + var authHeaderValue = "Basic " + window.btoa(credentials); + + header = { + name : authHeader, + value : authHeaderValue + }; + } + } + + return header; +} + +function convertHeadersToArray(headers) { + var result = []; + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + var headerValue = headers[header]; + result.push({ + name: header, + value: headerValue.toString() + }); + } + } + return result; +} + +var idCounter = 0; + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() { + this._id = ++idCounter; + this.onprogress = null; // optional callback +}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + var headers = null; + var httpMethod = null; + var basicAuthHeader = getBasicAuthHeader(server); + if (basicAuthHeader) { + server = server.replace(getUrlCredentials(server) + '@', ''); + + options = options || {}; + options.headers = options.headers || {}; + options.headers[basicAuthHeader.name] = basicAuthHeader.value; + } + + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + headers = options.headers; + httpMethod = options.httpMethod || "POST"; + if (httpMethod.toUpperCase() == "PUT"){ + httpMethod = "PUT"; + } else { + httpMethod = "POST"; + } + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + if (cordova.platformId === "windowsphone") { + headers = headers && convertHeadersToArray(headers); + params = params && convertHeadersToArray(params); + } + + var fail = errorCallback && function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception); + errorCallback(error); + }; + + var self = this; + var win = function(result) { + if (typeof result.lengthComputable != "undefined") { + if (self.onprogress) { + self.onprogress(newProgressEvent(result)); + } + } else { + if (successCallback) { + successCallback(result); + } + } + }; + exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false + * @param options {FileDownloadOptions} Optional parameters such as headers + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) { + argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments); + var self = this; + + var basicAuthHeader = getBasicAuthHeader(source); + if (basicAuthHeader) { + source = source.replace(getUrlCredentials(source) + '@', ''); + + options = options || {}; + options.headers = options.headers || {}; + options.headers[basicAuthHeader.name] = basicAuthHeader.value; + } + + var headers = null; + if (options) { + headers = options.headers || null; + } + + if (cordova.platformId === "windowsphone" && headers) { + headers = convertHeadersToArray(headers); + } + + var win = function(result) { + if (typeof result.lengthComputable != "undefined") { + if (self.onprogress) { + return self.onprogress(newProgressEvent(result)); + } + } else if (successCallback) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova-plugin-file.DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova-plugin-file.FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + entry.filesystem = new FileSystem(result.filesystemName || (result.filesystem == window.PERSISTENT ? 'persistent' : 'temporary')); + entry.nativeURL = result.nativeURL; + successCallback(entry); + } + }; + + var fail = errorCallback && function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception); + errorCallback(error); + }; + + exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]); +}; + +/** + * Aborts the ongoing file transfer on this object. The original error + * callback for the file transfer will be called if necessary. + */ +FileTransfer.prototype.abort = function() { + exec(null, null, 'FileTransfer', 'abort', [this._id]); +}; + +module.exports = FileTransfer; diff --git a/plugins/cordova-plugin-file-transfer/www/FileTransferError.js b/plugins/cordova-plugin-file-transfer/www/FileTransferError.js new file mode 100644 index 0000000..07ca831 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/FileTransferError.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. + * +*/ + +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status, body, exception) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; + this.body = body || null; + this.exception = exception || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; +FileTransferError.ABORT_ERR = 4; +FileTransferError.NOT_MODIFIED_ERR = 5; + +module.exports = FileTransferError; diff --git a/plugins/cordova-plugin-file-transfer/www/blackberry10/.jshintrc b/plugins/cordova-plugin-file-transfer/www/blackberry10/.jshintrc new file mode 100644 index 0000000..85ccb32 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/blackberry10/.jshintrc @@ -0,0 +1,5 @@ +{ + "globals": { + "requestAnimationFrame": true + } +} diff --git a/plugins/cordova-plugin-file-transfer/www/blackberry10/FileTransfer.js b/plugins/cordova-plugin-file-transfer/www/blackberry10/FileTransfer.js new file mode 100644 index 0000000..76e1682 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/blackberry10/FileTransfer.js @@ -0,0 +1,190 @@ +/* + * + * 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'), + FileTransferError = require('./FileTransferError'), + xhrImpl = require('./BB10XHRImplementation'); + + +function getBasicAuthHeader(urlString) { + var header = null; + + if (window.btoa) { + // parse the url using the Location object + var url = document.createElement('a'); + url.href = urlString; + + var credentials = null; + var protocol = url.protocol + "//"; + var origin = protocol + url.host; + + // check whether there are the username:password credentials in the url + if (url.href.indexOf(origin) !== 0) { // credentials found + var atIndex = url.href.indexOf("@"); + credentials = url.href.substring(protocol.length, atIndex); + } + + if (credentials) { + var authHeader = "Authorization"; + var authHeaderValue = "Basic " + window.btoa(credentials); + + header = { + name : authHeader, + value : authHeaderValue + }; + } + } + + return header; +} + +var idCounter = 0; + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() { + this._id = ++idCounter; + this.onprogress = null; // optional callback +}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + var headers = null; + var httpMethod = null; + var basicAuthHeader = getBasicAuthHeader(server); + if (basicAuthHeader) { + options = options || {}; + options.headers = options.headers || {}; + options.headers[basicAuthHeader.name] = basicAuthHeader.value; + } + + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + headers = options.headers; + httpMethod = options.httpMethod || "POST"; + if (httpMethod.toUpperCase() == "PUT"){ + httpMethod = "PUT"; + } else { + httpMethod = "POST"; + } + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = errorCallback && function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body); + errorCallback(error); + }; + + var self = this; + var win = function(result) { + if (typeof result.lengthComputable != "undefined") { + if (self.onprogress) { + self.onprogress(result); + } + } else { + if (successCallback) { + successCallback(result); + } + } + }; + xhrImpl.upload(win, fail, [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false + * @param options {FileDownloadOptions} Optional parameters such as headers + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) { + argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments); + var self = this; + + var basicAuthHeader = getBasicAuthHeader(source); + if (basicAuthHeader) { + options = options || {}; + options.headers = options.headers || {}; + options.headers[basicAuthHeader.name] = basicAuthHeader.value; + } + + var headers = null; + if (options) { + headers = options.headers || null; + } + + var win = function(result) { + if (typeof result.lengthComputable != "undefined") { + if (self.onprogress) { + return self.onprogress(result); + } + } else if (successCallback) { + successCallback(result); + } + }; + + var fail = errorCallback && function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body); + errorCallback(error); + }; + + xhrImpl.download(win, fail, [source, target, trustAllHosts, this._id, headers]); +}; + +/** + * Aborts the ongoing file transfer on this object. The original error + * callback for the file transfer will be called if necessary. + */ +FileTransfer.prototype.abort = function() { + xhrImpl.abort(null, null, [this._id]); +}; + +module.exports = FileTransfer; diff --git a/plugins/cordova-plugin-file-transfer/www/blackberry10/FileTransferProxy.js b/plugins/cordova-plugin-file-transfer/www/blackberry10/FileTransferProxy.js new file mode 100644 index 0000000..a363f07 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/blackberry10/FileTransferProxy.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. + * +*/ + +/* + * FileTransferProxy + * + * Register all FileTransfer exec calls to be handled by proxy + */ + +var xhrFileTransfer = require('cordova-plugin-file-transfer.xhrFileTransfer'); + +module.exports = { + abort: xhrFileTransfer.abort, + download: xhrFileTransfer.download, + upload: xhrFileTransfer.upload +}; + +require('cordova/exec/proxy').add('FileTransfer', module.exports); diff --git a/plugins/cordova-plugin-file-transfer/www/blackberry10/xhrFileTransfer.js b/plugins/cordova-plugin-file-transfer/www/blackberry10/xhrFileTransfer.js new file mode 100644 index 0000000..53d0d36 --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/blackberry10/xhrFileTransfer.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. + * +*/ + +/* global Blob:false */ + +var cordova = require('cordova'), + resolve = cordova.require('cordova-plugin-file.resolveLocalFileSystemURIProxy'), + requestAnimationFrame = cordova.require('cordova-plugin-file.bb10RequestAnimationFrame'), + xhr = {}; + +function getParentPath(filePath) { + var pos = filePath.lastIndexOf('/'); + return filePath.substring(0, pos + 1); +} + +function getFileName(filePath) { + var pos = filePath.lastIndexOf('/'); + return filePath.substring(pos + 1); +} + +function checkURL(url) { + return url.indexOf(' ') === -1 ? true : false; +} + +module.exports = { + abort: function (win, fail, args) { + var id = args[0]; + if (xhr[id]) { + xhr[id].abort(); + if (typeof(win) === 'function') { + win(); + } + } else if (typeof(fail) === 'function') { + fail(); + } + }, + + upload: function(win, fail, args) { + var filePath = args[0], + server = args[1], + fileKey = args[2], + fileName = args[3], + mimeType = args[4], + params = args[5], + /*trustAllHosts = args[6],*/ + chunkedMode = args[7], + headers = args[8], + onSuccess = function (data) { + if (typeof(win) === 'function') { + win(data); + } + }, + onFail = function (code) { + delete xhr[fileKey]; + if (typeof(fail) === 'function') { + fail(code); + } + }; + + if (!checkURL(server)) { + onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, server, filePath)); + } + + xhr[fileKey] = new XMLHttpRequest(); + xhr[fileKey].onabort = function () { + onFail(new FileTransferError(FileTransferError.ABORT_ERR, server, filePath, this.status, xhr[fileKey].response)); + }; + + resolve(function(entry) { + requestAnimationFrame(function () { + entry.nativeEntry.file(function(file) { + function uploadFile(blobFile) { + var fd = new FormData(); + + fd.append(fileKey, blobFile, fileName); + for (var prop in params) { + if(params.hasOwnProperty(prop)) { + fd.append(prop, params[prop]); + } + } + + xhr[fileKey].open("POST", server); + xhr[fileKey].onload = function(evt) { + if (xhr[fileKey].status === 200) { + var result = new FileUploadResult(); + result.bytesSent = file.size; + result.responseCode = xhr[fileKey].status; + result.response = xhr[fileKey].response; + delete xhr[fileKey]; + onSuccess(result); + } else if (xhr[fileKey].status === 404) { + onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response)); + } else { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response)); + } + }; + xhr[fileKey].ontimeout = function(evt) { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response)); + }; + xhr[fileKey].onerror = function () { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, this.status, xhr[fileKey].response)); + }; + xhr[fileKey].upload.onprogress = function (evt) { + if (evt.loaded > 0) { + onSuccess(evt); + } + }; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + xhr[fileKey].setRequestHeader(header, headers[header]); + } + } + + requestAnimationFrame(function () { + xhr[fileKey].send(fd); + }); + } + + var bytesPerChunk; + if (chunkedMode === true) { + bytesPerChunk = 1024 * 1024; // 1MB chunk sizes. + } else { + bytesPerChunk = file.size; + } + var start = 0; + var end = bytesPerChunk; + while (start < file.size) { + var chunk = file.slice(start, end, mimeType); + uploadFile(chunk); + start = end; + end = start + bytesPerChunk; + } + }, function(error) { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath)); + }); + }); + }, function(error) { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath)); + }, [filePath]); + }, + + download: function (win, fail, args) { + var source = args[0], + target = args[1], + id = args[3], + headers = args[4], + fileWriter, + onSuccess = function (entry) { + if (typeof(win) === 'function') { + win(entry); + } + }, + onFail = function (error) { + var reader; + delete xhr[id]; + if (typeof(fail) === 'function') { + if (error && error.body && typeof(error.body) === 'object') { + reader = new FileReader()._realReader; + reader.onloadend = function () { + error.body = this.result; + fail(error); + }; + reader.onerror = function () { + fail(error); + }; + reader.readAsText(error.body); + } else { + fail(error); + } + } + }; + + if (!checkURL(source)) { + onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target)); + } + + xhr[id] = new XMLHttpRequest(); + + function writeFile(entry) { + entry.createWriter(function (writer) { + fileWriter = writer; + fileWriter.onwriteend = function (evt) { + if (!evt.target.error) { + entry.filesystemName = entry.filesystem.name; + delete xhr[id]; + onSuccess(entry); + } else { + onFail(evt.target.error); + } + }; + fileWriter.onerror = function (evt) { + onFail(evt.target.error); + }; + fileWriter.write(new Blob([xhr[id].response])); + }, function (error) { + onFail(error); + }); + } + + xhr[id].onerror = function (e) { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response)); + }; + + xhr[id].onabort = function (e) { + onFail(new FileTransferError(FileTransferError.ABORT_ERR, source, target, xhr[id].status, xhr[id].response)); + }; + + xhr[id].onload = function () { + if (xhr[id].readyState === xhr[id].DONE) { + if (xhr[id].status === 200 && xhr[id].response) { + resolveLocalFileSystemURI(getParentPath(target), function (dir) { + dir.getFile(getFileName(target), {create: true}, writeFile, function (error) { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response)); + }); + }, function (error) { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response)); + }); + } else if (xhr[id].status === 404) { + onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target, xhr[id].status, xhr[id].response)); + } else { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response)); + } + } + }; + xhr[id].onprogress = function (evt) { + onSuccess(evt); + }; + xhr[id].open("GET", source, true); + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + xhr[id].setRequestHeader(header, headers[header]); + } + } + xhr[id].responseType = "blob"; + requestAnimationFrame(function () { + if (xhr[id]) { + xhr[id].send(); + } + }); + } +}; diff --git a/plugins/cordova-plugin-file-transfer/www/browser/FileTransfer.js b/plugins/cordova-plugin-file-transfer/www/browser/FileTransfer.js new file mode 100644 index 0000000..14868ac --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/browser/FileTransfer.js @@ -0,0 +1,344 @@ +/* + * + * 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 module, require*/ + +var argscheck = require('cordova/argscheck'), + FileTransferError = require('./FileTransferError'); + +function getParentPath(filePath) { + var pos = filePath.lastIndexOf('/'); + return filePath.substring(0, pos + 1); +} + +function getFileName(filePath) { + var pos = filePath.lastIndexOf('/'); + return filePath.substring(pos + 1); +} + +function getUrlCredentials(urlString) { + var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/, + credentials = credentialsPattern.exec(urlString); + + return credentials && credentials[1]; +} + +function getBasicAuthHeader(urlString) { + var header = null; + + + // This is changed due to MS Windows doesn't support credentials in http uris + // so we detect them by regexp and strip off from result url + // Proof: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/a327cf3c-f033-4a54-8b7f-03c56ba3203f/windows-foundation-uri-security-problem + + if (window.btoa) { + var credentials = getUrlCredentials(urlString); + if (credentials) { + var authHeader = "Authorization"; + var authHeaderValue = "Basic " + window.btoa(credentials); + + header = { + name : authHeader, + value : authHeaderValue + }; + } + } + + return header; +} + +function checkURL(url) { + return url.indexOf(' ') === -1 ? true : false; +} + +var idCounter = 0; + +var transfers = {}; + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() { + this._id = ++idCounter; + this.onprogress = null; // optional callback +}; + +/** + * Given an absolute file path, uploads a file on the device to a remote server + * using a multipart HTTP request. + * @param filePath {String} Full path of the file on the device + * @param server {String} URL of the server to receive the file + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + * @param options {FileUploadOptions} Optional parameters such as file name and mimetype + * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false + */ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options) { + // check for arguments + argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments); + + // Check if target URL doesn't contain spaces. If contains, it should be escaped first + // (see https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#upload) + if (!checkURL(server)) { + if (errorCallback) { + errorCallback(new FileTransferError(FileTransferError.INVALID_URL_ERR, filePath, server)); + } + return; + } + + options = options || {}; + + var fileKey = options.fileKey || "file"; + var fileName = options.fileName || "image.jpg"; + var mimeType = options.mimeType || "image/jpeg"; + var params = options.params || {}; + var withCredentials = options.withCredentials || false; + // var chunkedMode = !!options.chunkedMode; // Not supported + var headers = options.headers || {}; + var httpMethod = options.httpMethod && options.httpMethod.toUpperCase() === "PUT" ? "PUT" : "POST"; + + var basicAuthHeader = getBasicAuthHeader(server); + if (basicAuthHeader) { + server = server.replace(getUrlCredentials(server) + '@', ''); + headers[basicAuthHeader.name] = basicAuthHeader.value; + } + + var that = this; + var xhr = transfers[this._id] = new XMLHttpRequest(); + xhr.withCredentials = withCredentials; + + var fail = errorCallback && function(code, status, response) { + if (transfers[this._id]) { + delete transfers[this._id]; + } + var error = new FileTransferError(code, filePath, server, status, response); + if (errorCallback) { + errorCallback(error); + } + }; + + window.resolveLocalFileSystemURL(filePath, function(entry) { + entry.file(function(file) { + var reader = new FileReader(); + reader.onloadend = function() { + var blob = new Blob([this.result], {type: mimeType}); + + // Prepare form data to send to server + var fd = new FormData(); + fd.append(fileKey, blob, fileName); + for (var prop in params) { + if (params.hasOwnProperty(prop)) { + fd.append(prop, params[prop]); + } + } + + xhr.open(httpMethod, server); + + // Fill XHR headers + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header, headers[header]); + } + } + + xhr.onload = function() { + if (this.status === 200) { + var result = new FileUploadResult(); // jshint ignore:line + result.bytesSent = blob.size; + result.responseCode = this.status; + result.response = this.response; + delete transfers[that._id]; + successCallback(result); + } else if (this.status === 404) { + fail(FileTransferError.INVALID_URL_ERR, this.status, this.response); + } else { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + } + }; + + xhr.ontimeout = function() { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + }; + + xhr.onerror = function() { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + }; + + xhr.onabort = function () { + fail(FileTransferError.ABORT_ERR, this.status, this.response); + }; + + xhr.upload.onprogress = function (e) { + if (that.onprogress) { + that.onprogress(e); + } + }; + + xhr.send(fd); + // Special case when transfer already aborted, but XHR isn't sent. + // In this case XHR won't fire an abort event, so we need to check if transfers record + // isn't deleted by filetransfer.abort and if so, call XHR's abort method again + if (!transfers[that._id]) { + xhr.abort(); + } + }; + reader.readAsArrayBuffer(file); + }, function() { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + }); + }, function() { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + }); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false + * @param options {FileDownloadOptions} Optional parameters such as headers + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) { + argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments); + + // Check if target URL doesn't contain spaces. If contains, it should be escaped first + // (see https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#download) + if (!checkURL(source)) { + if (errorCallback) { + errorCallback(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target)); + } + return; + } + + options = options || {}; + + var headers = options.headers || {}; + var withCredentials = options.withCredentials || false; + + var basicAuthHeader = getBasicAuthHeader(source); + if (basicAuthHeader) { + source = source.replace(getUrlCredentials(source) + '@', ''); + headers[basicAuthHeader.name] = basicAuthHeader.value; + } + + var that = this; + var xhr = transfers[this._id] = new XMLHttpRequest(); + xhr.withCredentials = withCredentials; + var fail = errorCallback && function(code, status, response) { + if (transfers[that._id]) { + delete transfers[that._id]; + } + // In XHR GET reqests we're setting response type to Blob + // but in case of error we need to raise event with plain text response + if (response instanceof Blob) { + var reader = new FileReader(); + reader.readAsText(response); + reader.onloadend = function(e) { + var error = new FileTransferError(code, source, target, status, e.target.result); + errorCallback(error); + }; + } else { + var error = new FileTransferError(code, source, target, status, response); + errorCallback(error); + } + }; + + xhr.onload = function (e) { + + var fileNotFound = function () { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + }; + + var req = e.target; + // req.status === 0 is special case for local files with file:// URI scheme + if ((req.status === 200 || req.status === 0) && req.response) { + window.resolveLocalFileSystemURL(getParentPath(target), function (dir) { + dir.getFile(getFileName(target), {create: true}, function writeFile(entry) { + entry.createWriter(function (fileWriter) { + fileWriter.onwriteend = function (evt) { + if (!evt.target.error) { + entry.filesystemName = entry.filesystem.name; + delete transfers[that._id]; + if (successCallback) { + successCallback(entry); + } + } else { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + } + }; + fileWriter.onerror = function () { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + }; + fileWriter.write(req.response); + }, fileNotFound); + }, fileNotFound); + }, fileNotFound); + } else if (req.status === 404) { + fail(FileTransferError.INVALID_URL_ERR, req.status, req.response); + } else { + fail(FileTransferError.CONNECTION_ERR, req.status, req.response); + } + }; + + xhr.onprogress = function (e) { + if (that.onprogress) { + that.onprogress(e); + } + }; + + xhr.onerror = function () { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + }; + + xhr.onabort = function () { + fail(FileTransferError.ABORT_ERR, this.status, this.response); + }; + + xhr.open("GET", source, true); + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header, headers[header]); + } + } + + xhr.responseType = "blob"; + + xhr.send(); +}; + +/** + * Aborts the ongoing file transfer on this object. The original error + * callback for the file transfer will be called if necessary. + */ +FileTransfer.prototype.abort = function() { + if (this instanceof FileTransfer) { + if (transfers[this._id]) { + transfers[this._id].abort(); + delete transfers[this._id]; + } + } +}; + +module.exports = FileTransfer; diff --git a/plugins/cordova-plugin-file-transfer/www/firefoxos/FileTransferProxy.js b/plugins/cordova-plugin-file-transfer/www/firefoxos/FileTransferProxy.js new file mode 100644 index 0000000..86c46be --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/firefoxos/FileTransferProxy.js @@ -0,0 +1,222 @@ +/* + * + * 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 FileTransferError = require('./FileTransferError'), + xhr = {}; + +function getParentPath(filePath) { + var pos = filePath.lastIndexOf('/'); + return filePath.substring(0, pos + 1); +} + +function getFileName(filePath) { + var pos = filePath.lastIndexOf('/'); + return filePath.substring(pos + 1); +} + +module.exports = { + abort: function (successCallback, errorCallback, args) { + var id = args[0]; + if (xhr[id]) { + xhr[id].abort(); + if (typeof(successCallback) === 'function') { + successCallback(); + } + } else if (typeof(errorCallback) === 'function') { + errorCallback(); + } + }, + + upload: function(successCallback, errorCallback, args) { + var filePath = args[0], + server = args[1], + fileKey = args[2], + fileName = args[3], + mimeType = args[4], + params = args[5], + /*trustAllHosts = args[6],*/ + /*chunkedMode = args[7],*/ + headers = args[8]; + + xhr[fileKey] = new XMLHttpRequest({mozSystem: true}); + xhr[fileKey].onabort = function() { + onFail(new FileTransferError(FileTransferError.ABORT_ERR, server, filePath, this.status, xhr[fileKey].response)); + }; + + window.resolveLocalFileSystemURL(filePath, function(entry) { + entry.file(function(file) { + var reader = new FileReader(); + + reader.onloadend = function() { + var blob = new Blob([this.result], {type: mimeType}); + var fd = new FormData(); + + fd.append(fileKey, blob, fileName); + + for (var prop in params) { + if (params.hasOwnProperty(prop)) { + fd.append(prop, params[prop]); + } + } + + xhr[fileKey].open("POST", server); + + xhr[fileKey].onload = function(evt) { + if (xhr[fileKey].status === 200) { + var result = new FileUploadResult(); + result.bytesSent = blob.size; + result.responseCode = xhr[fileKey].status; + result.response = xhr[fileKey].response; + delete xhr[fileKey]; + onSuccess(result); + } else if (xhr[fileKey].status === 404) { + onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response)); + } else { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response)); + } + }; + + xhr[fileKey].ontimeout = function() { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response)); + }; + + xhr[fileKey].onerror = function() { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, this.status, xhr[fileKey].response)); + }; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + xhr[fileKey].setRequestHeader(header, headers[header]); + } + } + + xhr[fileKey].send(fd); + }; + + reader.readAsArrayBuffer(file); + + }, function() { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath)); + }); + }, function() { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath)); + }); + + function onSuccess(data) { + if (typeof(successCallback) === 'function') { + successCallback(data); + } + } + + function onFail(code) { + delete xhr[fileKey]; + if (typeof(errorCallback) === 'function') { + errorCallback(code); + } + } + }, + + download: function (successCallback, errorCallback, args) { + var source = args[0], + target = args[1], + id = args[3], + headers = args[4]; + + xhr[id] = new XMLHttpRequest({mozSystem: true}); + + xhr[id].onload = function () { + if (xhr[id].readyState === xhr[id].DONE) { + if (xhr[id].status === 200 && xhr[id].response) { + window.resolveLocalFileSystemURL(getParentPath(target), function (dir) { + dir.getFile(getFileName(target), {create: true}, writeFile, function (error) { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response)); + }); + }, function () { + onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response)); + }); + } else if (xhr[id].status === 404) { + onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target, xhr[id].status, xhr[id].response)); + } else { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response)); + } + } + }; + + function writeFile(entry) { + entry.createWriter(function (fileWriter) { + fileWriter.onwriteend = function (evt) { + if (!evt.target.error) { + entry.filesystemName = entry.filesystem.name; + delete xhr[id]; + onSuccess(entry); + } else { + onFail(evt.target.error); + } + }; + fileWriter.onerror = function (evt) { + onFail(evt.target.error); + }; + fileWriter.write(new Blob([xhr[id].response])); + }, function (error) { + onFail(error); + }); + } + + xhr[id].onerror = function (e) { + onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response)); + }; + + xhr[id].onabort = function (e) { + onFail(new FileTransferError(FileTransferError.ABORT_ERR, source, target, xhr[id].status, xhr[id].response)); + }; + + xhr[id].open("GET", source, true); + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + xhr[id].setRequestHeader(header, headers[header]); + } + } + + xhr[id].responseType = "blob"; + + setTimeout(function () { + if (xhr[id]) { + xhr[id].send(); + } + }, 0); + + function onSuccess(entry) { + if (typeof(successCallback) === 'function') { + successCallback(entry); + } + } + + function onFail(error) { + delete xhr[id]; + if (typeof(errorCallback) === 'function') { + errorCallback(error); + } + } + } +}; + +require('cordova/exec/proxy').add('FileTransfer', module.exports); diff --git a/plugins/cordova-plugin-file-transfer/www/wp7/base64.js b/plugins/cordova-plugin-file-transfer/www/wp7/base64.js new file mode 100644 index 0000000..221e30a --- /dev/null +++ b/plugins/cordova-plugin-file-transfer/www/wp7/base64.js @@ -0,0 +1,73 @@ +/* + * + * 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. + * +*/ + +// jshint ignore: start + +var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', + INVALID_CHARACTER_ERR = (function () { + // fabricate a suitable error object + try { document.createElement('$'); } + catch (error) { return error; } + }()); + + // encoder + // [https://gist.github.com/999166] by [https://github.com/nignag] + window.btoa || ( + window.btoa = function (input) { + for ( + // initialize result and counter + var block, charCode, idx = 0, map = chars, output = ''; + // if the next input index does not exist: + // change the mapping table to "=" + // check if d has no fractional digits + input.charAt(idx | 0) || (map = '=', idx % 1) ; + // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 + output += map.charAt(63 & block >> 8 - idx % 1 * 8) + ) { + charCode = input.charCodeAt(idx += 3 / 4); + if (charCode > 0xFF) throw INVALID_CHARACTER_ERR; + block = block << 8 | charCode; + } + return output; + }); + + // decoder + // [https://gist.github.com/1020396] by [https://github.com/atk] + window.atob || ( + window.atob = function (input) { + input = input.replace(/=+$/, '') + if (input.length % 4 == 1) throw INVALID_CHARACTER_ERR; + for ( + // initialize result and counters + var bc = 0, bs, buffer, idx = 0, output = ''; + // get next character + buffer = input.charAt(idx++) ; + // character found in table? initialize bit storage and add its ascii value; + ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, + // and if not first of each 4 characters, + // convert the first 8 bits to one ascii character + bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 + ) { + // try to find character in table (0-63, not found => -1) + buffer = chars.indexOf(buffer); + } + return output; + }); \ No newline at end of file diff --git a/plugins/cordova-plugin-file/CONTRIBUTING.md b/plugins/cordova-plugin-file/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-file/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# 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/plugins/cordova-plugin-file/LICENSE b/plugins/cordova-plugin-file/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/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/plugins/cordova-plugin-file/NOTICE b/plugins/cordova-plugin-file/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/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/plugins/cordova-plugin-file/README.md b/plugins/cordova-plugin-file/README.md new file mode 100644 index 0000000..8a50749 --- /dev/null +++ b/plugins/cordova-plugin-file/README.md @@ -0,0 +1,868 @@ +--- +title: File +description: Read/write files on the device. +--- + + +|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI | +|:-:|:-:|:-:|:-:|:-:|:-:| +|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-file)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-file/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-file)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-file/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-file)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-file/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-file)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-file/)|[![Build Status](http://cordova-ci.cloudapp.net:8080/buildStatus/icon?job=cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-file)](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=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); + } + +Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20File%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + + cordova plugin add cordova-plugin-file + +## Supported Platforms + +- Amazon Fire OS +- Android +- BlackBerry 10 +- Firefox OS +- iOS +- OS X +- Windows Phone 7 and 8* +- Windows 8* +- 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//` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Yes | +|    `appname.app/` | applicationDirectory | bundle | r | N/A | N/A | N/A | Yes | +|       `www/` | - | - | r | N/A | N/A | N/A | Yes | +|    `Documents/` | documentsDirectory | documents | r/w | Yes | No | Yes | Yes | +|       `NoCloud/` | - | documents-nosync | r/w | Yes | No | No | Yes | +|    `Library` | - | library | r/w | Yes | No | Yes? | Yes | +|       `NoCloud/` | dataDirectory | library-nosync | r/w | Yes | No | No | Yes | +|       `Cloud/` | syncedDataDirectory | - | r/w | Yes | No | Yes | Yes | +|       `Caches/` | cacheDirectory | cache | r/w | Yes* | Yes\*\*\*| No | Yes | +|    `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//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | +|    `cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes | +|    `files` | dataDirectory | files | r/w | Yes | No | Yes | +|       `Documents` | | documents | r/w | Yes | No | Yes | +| `/` | externalRootDirectory | sdcard | r/w | Yes | No | No | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Yes | No | No | +|       `cache` | externalCacheDirectry | cache-external | r/w | Yes | No\*\*| No | +|       `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`. + +### BlackBerry 10 File System Layout + +| Device Path | `cordova.file.*` | r/w? | persistent? | OS clears | private | +|:-------------------------------------------------------------|:----------------------------|:----:|:-----------:|:---------:|:-------:| +| `file:///accounts/1000/appdata//` | applicationStorageDirectory | r | N/A | N/A | Yes | +|    `app/native` | applicationDirectory | r | N/A | N/A | Yes | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | No | Yes | Yes | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Yes | No | Yes | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | Yes | No | No | +| `file:///accounts/1000/shared` | sharedDirectory | r/w | Yes | No | No | + +*Note*: When application is deployed to work perimeter, all paths are relative to /accounts/1000-enterprise. + +### OS X File System Layout + +| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | OS clears | private | +|:-------------------------------------------------|:----------------------------|:----------------------|:----:|:---------:|:-------:| +| `/Applications/.app/` | - | bundle | r | N/A | Yes | +|     `Content/Resources/` | applicationDirectory | - | r | N/A | Yes | +| `~/Library/Application Support//` | applicationStorageDirectory | - | r/w | No | Yes | +|     `files/` | dataDirectory | - | r/w | No | Yes | +| `~/Documents/` | documentsDirectory | documents | r/w | No | No | +| `~/Library/Caches//` | 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//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 | +|    `local/` | dataDirectory | r/w | Yes | No | Yes | +|    `temp/` | cacheDirectory | r/w | No | Yes\* | Yes | +|    `temp/` | tempDirectory | r/w | No | Yes\* | Yes | +|    `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/`, 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`: + + + + + +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`: + + + + + +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. + +## Firefox OS Quirks + +The File System API is not natively supported by Firefox OS and is implemented +as a shim on top of indexedDB. + +* Does not fail when removing non-empty directories +* Does not support metadata for directories +* Methods `copyTo` and `moveTo` do not support directories + +The following data paths are supported: +* `applicationDirectory` - Uses `xhr` to get local files that are packaged with the app. +* `dataDirectory` - For persistent app-specific data files. +* `cacheDirectory` - Cached files that should survive app restarts (Apps should not rely +on the OS to delete files in here). + +## 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:;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//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 + +``` + +__Note__: This method requires following Content Security rules updates: +* Add `cdvfile:` scheme to `Content-Security-Policy` meta tag of the index page, e.g.: + - `` +* Add `` 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 tag in `config.xml` which names the +filesystems to be installed. By default, all file-system roots are enabled. + + + + +### 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 + +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 + +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 + +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.requestLocalFileSystemURL, 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 + +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 + +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 + +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 + +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 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 + +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 element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your element. Here is an example. + +```html + +``` + +## Create Directories + +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/plugins/cordova-plugin-file/RELEASENOTES.md b/plugins/cordova-plugin-file/RELEASENOTES.md new file mode 100644 index 0000000..fd57274 --- /dev/null +++ b/plugins/cordova-plugin-file/RELEASENOTES.md @@ -0,0 +1,444 @@ + +# Release Notes + +### 4.3.1 (Dec 07, 2016) +* [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 `#` 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/plugins/cordova-plugin-file/doc/de/README.md b/plugins/cordova-plugin-file/doc/de/README.md new file mode 100644 index 0000000..242a2f7 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/de/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +Dieses Plugin implementiert eine File-API, die Lese-/Schreibzugriff Zugriff auf Dateien, die auf dem Gerät befinden. + +Dieses Plugin basiert auf mehrere Angaben, einschließlich: die HTML5-File-API + +Die (heute nicht mehr existierenden) Verzeichnisse und System neuesten Erweiterungen: , obwohl die meisten von den Plugin-Code wurde geschrieben, als eine frühere Spec aktuell waren: + +Es implementiert auch die FileWriter Spec: + +Verwendung finden Sie in HTML5 Rocks ausgezeichnete [Dateisystem Artikel.](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +Finden Sie einen Überblick über andere Speicheroptionen Cordovas [Speicher-Führer](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html). + +Dieses Plugin wird global `cordova.file`-Objekt definiert. + +Obwohl im globalen Gültigkeitsbereich, steht es nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Installation + + cordova plugin add cordova-plugin-file + + +## Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 und 8 * + * Windows 8 * + * Windows* + * Browser + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## Wo Dateien gespeichert + +Stand: V1 werden URLs auf wichtige Datei-System-Verzeichnisse zur Verfügung gestellt. Jede URL in der Form *file:///path/to/spot/* ist, und ein `DirectoryEntry` mit `window.resolveLocalFileSystemURL()` konvertiert werden können. + + * `cordova.file.applicationDirectory`-Die schreibgeschützten Verzeichnis, in dem die Anwendung installiert ist. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.applicationStorageDirectory`-Root-Verzeichnis der Anwendungs-Sandbox; auf iOS ist schreibgeschützt (aber bestimmte Unterverzeichnisse [wie `/Documents` ] sind Lese-und Schreibzugriff). Alle enthaltene Daten ist für die app privat. ( *iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.dataDirectory`-Beständige und private Datenspeicherung innerhalb der Anwendungs-Sandbox, die mit internen Speicher (auf Android, externen Speicher verwenden, verwenden Sie `.externalDataDirectory` ). Auf iOS, ist dieses Verzeichnis nicht mit iCloud synchronisiert (verwenden Sie `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.cacheDirectory`-Verzeichnis der zwischengespeicherten Daten-Dateien oder Dateien, die Ihre app einfach neu erstellen können. Das Betriebssystem kann diese Dateien löschen, wenn das Gerät auf Speicher knapp wird, dennoch sollten die apps vom Betriebssystem zum Löschen von Dateien hier nicht verlassen. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.externalApplicationStorageDirectory`-Anwendungsraum auf externen Speicher. (*Android*) + + * `cordova.file.externalDataDirectory`-Wo, app-spezifische Datendateien auf externen Speicher setzen. (*Android*) + + * `cordova.file.externalCacheDirectory`-Anwendungscache auf externen Speicher. (*Android*) + + * `cordova.file.externalRootDirectory`-Externer Speicher (SD-Karte) Stamm. (*Android*, *BlackBerry 10*) + + * `cordova.file.tempDirectory`-Temp-Verzeichnis, dem das OS auf deaktivieren können wird. Verlassen Sie sich nicht auf das Betriebssystem, um dieses Verzeichnis zu löschen; Ihre Anwendung sollte immer Dateien gegebenenfalls entfernen. (*iOS*) + + * `cordova.file.syncedDataDirectory`-Hält app-spezifische Dateien, die (z. B. auf iCloud) synchronisiert werden sollten. (*iOS*) + + * `cordova.file.documentsDirectory`-Dateien für die app, aber privat sind sinnvoll, andere Anwendungen (z.B. Office-Dateien). (*iOS*) + + * `cordova.file.sharedDirectory`-Dateien für alle Anwendungen (*BlackBerry 10* weltweit verfügbar) + +## Dateisystemlayouts + +Obwohl technisch ein Implementierungsdetail, kann es sehr hilfreich zu wissen, wie die `cordova.file.*`-Eigenschaften physikalische Pfade auf einem echten Gerät zugeordnet sein. + +### iOS-Datei-System-Layout + +| Gerätepfad | `Cordova.file.*` | `iosExtraFileSystems` | R/w? | persistent? | OS löscht | Sync | Private | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:----:|:-----------:|:------------:|:----:|:-------:| +| `/ Var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Ja | +|    `appname.app/` | applicationDirectory | Bundle | r | N/A | N/A | N/A | Ja | +|       `www/` | - | - | r | N/A | N/A | N/A | Ja | +|    `Documents/` | documentsDirectory | Dokumente | R/w | Ja | Nein | Ja | Ja | +|       `NoCloud/` | - | Dokumente-nosync | R/w | Ja | Nein | Nein | Ja | +|    `Library` | - | Bibliothek | R/w | Ja | Nein | Ja? | Ja | +|       `NoCloud/` | dataDirectory | Bibliothek-nosync | R/w | Ja | Nein | Nein | Ja | +|       `Cloud/` | syncedDataDirectory | - | R/w | Ja | Nein | Ja | Ja | +|       `Caches/` | cacheDirectory | Cache | R/w | Ja * | Ja**\* | Nein | Ja | +|    `tmp/` | tempDirectory | - | R/w | Nein** | Ja**\* | Nein | Ja | + +\ * Dateien über app-Neustarts und Upgrades beibehalten, aber dieses Verzeichnis kann gelöscht werden, wenn das OS begehrt. Ihre Anwendung sollte in der Lage, alle Inhalte neu zu erstellen, die gelöscht werden können. + +** -Dateien kann über app-Neustarts beizubehalten, aber verlasse dich nicht auf dieses Verhalten. Dateien sind nicht unbedingt Aktuelles beibehalten. Ihre Anwendung sollte Dateien aus diesem Verzeichnis entfernen, wenn es gilt, diese Dateien werden entfernt, da das OS nicht wann (oder auch wenn) garantiert. + +**\ * The OS kann den Inhalt dieses Verzeichnisses löschen, wann immer es sich anfühlt, ist es erforderlich, aber verlassen Sie sich nicht dazu. Sie sollten dieses Verzeichnis entsprechend Ihrer Anwendung deaktivieren. + +### Android File System-Layout + +| Gerätepfad | `Cordova.file.*` | `AndroidExtraFileSystems` | R/w? | persistent? | OS löscht | Private | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:----:|:-----------:|:----------:|:-------:| +| `file:///android_asset/` | applicationDirectory | | r | N/A | N/A | Ja | +| `/ Data/Data/< app-Id > /` | applicationStorageDirectory | - | R/w | N/A | N/A | Ja | +|    `cache` | cacheDirectory | Cache | R/w | Ja | Ja\* | Ja | +|    `files` | dataDirectory | Dateien | R/w | Ja | Nein | Ja | +|       `Documents` | | Dokumente | R/w | Ja | Nein | Ja | +| `< Sdcard > /` | externalRootDirectory | sdcard | R/w | Ja | Nein | Nein | +|    `Android/data//` | externalApplicationStorageDirectory | - | R/w | Ja | Nein | Nein | +|       `cache` | externalCacheDirectry | Cache-extern | R/w | Ja | Nein** | Nein | +|       `files` | externalDataDirectory | Dateien-extern | R/w | Ja | Nein | Nein | + +\ * OS kann regelmäßig dieses Verzeichnis zu löschen, aber verlasse dich nicht auf dieses Verhalten. Deaktivieren Sie den Inhalt dieses Verzeichnisses für Ihre Anwendung geeigneten. Ein Benutzer den Cache manuell löschen sollte, werden die Inhalte dieses Verzeichnisses entfernt. + +** Der OS nicht klar dieses Verzeichnis automatisch; Sie sind verantwortlich für die Inhalte selbst verwalten. Der Benutzer den Cache manuell löschen sollte, werden der Inhalt des Verzeichnisses entfernt. + +**Hinweis**: Wenn externe Speichergeräte nicht bereitgestellt werden kann, sind die `cordova.file.external*` Eigenschaften `null`. + +### BlackBerry 10-File-System-Layout + +| Gerätepfad | `Cordova.file.*` | R/w? | persistent? | OS löscht | Private | +|:----------------------------------------------------------- |:--------------------------- |:----:|:-----------:|:---------:|:-------:| +| `file:///Accounts/1000/APPDATA/ < app Id > /` | applicationStorageDirectory | r | N/A | N/A | Ja | +|    `app/native` | applicationDirectory | r | N/A | N/A | Ja | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | R/w | Nein | Ja | Ja | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | R/w | Ja | Nein | Ja | +| `file:///Accounts/1000/Removable/sdcard` | externalRemovableDirectory | R/w | Ja | Nein | Nein | +| `file:///Accounts/1000/Shared` | sharedDirectory | R/w | Ja | Nein | Nein | + +*Hinweis*: Wenn die Anwendung bereitgestellt wird, um Perimeter zu arbeiten, alle Pfade sind relativ /accounts/1000-enterprise. + +## Android Eigenarten + +### Android permanenten Speicherort + +Es gibt mehrere gültige Speicherorte, persistente Dateien auf einem Android-Gerät zu speichern. Finden Sie auf [dieser Seite](http://developer.android.com/guide/topics/data/data-storage.html) eine ausführliche Diskussion über die verschiedenen Möglichkeiten. + +Frühere Versionen des Plugins wählen würde, den Speicherort der temporären und permanenten Dateien beim Start, basierend auf, ob das Gerät behauptete, dass die SD-Karte (oder gleichwertige Speicherpartition) bereitgestellt wurde. Wenn die SD-Karte eingelegt wurde, oder wenn eine große interne Speicherpartition verfügbar war (wie auf Nexus-Geräten) und dann in die Wurzel dieses Raumes, die persistenten Dateien gespeichert werden. Dies bedeutete, dass alle Cordova apps aller verfügbaren Dateien auf der Karte sehen konnte. + +Wenn die SD-Karte nicht verfügbar war, dann Vorgängerversionen Daten unter speichern würde `/data/data/`, die isoliert Anwendungen voneinander, aber möglicherweise noch Ursache Daten zwischen Benutzern freigegeben werden. + +Es ist jetzt möglich, ob Sie Dateien der internen Datei-Speicherort oder unter Verwendung der bisherigen Logik, mit einer Präferenz in der Anwendung-`config.xml`-Datei speichern möchten. Hierzu fügen Sie eine dieser zwei Zeilen zu `"config.xml"`: + + + + + + +Ohne diese Zeile wird das Datei Plugin `Compatibility` als Standard verwenden. Wenn ein Präferenz-Tag vorhanden ist, und nicht einen der folgenden Werte, wird die Anwendung nicht gestartet. + +Wenn Ihre Anwendung für Benutzer zuvor versandt wird, mithilfe eines älteren (Pre-1.0) Version dieses Plugins und gespeicherte Dateien im permanenten Dateisystem hat, dann sollten Sie die Einstellung zur `Compatibility` einstellen. Wechseln die Location auf "Internal" würde bedeuten, dass Benutzer, die aktualisieren Sie ihre Anwendung, möglicherweise nicht auf ihre zuvor gespeicherte Dateien, abhängig von ihrem Gerät zugreifen. + +Wenn Ihre Anwendung neu ist, oder nie zuvor Dateien im Dateisystem persistent gespeichert hat, wird die `Internal` Einstellung in der Regel empfohlen. + +### Langsame rekursive Operationen für /android_asset + +Auflisten von Verzeichnissen Vermögenswert ist wirklich langsam auf Android. Sie können beschleunigen, es oben aber durch `src/android/build-extras.gradle` in das Stammverzeichnis von Ihrem android Projekt hinzufügen (erfordert auch cordova-android@4.0.0 oder größer). + +## iOS Macken + + * `cordova.file.applicationStorageDirectory`ist schreibgeschützt; zum Speichern von Dateien im Stammverzeichnis der Versuch schlägt fehl. Verwenden Sie eine der anderen `cordova.file.*` für iOS definierten Eigenschaften (nur `applicationDirectory` und `applicationStorageDirectory` sind schreibgeschützt). + * `FileReader.readAsText(blob, encoding)` + * Die `encoding` Parameter wird nicht unterstützt und UTF-8-Kodierung ist immer wirksam. + +### iOS permanenten Speicherort + +Es gibt zwei gültige Speicherorte persistente Dateien auf ein iOS-Gerät speichern: das Dokumenten-Verzeichnis und das Verzeichnis Library. Frühere Versionen des Plugins gespeichert immer nur persistente Dateien im Verzeichnis Dokumente. Dies hatte den Nebeneffekt einer Anwendung Dateien in iTunes, die oft unbeabsichtigte, speziell für Anwendungen, die viele kleine Dateien behandeln war, sichtbar zu machen, anstatt komplette Dokumente für den Export, die den beabsichtigten Zweck des Verzeichnisses ist zu produzieren. + +Es ist jetzt möglich, ob Sie Dateien in Dokumente oder Verzeichnis Library mit einer Präferenz in der Anwendung-`config.xml`-Datei speichern möchten. Hierzu fügen Sie eine dieser zwei Zeilen zu `"config.xml"`: + + + + + + +Ohne diese Zeile wird das Datei Plugin `Compatibility` als Standard verwenden. Wenn ein Präferenz-Tag vorhanden ist, und nicht einen der folgenden Werte, wird die Anwendung nicht gestartet. + +Wenn Ihre Anwendung für Benutzer zuvor versandt wird, mithilfe eines älteren (Pre-1.0) Version dieses Plugins und gespeicherte Dateien im permanenten Dateisystem hat, dann sollten Sie die Einstellung zur `Compatibility` einstellen. Standort zur `Library` wechseln würde bedeuten, dass Benutzer, die ihre Anwendung aktualisieren nicht in der Lage wäre, ihre zuvor gespeicherte Dateien zugreifen. + +Wenn die Anwendung neu, oder nie zuvor Dateien im Dateisystem persistent gespeichert hat, wird die Einstellung der `Library` allgemein empfohlen. + +## Firefox OS Macken + +Der Datei-System-API wird von Firefox-OS nicht nativ unterstützt und wird als ein Shim auf IndexedDB implementiert. + + * Schlägt nicht fehl, wenn Sie nicht leere Verzeichnisse entfernen + * Metadaten wird für Verzeichnisse nicht unterstützt. + * Methoden `copyTo` und `moveTo` unterstützen keine Verzeichnisse + +Die folgenden Datenpfade werden unterstützt: * `applicationDirectory` - `xhr` verwendet, um lokale Dateien erhalten, die mit der app verpackt sind. *`dataDirectory` - für persistente app-spezifische Daten-Dateien. *`cacheDirectory` - Cache-Dateien, die app startet überleben sollte (Apps sollten nicht vom Betriebssystem zum Löschen von Dateien hier verlassen). + +## Browser-Eigenheiten + +### Gemeinsamen Macken und Bemerkungen + + * Jeder Browser verwendet ein eigene Sandbox Dateisystem. IE und Firefox verwenden IndexedDB als Basis. Alle Browser verwenden Schrägstrich als Verzeichnistrennzeichen in einem Pfad. + * Directory-Einträge müssen nacheinander erstellt werden. Z. B. der Aufruf `fs.root.getDirectory ("dir1/Ordner2 ', {create:true}, SuccessCallback, ErrorCallback)` schlägt fehl, wenn dir1 nicht existierte. + * Das Plugin fordert Benutzer die Berechtigung zum permanenten Speicher beim ersten Start Anwendung verwenden. + * Plugin unterstützt `Cdvfile://localhost` (lokale Ressourcen) nur. D.h. externe Ressourcen nicht über `Cdvfile` unterstützt. + * Das Plugin folgt nicht ["File System API 8.3 Naming Einschränkungen"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). + * BLOB und Datei "`close`-Funktion wird nicht unterstützt. + * `FileSaver` und `BlobBuilder` werden von diesem Plugin nicht unterstützt und müssen nicht geboren. + * Das Plugin unterstützt keine `RequestAllFileSystems`. Diese Funktion fehlt auch in den Spezifikationen. + * Einträge im Verzeichnis werden nicht entfernt werden, wenn Sie verwenden `create: true` Flag für vorhandenes Verzeichnis. + * Über Konstruktor erstellte Dateien werden nicht unterstützt. Sie sollten stattdessen die entry.file-Methode verwenden. + * Jeder Browser verwendet eine eigene Form für Blob-URL-Verweise. + * `readAsDataURL`-Funktion wird unterstützt, aber die Mediatype in Chrom hängt von der Eintrag Namenerweiterung, Mediatype im IE immer leer ist (das ist dasselbe wie `Text-Plain` gemäß der Spezifikation), Mediatype in Firefox ist immer `Application/Octet-Stream`. Beispielsweise, wenn der Inhalt `Abcdefg` gibt dann Firefox `Daten: Anwendung / Octet-Stream; base64, YWJjZGVmZw ==`, IE gibt `Daten:; base64, YWJjZGVmZw ==`, Chrom gibt `Daten: < Mediatype je nach Erweiterung des Eintragsnamens >; base64, YWJjZGVmZw ==`. + * `ToInternalURL` gibt den Pfad zurück, in der Form `file:///persistent/path/to/entry` (Firefox, IE). Chrom gibt den Pfad zurück, in der Form `cdvfile://localhost/persistent/file`. + +### Chrom-Macken + + * Chrom-Dateisystem ist nicht sofort nach Gerät bereit. Als Workaround können Sie `FilePluginIsReady`-Ereignis abonnieren. Beispiel: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +`Window.isFilePluginReadyRaised`-Funktion können Sie überprüfen, ob Ereignis bereits ausgelöst wurde. -window.requestFileSystem temporär und PERSISTENTE Dateisystem-Quoten sind nicht begrenzt, in Chrom. -Um die dauerhafte Speicherung in Chrom zu erhöhen benötigen Sie `window.initPersistentFileSystem`-Methode aufrufen. Permanenter Speicherkontingent beträgt 5 MB standardmäßig. -Chrome erforderlich `--erlauben-Datei-Zugriff-aus-Files` Argument an den Support API via `file:///` Protokoll führen. -`Datei`-Objekt wird nicht geändert werden, wenn Sie Flag verwenden `{create:true}` als einen vorhandenen `Eintrag` zu erhalten. -Veranstaltungen `cancelable`-Eigenschaft festgelegt ist in Chrom wahr. Dies widerspricht der [Spezifikation](http://dev.w3.org/2009/dap/file-system/file-writer.html). -`toURL`-Funktion in Chrome zurück `Dateisystem:`-Pfad je nach Anwendungshost vorangestellt. Z. B. `filesystem:file:///persistent/somefile.txt`, `Filesystem:http://localhost:8080/persistent/somefile.txt`. -`toURL` Funktionsergebnis enthält keine nachgestellten Schrägstrich bei Verzeichniseintrag. Chrom löst Verzeichnisse mit Schrägstrich-gezogene Urls aber korrekt. -`ResolveLocalFileSystemURL`-Methode erfordert die eingehenden `Url` `Dateisystem` Präfix haben. Beispielsweise sollte die `Url`-Parameter für `ResolveLocalFileSystemURL` in der Form `filesystem:file:///persistent/somefile.txt` im Gegensatz zu der Form `file:///persistent/somefile.txt` in Android. -Veraltete `ToNativeURL`-Funktion wird nicht unterstützt und muss keinen Stub. -`SetMetadata`-Funktion ist nicht in den Spezifikationen angegeben und nicht unterstützt. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, statt SYNTAX_ERR(code: 8) auf anfordern des Dateisystems nicht existent. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, anstatt PATH_EXISTS_ERR(code: 12) zu versuchen, die ausschließlich eine Datei oder ein Verzeichnis zu erstellen, die bereits vorhanden ist. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, anstatt NO_MODIFICATION_ALLOWED_ERR(code: 6) zu versuchen, rufen Sie RemoveRecursively auf das Root-Dateisystem. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, anstatt NOT_FOUND_ERR(code: 1) zu versuchen, MoveTo-Verzeichnis, das nicht vorhanden ist. + +### Auf der Grundlage von IndexedDB Impl Macken (Firefox und IE) + + * `.` und `.` werden nicht unterstützt. + * IE unterstützt keine `file:///`-Modus; nur der Modus für gehostete ist unterstützten (Http://localhost:xxxx). + * Firefox Dateisystem Größe ist nicht begrenzt, aber jede 50MB-Erweiterung wird eine Benutzer die Berechtigung anzufordern. IE10 können bis zu 10mb kombinierte AppCache und IndexedDB in Implementierung des Dateisystems verwendet, ohne Aufforderung, sobald Sie dieses Niveau, werden, das Sie aufgefordert werden schlagen, wenn Sie es bis Max 250 mb pro Standort erhöht werden sollen. `Size`-Parameter für `RequestFileSystem` Funktion wirkt also nicht Dateisystem in Firefox und IE. + * `ReadAsBinaryString` Funktion heißt es nicht in die Angaben und in IE nicht unterstützt und muss keinen Stub. + * `file.Type` ist immer null. + * Sie sollten nicht erstellen Eintrag mit DirectoryEntry Instanz Rückrufergebnis, die gelöscht wurde. Andernfalls erhalten Sie einen "hängende Eintrag". + * Bevor Sie eine Datei lesen können, die gerade geschrieben wurde, müssen Sie eine neue Instanz dieser Datei erhalten. + * `SetMetadata`-Funktion, die nicht in den Specs genannt unterstützt Feldänderung nur `ModificationTime`. + * `CopyTo` und `MoveTo`-Funktionen unterstützen keine Verzeichnisse. + * Verzeichnisse-Metadaten werden nicht unterstützt. + * Beide Entry.remove und directoryEntry.removeRecursively nicht scheitern, wenn nicht leere Verzeichnisse entfernen - Verzeichnisse entfernt werden stattdessen zusammen mit Inhalt gereinigt. + * `abort` und `truncate`-Funktionen werden nicht unterstützt. + * Progress-Ereignisse werden nicht ausgelöst. Beispielsweise wird dieser Handler nicht ausgeführt werden: + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## Upgrade Notes + +In v1.0.0 dieses Plugins haben die `FileEntry` und `DirectoryEntry` Strukturen geändert, um mehr im Einklang mit der veröffentlichten Spezifikation sein. + +Vorgängerversionen (Pre-1.0.0) des Plugins den Gerät-Absolute-Dateispeicherort in der Eigenschaft `fullPath` `Entry` Objekte gespeichert. Diese Pfade würde in der Regel aussehen + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Diese Pfade wurden auch von der `toURL()`-Methode der `Entry` Objekte zurückgegeben. + +Mit v1.0.0 ist das `fullPath`-Attribut den Pfad zu der Datei, *relativ zum Stammverzeichnis des Dateisystems HTML*. Also, würde die oben genannten Wege jetzt beide durch ein `FileEntry`-Objekt mit einem `fullPath` von dargestellt werden + + /path/to/file + + +Wenn Ihre Anwendung mit absoluter Gerätepfade arbeitet und Sie zuvor diese Pfade durch die Eigenschaft `fullPath` `Entry` Objekte abgerufen, sollten dann Sie den Code, um stattdessen `entry.toURL()` verwenden aktualisieren. + +Für rückwärts Kompatibilität, die `resolveLocalFileSystemURL()`-Methode wird einen Absolute-Gerätepfad zu akzeptieren und kehrt ein `Entry`-Objekt entspricht, solange diese Datei in den `TEMPORARY` oder `PERSISTENT` Dateisysteme existiert. + +Dies wurde vor allem ein Problem mit dem File-Transfer-Plugin, die zuvor-Absolute-Gerätepfade verwendet (und kann damit noch einverstanden). Es wurde überarbeitet, um mit Dateisystem-URLs korrekt zu arbeiten, damit ersetzen `entry.fullPath` mit `entry.toURL()` immer das Plugin zum Arbeiten mit Dateien auf dem Gerät Probleme lösen sollte. + +In v1.1.0 wurde der Rückgabewert von `toURL()` (siehe \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) geändert, um eine absolute "file://" URL zurückgeben. wo immer möglich. Sicherstellung einer ' Cdvfile:'-URL können Sie an `toInternalURL()`. Diese Methode gibt jetzt Dateisystem URLs der Form zurück. + + cdvfile://localhost/persistent/path/to/file + + +die benutzt werden können, um die Datei eindeutig zu identifizieren. + +## Liste der Fehlercodes und Bedeutungen + +Wenn ein Fehler ausgelöst wird, wird eines der folgenden Codes verwendet werden. + +| Code | Konstante | +| ----:|:----------------------------- | +| 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` | + +## Konfigurieren das Plugin (Optional) + +Die Menge der verfügbaren Dateisysteme kann pro Plattform konfiguriert sein. Erkennen von iOS und Android ein Tag in `"config.xml"` die Namen der Dateisysteme installiert werden. Standardmäßig sind alle Datei-System-Roots aktiviert. + + + + + +### Android + + * `files`: interne Datei-Speicher-Verzeichnis der Anwendung + * `files-external`: Verzeichnis der Anwendung externe Datei Speicher + * `sdcard`: das externe Globaldatei-Speicherverzeichnis (Dies ist die Wurzel der SD-Karte, sofern installiert). Sie benötigen die Berechtigung zur Verwendung dieses `android.permission.WRITE_EXTERNAL_STORAGE`. + * `cache`: internen Cache-Verzeichnis der Anwendung + * `cache-external`: externer Cache-Verzeichnis der Anwendung + * `root`: das gesamte Gerät-Dateisystem + +Android unterstützt auch eine spezielle Dateisystem mit dem Namen "documents", die ein Unterverzeichnis "/Documents/" die "files" Dateisystem darstellt. + +### iOS + + * `library`: Bibliothek-Verzeichnis der Anwendung + * `documents`: Dokumente-Verzeichnis der Anwendung + * `cache`: Cache-Verzeichnis der Anwendung + * `bundle`: die Anwendung Bündel; den Speicherort der die app selbst auf dem Datenträger (schreibgeschützt) + * `root`: das gesamte Gerät-Dateisystem + +Standardmäßig können die Bibliothek und Dokumenten-Verzeichnisse mit iCloud synchronisiert werden. Sie können auch verlangen, zwei zusätzliche Dateisysteme, `library-nosync` und `documents-nosync`, die einem speziellen nicht synchronisierten Verzeichnis innerhalb darstellen der `/Library` oder `/Documents`-Dateisystem. \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/de/index.md b/plugins/cordova-plugin-file/doc/de/index.md new file mode 100644 index 0000000..2a51695 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/de/index.md @@ -0,0 +1,338 @@ + + +# cordova-plugin-file + +Dieses Plugin implementiert eine File-API, die Lese-/Schreibzugriff Zugriff auf Dateien, die auf dem Gerät befinden. + +Dieses Plugin basiert auf mehrere Angaben, einschließlich: die HTML5-File-API + +Die (heute nicht mehr existierenden) Verzeichnisse und System neuesten Erweiterungen: , obwohl die meisten von den Plugin-Code wurde geschrieben, als eine frühere Spec aktuell waren: + +Es implementiert auch die FileWriter Spec: + +Verwendung finden Sie in HTML5 Rocks ausgezeichnete [Dateisystem Artikel.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +Finden Sie einen Überblick über andere Speicheroptionen Cordovas [Speicher-Führer][2]. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +Dieses Plugin wird global `cordova.file`-Objekt definiert. + +Obwohl im globalen Gültigkeitsbereich, steht es nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Installation + + cordova plugin add cordova-plugin-file + + +## Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 und 8 * +* Windows 8 * +* Browser + +* *Diese Plattformen unterstützen nicht `FileReader.readAsArrayBuffer` noch `FileWriter.write(blob)`.* + +## Wo Dateien gespeichert + +Stand: V1 werden URLs auf wichtige Datei-System-Verzeichnisse zur Verfügung gestellt. Jede URL in der Form *file:///path/to/spot/* ist, und ein `DirectoryEntry` mit `window.resolveLocalFileSystemURL()` konvertiert werden können. + +* `cordova.file.applicationDirectory`-Die schreibgeschützten Verzeichnis, in dem die Anwendung installiert ist. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.applicationStorageDirectory`-Root-Verzeichnis der Anwendungs-Sandbox; auf iOS ist schreibgeschützt (aber bestimmte Unterverzeichnisse [wie `/Documents` ] sind Lese-und Schreibzugriff). Alle enthaltene Daten ist für die app privat. ( *iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.dataDirectory`-Beständige und private Datenspeicherung innerhalb der Anwendungs-Sandbox, die mit internen Speicher (auf Android, externen Speicher verwenden, verwenden Sie `.externalDataDirectory` ). Auf iOS, ist dieses Verzeichnis nicht mit iCloud synchronisiert (verwenden Sie `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.cacheDirectory`-Verzeichnis der zwischengespeicherten Daten-Dateien oder Dateien, die Ihre app einfach neu erstellen können. Das Betriebssystem kann diese Dateien löschen, wenn das Gerät auf Speicher knapp wird, dennoch sollten die apps vom Betriebssystem zum Löschen von Dateien hier nicht verlassen. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.externalApplicationStorageDirectory`-Anwendungsraum auf externen Speicher. (*Android*) + +* `cordova.file.externalDataDirectory`-Wo, app-spezifische Datendateien auf externen Speicher setzen. (*Android*) + +* `cordova.file.externalCacheDirectory`-Anwendungscache auf externen Speicher. (*Android*) + +* `cordova.file.externalRootDirectory`-Externer Speicher (SD-Karte) Stamm. (*Android*, *BlackBerry 10*) + +* `cordova.file.tempDirectory`-Temp-Verzeichnis, dem das OS auf deaktivieren können wird. Verlassen Sie sich nicht auf das Betriebssystem, um dieses Verzeichnis zu löschen; Ihre Anwendung sollte immer Dateien gegebenenfalls entfernen. (*iOS*) + +* `cordova.file.syncedDataDirectory`-Hält app-spezifische Dateien, die (z. B. auf iCloud) synchronisiert werden sollten. (*iOS*) + +* `cordova.file.documentsDirectory`-Dateien für die app, aber privat sind sinnvoll, andere Anwendungen (z.B. Office-Dateien). (*iOS*) + +* `cordova.file.sharedDirectory`-Dateien für alle Anwendungen (*BlackBerry 10* weltweit verfügbar) + +## Dateisystemlayouts + +Obwohl technisch ein Implementierungsdetail, kann es sehr hilfreich zu wissen, wie die `cordova.file.*`-Eigenschaften physikalische Pfade auf einem echten Gerät zugeordnet sein. + +### iOS-Datei-System-Layout + +| Gerätepfad | `Cordova.file.*` | `iosExtraFileSystems` | R/w? | persistent? | OS löscht | Sync | Private | +|:-------------------------------------------- |:--------------------------- |:--------------------- |:----:|:-----------:|:----------:|:----:|:-------:| +| `/ Var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Ja | +|    `appname.app/` | applicationDirectory | Bundle | r | N/A | N/A | N/A | Ja | +|       `www/` | - | - | r | N/A | N/A | N/A | Ja | +|    `Documents/` | documentsDirectory | Dokumente | R/w | Ja | Nein | Ja | Ja | +|       `NoCloud/` | - | Dokumente-nosync | R/w | Ja | Nein | Nein | Ja | +|    `Library` | - | Bibliothek | R/w | Ja | Nein | Ja? | Ja | +|       `NoCloud/` | dataDirectory | Bibliothek-nosync | R/w | Ja | Nein | Nein | Ja | +|       `Cloud/` | syncedDataDirectory | - | R/w | Ja | Nein | Ja | Ja | +|       `Caches/` | cacheDirectory | Cache | R/w | Ja * | Ja * * *| | Nein | Ja | +|    `tmp/` | tempDirectory | - | R/w | Nicht * * | Ja * * *| | Nein | Ja | + +* Dateien werden hinweg app Neustarts und Upgrades beibehalten, aber dieses Verzeichnis kann gelöscht werden, wenn das OS begehrt. Ihre Anwendung sollte in der Lage, Inhalte zu erschaffen, die möglicherweise gelöscht werden. + +* *-Dateien kann über app-Neustarts beizubehalten, aber verlasse dich nicht auf dieses Verhalten. Dateien sind nicht unbedingt Aktuelles beibehalten. Ihre Anwendung sollte Dateien aus diesem Verzeichnis entfernen, wenn es gilt, diese Dateien werden entfernt, da das OS nicht wann (oder auch wenn) garantiert. + +* * *| The OS kann den Inhalt dieses Verzeichnisses löschen, wenn es sich anfühlt, ist es erforderlich, aber verlassen Sie sich nicht dazu. Sie sollten dieses Verzeichnis entsprechend Ihrer Anwendung deaktivieren. + +### Android File System-Layout + +| Gerätepfad | `Cordova.file.*` | `AndroidExtraFileSystems` | R/w? | persistent? | OS löscht | Private | +|:--------------------------------- |:----------------------------------- |:------------------------- |:----:|:-----------:|:---------:|:-------:| +| `file:///android_asset/` | applicationDirectory | | r | N/A | N/A | Ja | +| `/ Data/Data/< app-Id > /` | applicationStorageDirectory | - | R/w | N/A | N/A | Ja | +|    `cache` | cacheDirectory | Cache | R/w | Ja | Ja * | Ja | +|    `files` | dataDirectory | Dateien | R/w | Ja | Nein | Ja | +|       `Documents` | | Dokumente | R/w | Ja | Nein | Ja | +| `< Sdcard > /` | externalRootDirectory | sdcard | R/w | Ja | Nein | Nein | +|    `Android/data//` | externalApplicationStorageDirectory | - | R/w | Ja | Nein | Nein | +|       `cache` | externalCacheDirectry | Cache-extern | R/w | Ja | Nicht * * | Nein | +|       `files` | externalDataDirectory | Dateien-extern | R/w | Ja | Nein | Nein | + +* Das Betriebssystem kann regelmäßig dieses Verzeichnis zu löschen, aber verlasse dich nicht auf dieses Verhalten. Deaktivieren Sie den Inhalt dieses Verzeichnisses für Ihre Anwendung geeigneten. Ein Benutzer den Cache manuell löschen sollte, werden die Inhalte dieses Verzeichnisses entfernt. + +* * The OS nicht klar dieses Verzeichnis automatisch; Sie sind verantwortlich für die Inhalte selbst verwalten. Der Benutzer den Cache manuell löschen sollte, werden der Inhalt des Verzeichnisses entfernt. + +**Hinweis**: Wenn externe Speichergeräte nicht bereitgestellt werden kann, sind die `cordova.file.external*` Eigenschaften `null`. + +### BlackBerry 10-File-System-Layout + +| Gerätepfad | `Cordova.file.*` | R/w? | persistent? | OS löscht | Private | +|:--------------------------------------------------- |:--------------------------- |:----:|:-----------:|:---------:|:-------:| +| `file:///Accounts/1000/APPDATA/ < app Id > /` | applicationStorageDirectory | r | N/A | N/A | Ja | +|    `app/native` | applicationDirectory | r | N/A | N/A | Ja | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | R/w | Nein | Ja | Ja | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | R/w | Ja | Nein | Ja | +| `file:///Accounts/1000/Removable/sdcard` | externalRemovableDirectory | R/w | Ja | Nein | Nein | +| `file:///Accounts/1000/Shared` | sharedDirectory | R/w | Ja | Nein | Nein | + +*Hinweis*: Wenn die Anwendung bereitgestellt wird, um Perimeter zu arbeiten, alle Pfade sind relativ /accounts/1000-enterprise. + +## Android Eigenarten + +### Android permanenten Speicherort + +Es gibt mehrere gültige Speicherorte, persistente Dateien auf einem Android-Gerät zu speichern. Finden Sie auf [dieser Seite][3] eine ausführliche Diskussion über die verschiedenen Möglichkeiten. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +Frühere Versionen des Plugins wählen würde, den Speicherort der temporären und permanenten Dateien beim Start, basierend auf, ob das Gerät behauptete, dass die SD-Karte (oder gleichwertige Speicherpartition) bereitgestellt wurde. Wenn die SD-Karte eingelegt wurde, oder wenn eine große interne Speicherpartition verfügbar war (wie auf Nexus-Geräten) und dann in die Wurzel dieses Raumes, die persistenten Dateien gespeichert werden. Dies bedeutete, dass alle Cordova apps aller verfügbaren Dateien auf der Karte sehen konnte. + +Wenn die SD-Karte nicht verfügbar war, dann Vorgängerversionen Daten unter speichern würde `/data/data/`, die isoliert Anwendungen voneinander, aber möglicherweise noch Ursache Daten zwischen Benutzern freigegeben werden. + +Es ist jetzt möglich, ob Sie Dateien der internen Datei-Speicherort oder unter Verwendung der bisherigen Logik, mit einer Präferenz in der Anwendung-`config.xml`-Datei speichern möchten. Hierzu fügen Sie eine dieser zwei Zeilen zu `"config.xml"`: + + + + + + +Ohne diese Zeile wird das Datei Plugin `Compatibility` als Standard verwenden. Wenn ein Präferenz-Tag vorhanden ist, und nicht einen der folgenden Werte, wird die Anwendung nicht gestartet. + +Wenn Ihre Anwendung für Benutzer zuvor versandt wird, mithilfe eines älteren (Pre-1.0) Version dieses Plugins und gespeicherte Dateien im permanenten Dateisystem hat, dann sollten Sie die Einstellung zur `Compatibility` einstellen. Wechseln die Location auf "Internal" würde bedeuten, dass Benutzer, die aktualisieren Sie ihre Anwendung, möglicherweise nicht auf ihre zuvor gespeicherte Dateien, abhängig von ihrem Gerät zugreifen. + +Wenn Ihre Anwendung neu ist, oder nie zuvor Dateien im Dateisystem persistent gespeichert hat, wird die `Internal` Einstellung in der Regel empfohlen. + +## iOS Macken + +* `cordova.file.applicationStorageDirectory`ist schreibgeschützt; zum Speichern von Dateien im Stammverzeichnis der Versuch schlägt fehl. Verwenden Sie eine der anderen `cordova.file.*` für iOS definierten Eigenschaften (nur `applicationDirectory` und `applicationStorageDirectory` sind schreibgeschützt). +* `FileReader.readAsText(blob, encoding)` + * Die `encoding` Parameter wird nicht unterstützt und UTF-8-Kodierung ist immer wirksam. + +### iOS permanenten Speicherort + +Es gibt zwei gültige Speicherorte persistente Dateien auf ein iOS-Gerät speichern: das Dokumenten-Verzeichnis und das Verzeichnis Library. Frühere Versionen des Plugins gespeichert immer nur persistente Dateien im Verzeichnis Dokumente. Dies hatte den Nebeneffekt einer Anwendung Dateien in iTunes, die oft unbeabsichtigte, speziell für Anwendungen, die viele kleine Dateien behandeln war, sichtbar zu machen, anstatt komplette Dokumente für den Export, die den beabsichtigten Zweck des Verzeichnisses ist zu produzieren. + +Es ist jetzt möglich, ob Sie Dateien in Dokumente oder Verzeichnis Library mit einer Präferenz in der Anwendung-`config.xml`-Datei speichern möchten. Hierzu fügen Sie eine dieser zwei Zeilen zu `"config.xml"`: + + + + + + +Ohne diese Zeile wird das Datei Plugin `Compatibility` als Standard verwenden. Wenn ein Präferenz-Tag vorhanden ist, und nicht einen der folgenden Werte, wird die Anwendung nicht gestartet. + +Wenn Ihre Anwendung für Benutzer zuvor versandt wird, mithilfe eines älteren (Pre-1.0) Version dieses Plugins und gespeicherte Dateien im permanenten Dateisystem hat, dann sollten Sie die Einstellung zur `Compatibility` einstellen. Standort zur `Library` wechseln würde bedeuten, dass Benutzer, die ihre Anwendung aktualisieren nicht in der Lage wäre, ihre zuvor gespeicherte Dateien zugreifen. + +Wenn die Anwendung neu, oder nie zuvor Dateien im Dateisystem persistent gespeichert hat, wird die Einstellung der `Library` allgemein empfohlen. + +## Firefox OS Macken + +Der Datei-System-API wird von Firefox-OS nicht nativ unterstützt und wird als ein Shim auf IndexedDB implementiert. + +* Schlägt nicht fehl, wenn Sie nicht leere Verzeichnisse entfernen +* Metadaten wird für Verzeichnisse nicht unterstützt. +* Methoden `copyTo` und `moveTo` unterstützen keine Verzeichnisse + +Die folgenden Datenpfade werden unterstützt: * `applicationDirectory` - `xhr` verwendet, um lokale Dateien erhalten, die mit der app verpackt sind. *`dataDirectory` - für persistente app-spezifische Daten-Dateien. *`cacheDirectory` - Cache-Dateien, die app startet überleben sollte (Apps sollten nicht vom Betriebssystem zum Löschen von Dateien hier verlassen). + +## Browser-Eigenheiten + +### Gemeinsamen Macken und Bemerkungen + +* Jeder Browser verwendet ein eigene Sandbox Dateisystem. IE und Firefox verwenden IndexedDB als Basis. Alle Browser verwenden Schrägstrich als Verzeichnistrennzeichen in einem Pfad. +* Directory-Einträge müssen nacheinander erstellt werden. Z. B. der Aufruf `fs.root.getDirectory ("dir1/Ordner2 ', {create:true}, SuccessCallback, ErrorCallback)` schlägt fehl, wenn dir1 nicht existierte. +* Das Plugin fordert Benutzer die Berechtigung zum permanenten Speicher beim ersten Start Anwendung verwenden. +* Plugin unterstützt `Cdvfile://localhost` (lokale Ressourcen) nur. D.h. externe Ressourcen nicht über `Cdvfile` unterstützt. +* Das Plugin folgt nicht ["File System API 8.3 Naming Einschränkungen"][4]. +* BLOB und Datei "`close`-Funktion wird nicht unterstützt. +* `FileSaver` und `BlobBuilder` werden von diesem Plugin nicht unterstützt und müssen nicht geboren. +* Das Plugin unterstützt keine `RequestAllFileSystems`. Diese Funktion fehlt auch in den Spezifikationen. +* Einträge im Verzeichnis werden nicht entfernt werden, wenn Sie verwenden `create: true` Flag für vorhandenes Verzeichnis. +* Über Konstruktor erstellte Dateien werden nicht unterstützt. Sie sollten stattdessen die entry.file-Methode verwenden. +* Jeder Browser verwendet eine eigene Form für Blob-URL-Verweise. +* `readAsDataURL`-Funktion wird unterstützt, aber die Mediatype in Chrom hängt von der Eintrag Namenerweiterung, Mediatype im IE immer leer ist (das ist dasselbe wie `Text-Plain` gemäß der Spezifikation), Mediatype in Firefox ist immer `Application/Octet-Stream`. Beispielsweise, wenn der Inhalt `Abcdefg` gibt dann Firefox `Daten: Anwendung / Octet-Stream; base64, YWJjZGVmZw ==`, IE gibt `Daten:; base64, YWJjZGVmZw ==`, Chrom gibt `Daten: < Mediatype je nach Erweiterung des Eintragsnamens >; base64, YWJjZGVmZw ==`. +* `ToInternalURL` gibt den Pfad zurück, in der Form `file:///persistent/path/to/entry` (Firefox, IE). Chrom gibt den Pfad zurück, in der Form `cdvfile://localhost/persistent/file`. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### Chrom-Macken + +* Chrom-Dateisystem ist nicht sofort nach Gerät bereit. Als Workaround können Sie `FilePluginIsReady`-Ereignis abonnieren. Beispiel: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +`Window.isFilePluginReadyRaised`-Funktion können Sie überprüfen, ob Ereignis bereits ausgelöst wurde. -window.requestFileSystem temporär und PERSISTENTE Dateisystem-Quoten sind nicht begrenzt, in Chrom. -Um die dauerhafte Speicherung in Chrom zu erhöhen benötigen Sie `window.initPersistentFileSystem`-Methode aufrufen. Permanenter Speicherkontingent beträgt 5 MB standardmäßig. -Chrome erforderlich `--erlauben-Datei-Zugriff-aus-Files` Argument an den Support API via `file:///` Protokoll führen. -`Datei`-Objekt wird nicht geändert werden, wenn Sie Flag verwenden `{create:true}` als einen vorhandenen `Eintrag` zu erhalten. -Veranstaltungen `cancelable`-Eigenschaft festgelegt ist in Chrom wahr. Dies widerspricht der [Spezifikation][5]. -`toURL`-Funktion in Chrome zurück `Dateisystem:`-Pfad je nach Anwendungshost vorangestellt. Z. B. `filesystem:file:///persistent/somefile.txt`, `Filesystem:http://localhost:8080/persistent/somefile.txt`. -`toURL` Funktionsergebnis enthält keine nachgestellten Schrägstrich bei Verzeichniseintrag. Chrom löst Verzeichnisse mit Schrägstrich-gezogene Urls aber korrekt. -`ResolveLocalFileSystemURL`-Methode erfordert die eingehenden `Url` `Dateisystem` Präfix haben. Beispielsweise sollte die `Url`-Parameter für `ResolveLocalFileSystemURL` in der Form `filesystem:file:///persistent/somefile.txt` im Gegensatz zu der Form `file:///persistent/somefile.txt` in Android. -Veraltete `ToNativeURL`-Funktion wird nicht unterstützt und muss keinen Stub. -`SetMetadata`-Funktion ist nicht in den Spezifikationen angegeben und nicht unterstützt. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, statt SYNTAX_ERR(code: 8) auf anfordern des Dateisystems nicht existent. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, anstatt PATH_EXISTS_ERR(code: 12) zu versuchen, die ausschließlich eine Datei oder ein Verzeichnis zu erstellen, die bereits vorhanden ist. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, anstatt NO_MODIFICATION_ALLOWED_ERR(code: 6) zu versuchen, rufen Sie RemoveRecursively auf das Root-Dateisystem. -INVALID_MODIFICATION_ERR (Code: 9) wird ausgelöst, anstatt NOT_FOUND_ERR(code: 1) zu versuchen, MoveTo-Verzeichnis, das nicht vorhanden ist. + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### Auf der Grundlage von IndexedDB Impl Macken (Firefox und IE) + +* `.` und `.` werden nicht unterstützt. +* IE unterstützt keine `file:///`-Modus; nur der Modus für gehostete ist unterstützten (Http://localhost:xxxx). +* Firefox Dateisystem Größe ist nicht begrenzt, aber jede 50MB-Erweiterung wird eine Benutzer die Berechtigung anzufordern. IE10 können bis zu 10mb kombinierte AppCache und IndexedDB in Implementierung des Dateisystems verwendet, ohne Aufforderung, sobald Sie dieses Niveau, werden, das Sie aufgefordert werden schlagen, wenn Sie es bis Max 250 mb pro Standort erhöht werden sollen. `Size`-Parameter für `RequestFileSystem` Funktion wirkt also nicht Dateisystem in Firefox und IE. +* `ReadAsBinaryString` Funktion heißt es nicht in die Angaben und in IE nicht unterstützt und muss keinen Stub. +* `file.Type` ist immer null. +* Sie sollten nicht erstellen Eintrag mit DirectoryEntry Instanz Rückrufergebnis, die gelöscht wurde. Andernfalls erhalten Sie einen "hängende Eintrag". +* Bevor Sie eine Datei lesen können, die gerade geschrieben wurde, müssen Sie eine neue Instanz dieser Datei erhalten. +* `SetMetadata`-Funktion, die nicht in den Specs genannt unterstützt Feldänderung nur `ModificationTime`. +* `CopyTo` und `MoveTo`-Funktionen unterstützen keine Verzeichnisse. +* Verzeichnisse-Metadaten werden nicht unterstützt. +* Beide Entry.remove und directoryEntry.removeRecursively nicht scheitern, wenn nicht leere Verzeichnisse entfernen - Verzeichnisse entfernt werden stattdessen zusammen mit Inhalt gereinigt. +* `abort` und `truncate`-Funktionen werden nicht unterstützt. +* Progress-Ereignisse werden nicht ausgelöst. Beispielsweise wird dieser Handler nicht ausgeführt werden: + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## Upgrade Notes + +In v1.0.0 dieses Plugins haben die `FileEntry` und `DirectoryEntry` Strukturen geändert, um mehr im Einklang mit der veröffentlichten Spezifikation sein. + +Vorgängerversionen (Pre-1.0.0) des Plugins den Gerät-Absolute-Dateispeicherort in der Eigenschaft `fullPath` `Entry` Objekte gespeichert. Diese Pfade würde in der Regel aussehen + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Diese Pfade wurden auch von der `toURL()`-Methode der `Entry` Objekte zurückgegeben. + +Mit v1.0.0 ist das `fullPath`-Attribut den Pfad zu der Datei, *relativ zum Stammverzeichnis des Dateisystems HTML*. Also, würde die oben genannten Wege jetzt beide durch ein `FileEntry`-Objekt mit einem `fullPath` von dargestellt werden + + /path/to/file + + +Wenn Ihre Anwendung mit absoluter Gerätepfade arbeitet und Sie zuvor diese Pfade durch die Eigenschaft `fullPath` `Entry` Objekte abgerufen, sollten dann Sie den Code, um stattdessen `entry.toURL()` verwenden aktualisieren. + +Für rückwärts Kompatibilität, die `resolveLocalFileSystemURL()`-Methode wird einen Absolute-Gerätepfad zu akzeptieren und kehrt ein `Entry`-Objekt entspricht, solange diese Datei in den `TEMPORARY` oder `PERSISTENT` Dateisysteme existiert. + +Dies wurde vor allem ein Problem mit dem File-Transfer-Plugin, die zuvor-Absolute-Gerätepfade verwendet (und kann damit noch einverstanden). Es wurde überarbeitet, um mit Dateisystem-URLs korrekt zu arbeiten, damit ersetzen `entry.fullPath` mit `entry.toURL()` immer das Plugin zum Arbeiten mit Dateien auf dem Gerät Probleme lösen sollte. + +In v1.1.0 wurde der Rückgabewert von `toURL()` (siehe \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) geändert, um eine absolute "file://" URL zurückgeben. wo immer möglich. Sicherstellung einer ' Cdvfile:'-URL können Sie an `toInternalURL()`. Diese Methode gibt jetzt Dateisystem URLs der Form zurück. + + cdvfile://localhost/persistent/path/to/file + + +die benutzt werden können, um die Datei eindeutig zu identifizieren. + +## Liste der Fehlercodes und Bedeutungen + +Wenn ein Fehler ausgelöst wird, wird eines der folgenden Codes verwendet werden. + +| Code | Konstante | +| ----:|:----------------------------- | +| 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` | + +## Konfigurieren das Plugin (Optional) + +Die Menge der verfügbaren Dateisysteme kann pro Plattform konfiguriert sein. Erkennen von iOS und Android ein Tag in `"config.xml"` die Namen der Dateisysteme installiert werden. Standardmäßig sind alle Datei-System-Roots aktiviert. + + + + + +### Android + +* `files`: interne Datei-Speicher-Verzeichnis der Anwendung +* `files-external`: Verzeichnis der Anwendung externe Datei Speicher +* `sdcard`: das externe Globaldatei-Speicherverzeichnis (Dies ist die Wurzel der SD-Karte, sofern installiert). Sie benötigen die Berechtigung zur Verwendung dieses `android.permission.WRITE_EXTERNAL_STORAGE`. +* `cache`: internen Cache-Verzeichnis der Anwendung +* `cache-external`: externer Cache-Verzeichnis der Anwendung +* `root`: das gesamte Gerät-Dateisystem + +Android unterstützt auch eine spezielle Dateisystem mit dem Namen "documents", die ein Unterverzeichnis "/Documents/" die "files" Dateisystem darstellt. + +### iOS + +* `library`: Bibliothek-Verzeichnis der Anwendung +* `documents`: Dokumente-Verzeichnis der Anwendung +* `cache`: Cache-Verzeichnis der Anwendung +* `bundle`: die Anwendung Bündel; den Speicherort der die app selbst auf dem Datenträger (schreibgeschützt) +* `root`: das gesamte Gerät-Dateisystem + +Standardmäßig können die Bibliothek und Dokumenten-Verzeichnisse mit iCloud synchronisiert werden. Sie können auch verlangen, zwei zusätzliche Dateisysteme, `library-nosync` und `documents-nosync`, die einem speziellen nicht synchronisierten Verzeichnis innerhalb darstellen der `/Library` oder `/Documents`-Dateisystem. diff --git a/plugins/cordova-plugin-file/doc/de/plugins.md b/plugins/cordova-plugin-file/doc/de/plugins.md new file mode 100644 index 0000000..8887d7a --- /dev/null +++ b/plugins/cordova-plugin-file/doc/de/plugins.md @@ -0,0 +1,101 @@ + + +# Hinweise für Plugin-Entwickler + +Diese Notizen sind hauptsächlich für Android und iOS-Entwickler, die Plugins welche Schnittstelle mit dem Dateisystem, mit dem Plugin Datei schreiben möchten. + +## Arbeiten mit Cordova-Datei-System-URLs + +Seit der Version 1.0.0, wurde dieses Plugin verwendet URLs mit einer `cdvfile` Regelung für die gesamte Kommunikation über die Brücke, sondern als raw-Device Dateisystempfade zu JavaScript auszusetzen. + +Auf der Seite JavaScript bedeutet dies, dass FileEntries und DirectoryEntry-Objekt ein FullPath-Attribut haben, die relativ zum Stammverzeichnis des Dateisystems HTML ist. Wenn Ihr Plugins-JavaScript-API ein FileEntries oder DirectoryEntry-Objekt akzeptiert, rufen Sie `.toURL()` auf das Objekt vor der Übergabe an systemeigenen Code über die Brücke. + +### Konvertieren von Cdvfile: / / URLs auf Fileystem Pfade + +Plugins, die auf das Dateisystem schreiben müssen, sollten eine empfangene Datei-System-URL auf eine tatsächliche Stelle des Dateisystems zu konvertieren. Es gibt mehrere Wege, dies zu tun, je nach einheitlichen Plattform. + +Es ist wichtig, daran erinnern, dass nicht alle `cdvfile://` URLs sind zuweisbaren real Dateien auf das Gerät. Einige URLs verweisen auf Vermögenswerte auf Gerät nicht durch Dateien dargestellt werden, oder sogar auf Remoteressourcen verweisen können. Aufgrund dieser Möglichkeiten sollten Plugins immer testen, ob sie ein sinnvolles Ergebnis zu erhalten, wieder bei dem Versuch, die URLs in Pfade umwandeln. + +#### Android + +Auf Android, konvertiert die einfachste Methode eine `cdvfile://` URL zu einem Dateisystempfad zu verwenden ist `org.apache.cordova.CordovaResourceApi` . `CordovaResourceApi`verfügt über mehrere Methoden der verarbeiten kann `cdvfile://` URLs: + + WebView ist Mitglied der Plugin-Klasse CordovaResourceApi ResourceApi = webView.getResourceApi(); + + Erhalten eine file:/// URL, diese Datei auf dem Gerät / / oder die gleiche URL unverändert, wenn es eine Datei-Uri FileURL zugeordnet werden kann nicht = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +Es ist auch möglich, das Plugin Datei direkt zu verwenden: + + Import org.apache.cordova.file.FileUtils; + Import org.apache.cordova.file.FileSystem; + Import Java.net.MalformedURLException:; + + Erhalten Sie das Datei-Plugin aus dem Plugin-Manager FileUtils FilePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + Angesichts eine URL, einen Pfad zu erhalten, denn es versuchen {String Pfad = filePlugin.filesystemPathForURL(cdvfileURL);} catch (MalformedURLException e) {/ / die Dateisystem-Url war nicht erkannt} + + +Aus einem Pfad zu konvertieren eine `cdvfile://` URL: + + Import org.apache.cordova.file.LocalFilesystemURL; + + Rufen Sie ein LocalFilesystemURL-Objekt für einen Gerätepfad / / oder null, wenn sie nicht als URL Cdvfile dargestellt wird. + LocalFilesystemURL Url = filePlugin.filesystemURLforLocalPath(path); + Erhalten Sie die Zeichenfolgendarstellung der URL Objekt String CdvfileURL = url.toString(); + + +Wenn Ihr Plugin eine Datei erstellt, und Sie dafür ein FileEntries-Objekt zurückgeben möchten, verwenden Sie das Datei-Plugin: + + Zurückgeben eine JSON-Struktur geeignet für die Rückgabe an JavaScript, / / oder null, wenn diese Datei nicht als URL Cdvfile darstellbar ist. + JSONObject Eintrag = filePlugin.getEntryForFile(file); + + +#### iOS + +Cordova auf iOS verwendet nicht das gleiche `CordovaResourceApi` Konzept als Android. Auf iOS sollten Sie das Datei-Plugin verwenden, zum Konvertieren von URLs und Dateisystem-Pfaden. + + Rufen Sie ein CDVFilesystem URL-Objekt von einer URL-Zeichenfolge CDVFilesystemURL * Url = [CDVFilesystemURL FileSystemURLWithString:cdvfileURL]; + Erhalten Sie einen Pfad für die URL-Objekt oder NULL, wenn es einen Dateipfad NSString * zugeordnet werden kann nicht = [FilePlugin FilesystemPathForURL:url]; + + + Eine CDVFilesystem URL-Objekt für einen Gerätepfad abrufen oder / / gleich NULL, wenn sie nicht als URL Cdvfile dargestellt wird. + CDVFilesystemURL-Url = [FilePlugin FileSystemURLforLocalPath:path]; + Erhalten Sie die Zeichenfolgendarstellung der URL Objekt NSString * CdvfileURL = [Url AbsoluteString]; + + +Wenn Ihr Plugin eine Datei erstellt, und Sie dafür ein FileEntries-Objekt zurückgeben möchten, verwenden Sie das Datei-Plugin: + + Eine CDVFilesystem URL-Objekt für einen Gerätepfad abrufen oder / / gleich NULL, wenn sie nicht als URL Cdvfile dargestellt wird. + CDVFilesystemURL-Url = [FilePlugin FileSystemURLforLocalPath:path]; + Erhalten eine Struktur zurück nach JavaScript NSDictionary * Eintrag = [FilePlugin MakeEntryForLocalURL:url] + + +#### JavaScript + +In JavaScript, bekommen eine `cdvfile://` URL aus einem FileEntries oder DirectoryEntry-Objekt, rufen Sie einfach `.toURL()` drauf: + + Var CdvfileURL = entry.toURL(); + + +Im Plugin Antwort Handler zur Konvertierung von einer zurückgegebenen FileEntries-Struktur in einem tatsächlichen Eintrag-Objekt sollte Handlercode importieren die Datei-Erweiterung und ein neues Objekt zu erstellen: + + Erstellen Sie entsprechenden Eintrag Objekt Var Eintrag; + Wenn (entryStruct.isDirectory) {Eintrag = neues DirectoryEntry (entryStruct.name, entryStruct.fullPath, neue FileSystem(entryStruct.filesystemName));} sonst {Eintrag = neue FileEntries (entryStruct.name, entryStruct.fullPath, neue FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/es/README.md b/plugins/cordova-plugin-file/doc/es/README.md new file mode 100644 index 0000000..2a653bf --- /dev/null +++ b/plugins/cordova-plugin-file/doc/es/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +Este plugin implementa una API de archivo que permite acceso de lectura/escritura a los archivos que residen en el dispositivo. + +Este plugin se basa en varias especificaciones, incluyendo: el HTML5 archivo API + +Los directorios (ahora extinto) y sistema de extensiones más recientes: aunque la mayor parte del código del plugin fue escrito cuando una especificación anterior era actual: + +También implementa la especificación de FileWriter: + +Para el uso, por favor, consulte 'HTML5 Rocks excelente [FileSystem artículo.](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +Para tener una visión general de otras opciones de almacenamiento, consulte [Guía de almacenamiento Cordova](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html). + +Este plugin define global `cordova.file` objeto. + +Aunque en el ámbito global, no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Instalación + + cordova plugin add cordova-plugin-file + + +## Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 y 8 * + * Windows 8 * + * Windows* + * Explorador + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## Donde almacenar los archivos + +A partir de v1.2.0, URLs a directorios de sistema de archivos importantes son proporcionadas. Cada dirección URL está en la forma *file:///path/to/spot/*y se puede convertir en un `DirectoryEntry` usando`window.resolveLocalFileSystemURL()`. + + * `cordova.file.applicationDirectory`-Directorio Read-only donde está instalada la aplicación. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.applicationStorageDirectory`-Directorio del entorno limitado de la aplicación; en iOS esta ubicación es de sólo lectura (pero subdirectorios específicos [como `/Documents` ] son de lectura y escritura). Todos los datos contenidos dentro es privado para la aplicación. ( *iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.dataDirectory`-Almacenamiento de datos persistente y privadas dentro de entorno limitado de la aplicación utilizando la memoria interna (en Android, si necesitas usar memoria externa, use `.externalDataDirectory` ). En iOS, este directorio no está sincronizado con iCloud (utilice `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.cacheDirectory`-Directorio para los archivos de datos almacenados en caché o los archivos que su aplicación puede volver a crear fácilmente. El sistema operativo puede borrar estos archivos cuando el dispositivo se agota en almacenamiento de información, sin embargo, aplicaciones no deben confiar en el sistema operativo para eliminar los archivos de aquí. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.externalApplicationStorageDirectory`-Espacio aplicación de almacenamiento externo. (*Android*) + + * `cordova.file.externalDataDirectory`¿Dónde poner los archivos de datos específicos de la aplicación de almacenamiento externo. (*Android*) + + * `cordova.file.externalCacheDirectory`-Caché aplicación de almacenamiento externo. (*Android*) + + * `cordova.file.externalRootDirectory`-Raíz de almacenamiento externo (tarjeta SD). (*Android*, *BlackBerry 10*) + + * `cordova.file.tempDirectory`-Directorio temporal que puede borrar el sistema operativo en sí. No confíe en el sistema operativo para borrar este directorio; su aplicación siempre debe eliminar archivos según corresponda. (*iOS*) + + * `cordova.file.syncedDataDirectory`-Contiene los archivos de la aplicación específica que deben ser sincronizados (e.g. a iCloud). (*iOS*) + + * `cordova.file.documentsDirectory`-Archivos privados a la aplicación, pero que son significativos para otra aplicación (por ejemplo archivos de Office). (*iOS*) + + * `cordova.file.sharedDirectory`-Archivos disponibles globalmente para todas las aplicaciones (*BlackBerry 10*) + +## Diseños de sistema de archivo + +Aunque técnicamente un detalle de la implementación, puede ser muy útil saber cómo la `cordova.file.*` mapa de propiedades en trazados físicos en un dispositivo real. + +### iOS diseño de sistema de archivo + +| Ruta de dispositivo | `Cordova.file.*` | `iosExtraFileSystems` | ¿r/w? | ¿persistente? | OS despeja | sincronización | privado | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:-----:|:-------------:|:-------------:|:--------------:|:-------:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N / A | N / A | N / A | Sí | +|    `appname.app/` | applicationDirectory | Bundle | r | N / A | N / A | N / A | Sí | +|       `www/` | - | - | r | N / A | N / A | N / A | Sí | +|    `Documents/` | documentsDirectory | documentos | r/w | Sí | No | Sí | Sí | +|       `NoCloud/` | - | documentos-nosync | r/w | Sí | No | No | Sí | +|    `Library` | - | Biblioteca | r/w | Sí | No | ¿Sí? | Sí | +|       `NoCloud/` | dataDirectory | Biblioteca-nosync | r/w | Sí | No | No | Sí | +|       `Cloud/` | syncedDataDirectory | - | r/w | Sí | No | Sí | Sí | +|       `Caches/` | cacheDirectory | caché | r/w | Sí * | Yes**\* | No | Sí | +|    `tmp/` | tempDirectory | - | r/w | No** | Yes**\* | No | Sí | + +\ * Archivos persisten a través de reinicios de aplicación y actualizaciones, pero este directorio puede ser despejó cuando el OS deseos. Su aplicación debe ser capaz de recrear cualquier contenido que puede ser eliminado. + +** Archivos puede persistir a través de la aplicación se reinicia, pero no confiar en este comportamiento. Los archivos no se garantizan que persisten a través de actualizaciones. Su aplicación debe eliminar los archivos de este directorio cuando es aplicable, como el sistema operativo no garantiza cuando (o incluso si) estos archivos se quitan. + +**\ * OS la puede borrar el contenido de este directorio siempre que se siente es necesario, pero no dependen de esto. Debe borrar este directorio según sea apropiado para su aplicación. + +### Disposición del sistema Android File + +| Ruta de dispositivo | `Cordova.file.*` | `AndroidExtraFileSystems` | ¿r/w? | ¿persistente? | OS despeja | privado | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:-----:|:-------------:|:----------:|:-------:| +| `File:///android_asset/` | applicationDirectory | | r | N / A | N / A | Sí | +| `/Data/data/< id de aplicación > /` | applicationStorageDirectory | - | r/w | N / A | N / A | Sí | +|    `cache` | cacheDirectory | caché | r/w | Sí | Sí \ * | Sí | +|    `files` | dataDirectory | archivos | r/w | Sí | No | Sí | +|       `Documents` | | documentos | r/w | Sí | No | Sí | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Sí | No | No | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Sí | No | No | +|       `cache` | externalCacheDirectry | caché-externo | r/w | Sí | No** | No | +|       `files` | externalDataDirectory | archivos externos | r/w | Sí | No | No | + +\ * El sistema operativo periódicamente puede borrar este directorio, pero no confiar en este comportamiento. Borrar el contenido de este directorio según sea apropiado para su aplicación. El contenido de este directorio debe un usuario purga la caché manualmente, se eliminan. + +** El sistema operativo no borrar este directorio automáticamente; usted es responsable de administrar el contenido usted mismo. Deberá el usuario purga la caché manualmente, se extraen los contenidos del directorio. + +**Nota**: Si no se puede montar de almacenamiento externo, el `cordova.file.external*` Propiedades`null`. + +### Disposición del sistema blackBerry 10 archivo + +| Ruta de dispositivo | `Cordova.file.*` | ¿r/w? | ¿persistente? | OS despeja | privado | +|:------------------------------------------------------------- |:--------------------------- |:-----:|:-------------:|:----------:|:-------:| +| `File:///accounts/1000/AppData/ < id de aplicación > /` | applicationStorageDirectory | r | N / A | N / A | Sí | +|    `app/native` | applicationDirectory | r | N / A | N / A | Sí | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | No | Sí | Sí | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Sí | No | Sí | +| `File:///accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Sí | No | No | +| `File:///accounts/1000/shared` | sharedDirectory | r/w | Sí | No | No | + +*Nota*: cuando se implementa la aplicación al trabajo de perímetro, todos los caminos son relativos a /accounts/1000-enterprise. + +## Rarezas Android + +### Ubicación de almacenamiento persistente Android + +Hay múltiples ubicaciones válidas para almacenar archivos persistentes en un dispositivo Android. Vea [esta página](http://developer.android.com/guide/topics/data/data-storage.html) para una extensa discusión de las distintas posibilidades. + +Las versiones anteriores del plugin elegiría la ubicación de los archivos temporales y persistentes en el arranque, basado en si el dispositivo afirmó que fue montado en la tarjeta SD (o partición de almacenamiento equivalente). Si fue montada en la tarjeta SD, o una partición de gran almacenamiento interno estaba disponible (como en dispositivos de Nexus,) y luego los archivos persistentes se almacenaría en la raíz de ese espacio. Esto significaba que todas las apps Cordova podían ver todos los archivos disponibles en la tarjeta. + +Si la tarjeta SD no estaba disponible, entonces versiones anteriores podría almacenar datos debajo de `/data/data/` , que aísla las apps del otro, pero puede todavía causa datos para ser compartido entre los usuarios. + +Ahora es posible elegir si desea almacenar archivos en la ubicación de almacenamiento del archivo interno, o usando la lógica anterior, con una preferencia en de la aplicación `config.xml` archivo. Para ello, añada una de estas dos líneas a `config.xml` : + + + + + + +Sin esta línea, se utilizará el archivo plugin `Compatibility` como valor predeterminado. Si una etiqueta de preferencia está presente y no es uno de estos valores, no se iniciará la aplicación. + +Si su solicitud se ha enviado previamente a los usuarios, usando un mayor (1.0 pre) versión de este plugin y archivos almacenados en el sistema de ficheros persistente, entonces debería establecer la preferencia en `Compatibility` . Cambiar la ubicación para "Internal" significa que los usuarios existentes que actualización su aplicación pueden ser incapaces de acceder a sus archivos previamente almacenadas, dependiendo de su dispositivo. + +Si su solicitud es nuevo, o nunca antes ha almacenado archivos en el sistema de ficheros persistente, entonces el `Internal` generalmente se recomienda el ajuste. + +### Operaciones recursivas lento para /android_asset + +Listado de directorios activos es realmente lento en Android. Usted puede acelerar hacia arriba, agregando `src/android/build-extras.gradle` a la raíz de tu proyecto android (también requiere de cordova-android@4.0.0 o mayor). + +## iOS rarezas + + * `cordova.file.applicationStorageDirectory`es de sólo lectura; intentar almacenar archivos en el directorio raíz fallará. Utilice uno de los `cordova.file.*` las propiedades definidas para iOS (sólo `applicationDirectory` y `applicationStorageDirectory` son de sólo lectura). + * `FileReader.readAsText(blob, encoding)` + * El `encoding` no se admite el parámetro, y codificación UTF-8 es siempre en efecto. + +### iOS ubicación de almacenamiento persistente + +Hay dos ubicaciones válidas para almacenar archivos persistentes en un dispositivo iOS: el directorio de documentos y el directorio de biblioteca. Las versiones anteriores del plugin sólo almacenan archivos persistentes en el directorio de documentos. Esto tenía el efecto secundario de todos los archivos de la aplicación haciendo visible en iTunes, que era a menudo involuntarios, especialmente para aplicaciones que manejan gran cantidad de archivos pequeños, en lugar de producir documentos completos para la exportación, que es la finalidad del directorio. + +Ahora es posible elegir si desea almacenar archivos en los documentos o directorio de bibliotecas, con preferencia en de la aplicación `config.xml` archivo. Para ello, añada una de estas dos líneas a `config.xml` : + + + + + + +Sin esta línea, se utilizará el archivo plugin `Compatibility` como valor predeterminado. Si una etiqueta de preferencia está presente y no es uno de estos valores, no se iniciará la aplicación. + +Si su solicitud se ha enviado previamente a los usuarios, usando un mayor (1.0 pre) versión de este plugin y archivos almacenados en el sistema de ficheros persistente, entonces debería establecer la preferencia en `Compatibility` . Cambiar la ubicación de `Library` significa que los usuarios existentes que actualización su aplicación sería incapaces de acceder a sus archivos previamente almacenadas. + +Si su solicitud es nuevo, o nunca antes ha almacenado archivos en el sistema de ficheros persistente, entonces el `Library` generalmente se recomienda el ajuste. + +## Firefox OS rarezas + +La API de sistema de archivo de forma nativa no es compatible con Firefox OS y se implementa como una cuña en la parte superior indexedDB. + + * No falla cuando eliminar directorios no vacía + * No admite metadatos para directorios + * Los métodos `copyTo` y `moveTo` no son compatibles con directorios + +Se admiten las siguientes rutas de datos: * `applicationDirectory` -usa `xhr` para obtener los archivos locales que están envasados con la aplicación. * `dataDirectory` - Para archivos de datos específicos de aplicación persistente. * `cacheDirectory` -En caché archivos que deben sobrevivir se reinicia la aplicación (aplicaciones no deben confiar en el sistema operativo para eliminar archivos aquí). + +## Navegador rarezas + +### Rarezas y observaciones comunes + + * Cada navegador utiliza su propio sistema de ficheros un espacio aislado. IE y Firefox utilizan IndexedDB como base. Todos los navegadores utilizan diagonal como separador de directorio en un camino. + * Las entradas de directorio deben crearse sucesivamente. Por ejemplo, la llamada `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` se producirá un error si no existiera dir1. + * El plugin solicita permiso de usuario para usar almacenamiento persistente en el primer comienzo de la aplicación. + * Plugin soporta `cdvfile://localhost` (recursos locales) solamente. Es decir, no se admiten los recursos externos vía `cdvfile`. + * El plugin no sigue ["Archivo sistema API 8.3 nombrando restricciones"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). + * BLOB y archivo ' `close` la función no es compatible. + * `FileSaver` y `BlobBuilder` no son compatibles con este plugin y no tengo recibos. + * El plugin no es compatible con `requestAllFileSystems`. Esta función también está desaparecida en las especificaciones. + * No se quitarán las entradas de directorio Si utilizas `create: true` bandera de directorio existente. + * No se admiten archivos creados mediante el constructor. Debe utilizar método entry.file en su lugar. + * Cada navegador utiliza su propia forma de blob URL referencias. + * se admite la función `readAsDataURL`, pero el mediatype en cromo depende de la extensión de nombre de entrada, mediatype en IE siempre está vacío (que es lo mismo como `plain-text` según la especificación), el mediatype en Firefox siempre es `application/octet-stream`. Por ejemplo, si el contenido es `abcdefg` entonces Firefox devuelve `datos: aplicación / octet-stream; base64, YWJjZGVmZw ==`, es decir devuelve `datos:; base64, YWJjZGVmZw ==`, cromo devuelve `datos: < mediatype dependiendo de la extensión de nombre de la entrada >; base64, YWJjZGVmZw ==`. + * `toInternalURL` devuelve la ruta de la forma `file:///persistent/path/to/entry` (Firefox, IE). Cromo devuelve la ruta de acceso en el formulario `cdvfile://localhost/persistent/file`. + +### Rarezas de Chrome + + * Filesystem de Chrome no es inmediatamente después de evento ready dispositivo. Como solución temporal puede suscribirse al evento `filePluginIsReady`. Ejemplo: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +Puede utilizar la función `window.isFilePluginReadyRaised` para verificar si ya se provoca el evento. -window.requestFileSystem temporal y persistente filesystem cuotas no están limitadas en cromo. -Para aumentar el almacenamiento persistente en cromo necesitas llamar el método `window.initPersistentFileSystem`. Cuota de almacenamiento persistente es de 5 MB por defecto. -Chrome requiere `--permitir-archivo-acceso-de-archivos` ejecutar argumento al soporte API mediante protocolo `file:///`. -`Archivo` objeto no cambiará si utilizas bandera `{create:true}` cuando una `entrada` de existente. -eventos `cancelable` propiedad está establecida en true en cromo. Esto es contrario a la [Especificación](http://dev.w3.org/2009/dap/file-system/file-writer.html). -función de `toURL` en Chrome devuelve `filesystem:`-prefijo camino dependiendo de host de la aplicación. Por ejemplo, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -resultado de la función de `toURL` no contiene barra en caso de entrada en el directorio. Cromo resuelve directorios con urls slash-siguió correctamente sin embargo. -método `resolveLocalFileSystemURL` requiere la entrantes `url` que tienen prefijo `filesystem`. Por ejemplo, el parámetro de `url` para `resolveLocalFileSystemURL` debería estar en la forma `filesystem:file:///persistent/somefile.txt` en comparación con la forma `file:///persistent/somefile.txt` en Android. -Obsoleto `toNativeURL` función no es compatible y no tiene un trozo. -función de `setMetadata` no es indicada en las especificaciones y no admite. -INVALID_MODIFICATION_ERR (código: 9) se lanza en lugar de SYNTAX_ERR(code: 8) a petición de un sistema de ficheros inexistentes. -INVALID_MODIFICATION_ERR (código: 9) se lanza en vez de PATH_EXISTS_ERR(code: 12) en intentar exclusivamente crear un archivo o directorio, que ya existe. -INVALID_MODIFICATION_ERR (código: 9) se lanza en lugar de NO_MODIFICATION_ALLOWED_ERR(code: 6) para tratar de llamar a removeRecursively en el sistema de archivos raíz. -INVALID_MODIFICATION_ERR (código: 9) se lanza en vez de NOT_FOUND_ERR(code: 1) en tratar de moveTo directorio que no existe. + +### Impl base IndexedDB rarezas (IE y Firefox) + + * `.` y `..` no son compatibles. + * IE no soporta `file:///`-modo; modo alojado sólo es compatible (http://localhost:xxxx). + * Tamaño del sistema de archivos de Firefox no es limitada pero cada extensión de 50 MB solicitará un permiso de usuario. IE10 permite hasta 10mb de combinados AppCache y IndexedDB utilizados en la implementación del sistema de ficheros sin preguntar, cuando llegas a ese nivel que se le preguntará si desea permitir que ser aumentada hasta un máximo de 250 mb por sitio. Para que `size` parámetro para la función `requestFileSystem` no afecta sistema de ficheros en Firefox y IE. + * la función `readAsBinaryString` no se indica en las especificaciones y no compatible con IE y no tiene un trozo. + * `file.type` siempre es null. + * No debe crear entrada utilizando DirectoryEntry resultado de devolución de llamada de instancia que fue borrado. De lo contrario, obtendrá una entrada' colgar'. + * Antes de que se puede leer un archivo, el cual fue escrito sólo que necesitas una nueva instancia de este archivo. + * la función `setMetadata`, que no es indicada en las especificaciones soporta sólo el cambio de campo `modificationTime`. + * `copyTo` y `moveTo` funciones no son compatibles con directorios. + * Metadatos de directorios no es compatible. + * Tanto Entry.remove y directoryEntry.removeRecursively no fallan al retirar no vacía directorios - directorios de ser eliminados se limpian junto con contenido en su lugar. + * `abort` y `truncate` las funciones no son compatibles. + * eventos de progreso no están despedidos. Por ejemplo, este controlador no ejecutará: + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## Actualización de notas + +En v1.0.0 de este plugin, han cambiado las estructuras `FileEntry` y `DirectoryEntry`, para estar más acorde con las especificaciones publicadas. + +Versiones anteriores (pre-1.0.0) del plugin almacenan el dispositivo-absoluto-archivo-ubicación en la propiedad `fullPath` de objetos de `entrada`. Estos caminos típicamente parecería + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Estas rutas también fueron devueltos por el método `toURL()` de los objetos de `entrada`. + +Con v1.0.0, el atributo `fullPath` es la ruta del archivo, *relativo a la raíz del sistema de archivos HTML*. Así, los caminos más arriba sería ahora ambos ser representado por un objeto `FileEntry` con un `fullPath` de + + /path/to/file + + +Si su aplicación funciona con dispositivo-absoluto-caminos, y previamente obtenido esos caminos a través de la propiedad `fullPath` de objetos de `Entry`, deberá actualizar el código para utilizar `entry.toURL()` en su lugar. + +Para atrás compatibilidad, el método `resolveLocalFileSystemURL()` a aceptar un dispositivo-absoluto-trayectoria y devolverá un objeto de `Entry` correspondiente que, mientras exista ese archivo dentro de los sistemas de ficheros `TEMPORARY` o la `PERSISTENT`. + +Esto ha sido particularmente un problema con el plugin de transferencia de archivos, que anteriormente utilizado dispositivo-absoluto-caminos (y todavía puede aceptarlas). Ha sido actualizado para funcionar correctamente con sistema de ficheros URLs, para reemplazar `entry.fullPath` con `entry.toURL()` debe resolver cualquier problema conseguir ese plugin para trabajar con archivos en el dispositivo. + +En v1.1.0 el valor devuelto por `toURL()` fue cambiado (consulte \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) para devolver una dirección URL absoluta 'file://'. siempre que sea posible. Para asegurar una ' cdvfile:'-URL ahora puede utilizar `toInternalURL()`. Este método devolverá ahora filesystem URLs de la forma + + cdvfile://localhost/persistent/path/to/file + + +que puede utilizarse para identificar el archivo únicamente. + +## Lista de códigos de Error y significados + +Cuando se produce un error, uno de los siguientes códigos se utilizará. + +| Código | Constante | +| ------:|:----------------------------- | +| 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` | + +## Configurando el Plugin (opcional) + +El conjunto de los sistemas de ficheros disponibles puede ser configurado por plataforma. Tanto iOS y Android reconocen un etiqueta en el `archivo config.xml` que nombra a los sistemas de archivos para ser instalado. De forma predeterminada, se activan todas las raíces del sistema de archivos. + + + + + +### Android + + * `files`: directorio de almacenamiento de archivo interno de la aplicación + * `files-external`: directorio de almacenamiento de archivo externo de la aplicación + * `sdcard`: el directorio de almacenamiento de archivo externo global (esta es la raíz de la tarjeta SD, si uno está instalado). Debe tener el permiso de `android.permission.WRITE_EXTERNAL_STORAGE` a usar esto. + * `cache`: directorio de memoria caché interna de la aplicación + * `cache-external`: directorio de caché externo de la aplicación + * `root`: el sistema de archivos de todo el dispositivo + +Android también es compatible con un sistema de archivos especial llamado "documents", que representa un subdirectorio "/Documents/" dentro del sistema de archivos "archivos". + +### iOS + + * `library`: directorio de bibliotecas de la aplicación + * `documents`: directorio de documentos de la aplicación + * `cache`: directorio de caché de la aplicación + * `bundle`: paquete de la aplicación; la ubicación de la aplicación en sí mismo en el disco (sólo lectura) + * `root`: el sistema de archivos de todo el dispositivo + +De forma predeterminada, los directorios de documentos y la biblioteca pueden ser sincronizados con iCloud. También puede solicitar dos sistemas adicionales, `library-nosync` y `documents-nosync`, que representan un directorio especial no sincronizados dentro de la `/Library` o sistema de ficheros `/Documents`. \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/es/index.md b/plugins/cordova-plugin-file/doc/es/index.md new file mode 100644 index 0000000..a9c0264 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/es/index.md @@ -0,0 +1,336 @@ + + +# cordova-plugin-file + +Este plugin implementa una API de archivo que permite acceso de lectura/escritura a los archivos que residen en el dispositivo. + +Este plugin se basa en varias especificaciones, incluyendo: el HTML5 archivo API + +Los directorios (ahora extinto) y sistema de extensiones más recientes: aunque la mayor parte del código del plugin fue escrito cuando una especificación anterior era actual: + +También implementa la especificación de FileWriter: + +Para el uso, por favor, consulte 'HTML5 Rocks excelente [FileSystem artículo.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +Para tener una visión general de otras opciones de almacenamiento, consulte [Guía de almacenamiento Cordova][2]. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +Este plugin define global `cordova.file` objeto. + +Aunque en el ámbito global, no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Instalación + + cordova plugin add cordova-plugin-file + + +## Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 y 8 * +* Windows 8 * +* Explorador + +* *No son compatibles con estas plataformas `FileReader.readAsArrayBuffer` ni `FileWriter.write(blob)` .* + +## Donde almacenar los archivos + +A partir de v1.2.0, URLs a directorios de sistema de archivos importantes son proporcionadas. Cada dirección URL está en la forma *file:///path/to/spot/*y se puede convertir en un `DirectoryEntry` usando`window.resolveLocalFileSystemURL()`. + +* `cordova.file.applicationDirectory`-Directorio Read-only donde está instalada la aplicación. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.applicationStorageDirectory`-Directorio del entorno limitado de la aplicación; en iOS esta ubicación es de sólo lectura (pero subdirectorios específicos [como `/Documents` ] son de lectura y escritura). Todos los datos contenidos dentro es privado para la aplicación. ( *iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.dataDirectory`-Almacenamiento de datos persistente y privadas dentro de entorno limitado de la aplicación utilizando la memoria interna (en Android, si necesitas usar memoria externa, use `.externalDataDirectory` ). En iOS, este directorio no está sincronizado con iCloud (utilice `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.cacheDirectory`-Directorio para los archivos de datos almacenados en caché o los archivos que su aplicación puede volver a crear fácilmente. El sistema operativo puede borrar estos archivos cuando el dispositivo se agota en almacenamiento de información, sin embargo, aplicaciones no deben confiar en el sistema operativo para eliminar los archivos de aquí. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.externalApplicationStorageDirectory`-Espacio aplicación de almacenamiento externo. (*Android*) + +* `cordova.file.externalDataDirectory`¿Dónde poner los archivos de datos específicos de la aplicación de almacenamiento externo. (*Android*) + +* `cordova.file.externalCacheDirectory`-Caché aplicación de almacenamiento externo. (*Android*) + +* `cordova.file.externalRootDirectory`-Raíz de almacenamiento externo (tarjeta SD). (*Android*, *BlackBerry 10*) + +* `cordova.file.tempDirectory`-Directorio temporal que puede borrar el sistema operativo en sí. No confíe en el sistema operativo para borrar este directorio; su aplicación siempre debe eliminar archivos según corresponda. (*iOS*) + +* `cordova.file.syncedDataDirectory`-Contiene los archivos de la aplicación específica que deben ser sincronizados (e.g. a iCloud). (*iOS*) + +* `cordova.file.documentsDirectory`-Archivos privados a la aplicación, pero que son significativos para otra aplicación (por ejemplo archivos de Office). (*iOS*) + +* `cordova.file.sharedDirectory`-Archivos disponibles globalmente para todas las aplicaciones (*BlackBerry 10*) + +## Diseños de sistema de archivo + +Aunque técnicamente un detalle de la implementación, puede ser muy útil saber cómo la `cordova.file.*` mapa de propiedades en trazados físicos en un dispositivo real. + +### iOS diseño de sistema de archivo + +| Ruta de dispositivo | `Cordova.file.*` | `iosExtraFileSystems` | ¿r/w? | ¿persistente? | OS despeja | sincronización | privado | +|:-------------------------------------------- |:--------------------------- |:--------------------- |:-----:|:-------------:|:----------:|:--------------:|:-------:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N / A | N / A | N / A | Sí | +|    `appname.app/` | applicationDirectory | Bundle | r | N / A | N / A | N / A | Sí | +|       `www/` | - | - | r | N / A | N / A | N / A | Sí | +|    `Documents/` | documentsDirectory | documentos | r/w | Sí | No | Sí | Sí | +|       `NoCloud/` | - | documentos-nosync | r/w | Sí | No | No | Sí | +|    `Library` | - | Biblioteca | r/w | Sí | No | ¿Sí? | Sí | +|       `NoCloud/` | dataDirectory | Biblioteca-nosync | r/w | Sí | No | No | Sí | +|       `Cloud/` | syncedDataDirectory | - | r/w | Sí | No | Sí | Sí | +|       `Caches/` | cacheDirectory | caché | r/w | Sí * | Si * * *| | No | Sí | +|    `tmp/` | tempDirectory | - | r/w | No * * | Si * * *| | No | Sí | + +* Archivos persisten a través de la aplicación se reinicia y actualizaciones, pero este directorio puede ser despejó cuando el OS desea. Su aplicación debe ser capaz de recrear cualquier contenido que puede ser eliminado. + +* * Archivos pueden persistir a través de la aplicación se reinicia, pero no confiar en este comportamiento. Los archivos no se garantizan que persisten a través de actualizaciones. Su aplicación debe eliminar los archivos de este directorio cuando es aplicable, como el sistema operativo no garantiza cuando (o incluso si) estos archivos se quitan. + +* * *| OS la puede borrar el contenido de este directorio cuando se siente que es necesario, pero no dependen de éste. Debe borrar este directorio según sea apropiado para su aplicación. + +### Disposición del sistema Android File + +| Ruta de dispositivo | `Cordova.file.*` | `AndroidExtraFileSystems` | ¿r/w? | ¿persistente? | OS despeja | privado | +|:----------------------------------------- |:----------------------------------- |:------------------------- |:-----:|:-------------:|:----------:|:-------:| +| `File:///android_asset/` | applicationDirectory | | r | N / A | N / A | Sí | +| `/Data/data/< id de aplicación > /` | applicationStorageDirectory | - | r/w | N / A | N / A | Sí | +|    `cache` | cacheDirectory | caché | r/w | Sí | Sí * | Sí | +|    `files` | dataDirectory | archivos | r/w | Sí | No | Sí | +|       `Documents` | | documentos | r/w | Sí | No | Sí | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Sí | No | No | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Sí | No | No | +|       `cache` | externalCacheDirectry | caché-externo | r/w | Sí | No * * | No | +|       `files` | externalDataDirectory | archivos externos | r/w | Sí | No | No | + +* El sistema operativo puede eliminar periódicamente este directorio, pero no dependen de este comportamiento. Borrar el contenido de este directorio según sea apropiado para su aplicación. El contenido de este directorio debe un usuario purga la caché manualmente, se eliminan. + +* * El sistema operativo no borra este directorio automáticamente; Usted es responsable de administrar el contenido mismo. Deberá el usuario purga la caché manualmente, se extraen los contenidos del directorio. + +**Nota**: Si no se puede montar de almacenamiento externo, el `cordova.file.external*` Propiedades`null`. + +### Disposición del sistema blackBerry 10 archivo + +| Ruta de dispositivo | `Cordova.file.*` | ¿r/w? | ¿persistente? | OS despeja | privado | +|:------------------------------------------------------------- |:--------------------------- |:-----:|:-------------:|:----------:|:-------:| +| `File:///accounts/1000/AppData/ < id de aplicación > /` | applicationStorageDirectory | r | N / A | N / A | Sí | +|    `app/native` | applicationDirectory | r | N / A | N / A | Sí | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | No | Sí | Sí | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Sí | No | Sí | +| `File:///accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Sí | No | No | +| `File:///accounts/1000/shared` | sharedDirectory | r/w | Sí | No | No | + +*Nota*: cuando se implementa la aplicación al trabajo de perímetro, todos los caminos son relativos a /accounts/1000-enterprise. + +## Rarezas Android + +### Ubicación de almacenamiento persistente Android + +Hay múltiples ubicaciones válidas para almacenar archivos persistentes en un dispositivo Android. Vea [esta página][3] para una extensa discusión de las distintas posibilidades. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +Las versiones anteriores del plugin elegiría la ubicación de los archivos temporales y persistentes en el arranque, basado en si el dispositivo afirmó que fue montado en la tarjeta SD (o partición de almacenamiento equivalente). Si fue montada en la tarjeta SD, o una partición de gran almacenamiento interno estaba disponible (como en dispositivos de Nexus,) y luego los archivos persistentes se almacenaría en la raíz de ese espacio. Esto significaba que todas las apps Cordova podían ver todos los archivos disponibles en la tarjeta. + +Si la tarjeta SD no estaba disponible, entonces versiones anteriores podría almacenar datos debajo de `/data/data/` , que aísla las apps del otro, pero puede todavía causa datos para ser compartido entre los usuarios. + +Ahora es posible elegir si desea almacenar archivos en la ubicación de almacenamiento del archivo interno, o usando la lógica anterior, con una preferencia en de la aplicación `config.xml` archivo. Para ello, añada una de estas dos líneas a `config.xml` : + + < nombre de preferencia = "AndroidPersistentFileLocation" value = "Internal" / >< nombre de preferencia = "AndroidPersistentFileLocation" value = "Compatibilidad" / > + + +Sin esta línea, se utilizará el archivo plugin `Compatibility` como valor predeterminado. Si una etiqueta de preferencia está presente y no es uno de estos valores, no se iniciará la aplicación. + +Si su solicitud se ha enviado previamente a los usuarios, usando un mayor (1.0 pre) versión de este plugin y archivos almacenados en el sistema de ficheros persistente, entonces debería establecer la preferencia en `Compatibility` . Cambiar la ubicación para "Internal" significa que los usuarios existentes que actualización su aplicación pueden ser incapaces de acceder a sus archivos previamente almacenadas, dependiendo de su dispositivo. + +Si su solicitud es nuevo, o nunca antes ha almacenado archivos en el sistema de ficheros persistente, entonces el `Internal` generalmente se recomienda el ajuste. + +## iOS rarezas + +* `cordova.file.applicationStorageDirectory`es de sólo lectura; intentar almacenar archivos en el directorio raíz fallará. Utilice uno de los `cordova.file.*` las propiedades definidas para iOS (sólo `applicationDirectory` y `applicationStorageDirectory` son de sólo lectura). +* `FileReader.readAsText(blob, encoding)` + * El `encoding` no se admite el parámetro, y codificación UTF-8 es siempre en efecto. + +### iOS ubicación de almacenamiento persistente + +Hay dos ubicaciones válidas para almacenar archivos persistentes en un dispositivo iOS: el directorio de documentos y el directorio de biblioteca. Las versiones anteriores del plugin sólo almacenan archivos persistentes en el directorio de documentos. Esto tenía el efecto secundario de todos los archivos de la aplicación haciendo visible en iTunes, que era a menudo involuntarios, especialmente para aplicaciones que manejan gran cantidad de archivos pequeños, en lugar de producir documentos completos para la exportación, que es la finalidad del directorio. + +Ahora es posible elegir si desea almacenar archivos en los documentos o directorio de bibliotecas, con preferencia en de la aplicación `config.xml` archivo. Para ello, añada una de estas dos líneas a `config.xml` : + + + + + + +Sin esta línea, se utilizará el archivo plugin `Compatibility` como valor predeterminado. Si una etiqueta de preferencia está presente y no es uno de estos valores, no se iniciará la aplicación. + +Si su solicitud se ha enviado previamente a los usuarios, usando un mayor (1.0 pre) versión de este plugin y archivos almacenados en el sistema de ficheros persistente, entonces debería establecer la preferencia en `Compatibility` . Cambiar la ubicación de `Library` significa que los usuarios existentes que actualización su aplicación sería incapaces de acceder a sus archivos previamente almacenadas. + +Si su solicitud es nuevo, o nunca antes ha almacenado archivos en el sistema de ficheros persistente, entonces el `Library` generalmente se recomienda el ajuste. + +## Firefox OS rarezas + +La API de sistema de archivo de forma nativa no es compatible con Firefox OS y se implementa como una cuña en la parte superior indexedDB. + +* No falla cuando eliminar directorios no vacía +* No admite metadatos para directorios +* Los métodos `copyTo` y `moveTo` no son compatibles con directorios + +Se admiten las siguientes rutas de datos: * `applicationDirectory` -usa `xhr` para obtener los archivos locales que están envasados con la aplicación. * `dataDirectory` - Para archivos de datos específicos de aplicación persistente. * `cacheDirectory` -En caché archivos que deben sobrevivir se reinicia la aplicación (aplicaciones no deben confiar en el sistema operativo para eliminar archivos aquí). + +## Navegador rarezas + +### Rarezas y observaciones comunes + +* Cada navegador utiliza su propio sistema de ficheros un espacio aislado. IE y Firefox utilizan IndexedDB como base. Todos los navegadores utilizan diagonal como separador de directorio en un camino. +* Las entradas de directorio deben crearse sucesivamente. Por ejemplo, la llamada `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` se producirá un error si no existiera dir1. +* El plugin solicita permiso de usuario para usar almacenamiento persistente en el primer comienzo de la aplicación. +* Plugin soporta `cdvfile://localhost` (recursos locales) solamente. Es decir, no se admiten los recursos externos vía `cdvfile`. +* El plugin no sigue ["Archivo sistema API 8.3 nombrando restricciones"][4]. +* BLOB y archivo ' `close` la función no es compatible. +* `FileSaver` y `BlobBuilder` no son compatibles con este plugin y no tengo recibos. +* El plugin no es compatible con `requestAllFileSystems`. Esta función también está desaparecida en las especificaciones. +* No se quitarán las entradas de directorio Si utilizas `create: true` bandera de directorio existente. +* No se admiten archivos creados mediante el constructor. Debe utilizar método entry.file en su lugar. +* Cada navegador utiliza su propia forma de blob URL referencias. +* se admite la función `readAsDataURL`, pero el mediatype en cromo depende de la extensión de nombre de entrada, mediatype en IE siempre está vacío (que es lo mismo como `plain-text` según la especificación), el mediatype en Firefox siempre es `application/octet-stream`. Por ejemplo, si el contenido es `abcdefg` entonces Firefox devuelve `datos: aplicación / octet-stream; base64, YWJjZGVmZw ==`, es decir devuelve `datos:; base64, YWJjZGVmZw ==`, cromo devuelve `datos: < mediatype dependiendo de la extensión de nombre de la entrada >; base64, YWJjZGVmZw ==`. +* `toInternalURL` devuelve la ruta de la forma `file:///persistent/path/to/entry` (Firefox, IE). Cromo devuelve la ruta de acceso en el formulario `cdvfile://localhost/persistent/file`. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### Rarezas de Chrome + +* Filesystem de Chrome no es inmediatamente después de evento ready dispositivo. Como solución temporal puede suscribirse al evento `filePluginIsReady`. Ejemplo: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +Puede utilizar la función `window.isFilePluginReadyRaised` para verificar si ya se provoca el evento. -window.requestFileSystem temporal y persistente filesystem cuotas no están limitadas en cromo. -Para aumentar el almacenamiento persistente en cromo necesitas llamar el método `window.initPersistentFileSystem`. Cuota de almacenamiento persistente es de 5 MB por defecto. -Chrome requiere `--permitir-archivo-acceso-de-archivos` ejecutar argumento al soporte API mediante protocolo `file:///`. -`Archivo` objeto no cambiará si utilizas bandera `{create:true}` cuando una `entrada` de existente. -eventos `cancelable` propiedad está establecida en true en cromo. Esto es contrario a la [Especificación][5]. -función de `toURL` en Chrome devuelve `filesystem:`-prefijo camino dependiendo de host de la aplicación. Por ejemplo, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -resultado de la función de `toURL` no contiene barra en caso de entrada en el directorio. Cromo resuelve directorios con urls slash-siguió correctamente sin embargo. -método `resolveLocalFileSystemURL` requiere la entrantes `url` que tienen prefijo `filesystem`. Por ejemplo, el parámetro de `url` para `resolveLocalFileSystemURL` debería estar en la forma `filesystem:file:///persistent/somefile.txt` en comparación con la forma `file:///persistent/somefile.txt` en Android. -Obsoleto `toNativeURL` función no es compatible y no tiene un trozo. -función de `setMetadata` no es indicada en las especificaciones y no admite. -INVALID_MODIFICATION_ERR (código: 9) se lanza en lugar de SYNTAX_ERR(code: 8) a petición de un sistema de ficheros inexistentes. -INVALID_MODIFICATION_ERR (código: 9) se lanza en vez de PATH_EXISTS_ERR(code: 12) en intentar exclusivamente crear un archivo o directorio, que ya existe. -INVALID_MODIFICATION_ERR (código: 9) se lanza en lugar de NO_MODIFICATION_ALLOWED_ERR(code: 6) para tratar de llamar a removeRecursively en el sistema de archivos raíz. -INVALID_MODIFICATION_ERR (código: 9) se lanza en vez de NOT_FOUND_ERR(code: 1) en tratar de moveTo directorio que no existe. + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### Impl base IndexedDB rarezas (IE y Firefox) + +* `.` y `..` no son compatibles. +* IE no soporta `file:///`-modo; modo alojado sólo es compatible (http://localhost:xxxx). +* Tamaño del sistema de archivos de Firefox no es limitada pero cada extensión de 50 MB solicitará un permiso de usuario. IE10 permite hasta 10mb de combinados AppCache y IndexedDB utilizados en la implementación del sistema de ficheros sin preguntar, cuando llegas a ese nivel que se le preguntará si desea permitir que ser aumentada hasta un máximo de 250 mb por sitio. Para que `size` parámetro para la función `requestFileSystem` no afecta sistema de ficheros en Firefox y IE. +* la función `readAsBinaryString` no se indica en las especificaciones y no compatible con IE y no tiene un trozo. +* `file.type` siempre es null. +* No debe crear entrada utilizando DirectoryEntry resultado de devolución de llamada de instancia que fue borrado. De lo contrario, obtendrá una entrada' colgar'. +* Antes de que se puede leer un archivo, el cual fue escrito sólo que necesitas una nueva instancia de este archivo. +* la función `setMetadata`, que no es indicada en las especificaciones soporta sólo el cambio de campo `modificationTime`. +* `copyTo` y `moveTo` funciones no son compatibles con directorios. +* Metadatos de directorios no es compatible. +* Tanto Entry.remove y directoryEntry.removeRecursively no fallan al retirar no vacía directorios - directorios de ser eliminados se limpian junto con contenido en su lugar. +* `abort` y `truncate` las funciones no son compatibles. +* eventos de progreso no están despedidos. Por ejemplo, este controlador no ejecutará: + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## Actualización de notas + +En v1.0.0 de este plugin, han cambiado las estructuras `FileEntry` y `DirectoryEntry`, para estar más acorde con las especificaciones publicadas. + +Versiones anteriores (pre-1.0.0) del plugin almacenan el dispositivo-absoluto-archivo-ubicación en la propiedad `fullPath` de objetos de `entrada`. Estos caminos típicamente parecería + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Estas rutas también fueron devueltos por el método `toURL()` de los objetos de `entrada`. + +Con v1.0.0, el atributo `fullPath` es la ruta del archivo, *relativo a la raíz del sistema de archivos HTML*. Así, los caminos más arriba sería ahora ambos ser representado por un objeto `FileEntry` con un `fullPath` de + + /path/to/file + + +Si su aplicación funciona con dispositivo-absoluto-caminos, y previamente obtenido esos caminos a través de la propiedad `fullPath` de objetos de `Entry`, deberá actualizar el código para utilizar `entry.toURL()` en su lugar. + +Para atrás compatibilidad, el método `resolveLocalFileSystemURL()` a aceptar un dispositivo-absoluto-trayectoria y devolverá un objeto de `Entry` correspondiente que, mientras exista ese archivo dentro de los sistemas de ficheros `TEMPORARY` o la `PERSISTENT`. + +Esto ha sido particularmente un problema con el plugin de transferencia de archivos, que anteriormente utilizado dispositivo-absoluto-caminos (y todavía puede aceptarlas). Ha sido actualizado para funcionar correctamente con sistema de ficheros URLs, para reemplazar `entry.fullPath` con `entry.toURL()` debe resolver cualquier problema conseguir ese plugin para trabajar con archivos en el dispositivo. + +En v1.1.0 el valor devuelto por `toURL()` fue cambiado (consulte \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) para devolver una dirección URL absoluta 'file://'. siempre que sea posible. Para asegurar una ' cdvfile:'-URL ahora puede utilizar `toInternalURL()`. Este método devolverá ahora filesystem URLs de la forma + + cdvfile://localhost/persistent/path/to/file + + +que puede utilizarse para identificar el archivo únicamente. + +## Lista de códigos de Error y significados + +Cuando se produce un error, uno de los siguientes códigos se utilizará. + +| Código | Constante | +| ------:|:----------------------------- | +| 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` | + +## Configurando el Plugin (opcional) + +El conjunto de los sistemas de ficheros disponibles puede ser configurado por plataforma. Tanto iOS y Android reconocen un etiqueta en el `archivo config.xml` que nombra a los sistemas de archivos para ser instalado. De forma predeterminada, se activan todas las raíces del sistema de archivos. + + + + + +### Android + +* `files`: directorio de almacenamiento de archivo interno de la aplicación +* `files-external`: directorio de almacenamiento de archivo externo de la aplicación +* `sdcard`: el directorio de almacenamiento de archivo externo global (esta es la raíz de la tarjeta SD, si uno está instalado). Debe tener el permiso de `android.permission.WRITE_EXTERNAL_STORAGE` a usar esto. +* `cache`: directorio de memoria caché interna de la aplicación +* `cache-external`: directorio de caché externo de la aplicación +* `root`: el sistema de archivos de todo el dispositivo + +Android también es compatible con un sistema de archivos especial llamado "documents", que representa un subdirectorio "/Documents/" dentro del sistema de archivos "archivos". + +### iOS + +* `library`: directorio de bibliotecas de la aplicación +* `documents`: directorio de documentos de la aplicación +* `cache`: directorio de caché de la aplicación +* `bundle`: paquete de la aplicación; la ubicación de la aplicación en sí mismo en el disco (sólo lectura) +* `root`: el sistema de archivos de todo el dispositivo + +De forma predeterminada, los directorios de documentos y la biblioteca pueden ser sincronizados con iCloud. También puede solicitar dos sistemas adicionales, `library-nosync` y `documents-nosync`, que representan un directorio especial no sincronizados dentro de la `/Library` o sistema de ficheros `/Documents`. diff --git a/plugins/cordova-plugin-file/doc/es/plugins.md b/plugins/cordova-plugin-file/doc/es/plugins.md new file mode 100644 index 0000000..dace368 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/es/plugins.md @@ -0,0 +1,101 @@ + + +# Notas para los desarrolladores del plugin + +Estas notas están pensadas principalmente para desarrolladores de Android y el iOS que quieran escribir plugins que interfaz con el sistema de ficheros usando el plugin del archivo. + +## Trabajar con URLs de sistema de archivo de Córdoba + +Desde la versión 1.0.0, este plugin ha utilizado las direcciones URL con un `cdvfile` plan para todas las comunicaciones sobre el puente, en lugar de exponer rutas de sistema de archivos de dispositivos raw para JavaScript. + +En el lado de JavaScript, esto significa que los objetos FileEntry y DirectoryEntry tienen un atributo fullPath que es relativo a la raíz del sistema de archivos HTML. Si JavaScript API de tu plugin acepta un objeto FileEntry o DirectoryEntry, usted debe llamar a `.toURL()` en ese objeto antes de pasar a través del puente al código nativo. + +### Conversión de cdvfile: / / URL al fileystem caminos + +Plugins que necesita escribir en el sistema de archivos puede convertir un archivo recibido sistema URL a una ubicación de sistema de archivos real. Hay varias formas de hacerlo, dependiendo de la plataforma nativa. + +Es importante recordar que no todos `cdvfile://` las direcciones URL son asignables a reales archivos en el dispositivo. Algunas URLs pueden referirse a activos en dispositivos que no están representadas por archivos, o incluso pueden hacer referencia a recursos remotos. Debido a estas posibilidades, plugins siempre debe comprobar si consiguen un resultado significativo cuando tratando de convertir las URL en trazados. + +#### Android + +En Android, el método más simple para convertir un `cdvfile://` URL a una ruta de sistema de archivos es utilizar `org.apache.cordova.CordovaResourceApi` . `CordovaResourceApi`tiene varios métodos que pueden manejar `cdvfile://` URL: + + webView es un miembro de la clase Plugin CordovaResourceApi resourceApi = webView.getResourceApi(); + + Obtener una URL file:/// representando este archivo en el dispositivo, / / o el mismo URL sin cambios si no se puede asignar a un archivo Uri fileURL = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +También es posible utilizar el plugin de archivos directamente: + + Import org.apache.cordova.file.FileUtils; + Import org.apache.cordova.file.FileSystem; + Import java.net.MalformedURLException; + + Obtener el archivo plugin desde el administrador de plugin FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + Dada una URL, haz un camino para tratar de {camino de cadena = filePlugin.filesystemPathForURL(cdvfileURL);} catch (DD e) {/ / el sistema de archivos url no reconocida} + + +Para convertir de una ruta a un `cdvfile://` URL: + + Import org.apache.cordova.file.LocalFilesystemURL; + + Obtener un objeto LocalFilesystemURL para una ruta, / / o null si no se puede representar como una dirección URL cdvfile. + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path); + Obtener la representación string de la URL objeto String cdvfileURL = url.toString(); + + +Si tu plugin crea un archivo y desea devolver un objeto FileEntry para él, usar el plugin de archivos: + + Devolver una estructura JSON adecuado para volver a JavaScript, / / o null si este archivo no es representable como una dirección URL cdvfile. + JSONObject entrada = filePlugin.getEntryForFile(file); + + +#### iOS + +Cordova en iOS no utiliza la misma `CordovaResourceApi` concepto como Android. En iOS, debe usar el archivo plugin para convertir las direcciones URL y rutas de sistema de archivos. + + Obtener un objeto URL CDVFilesystem de una URL string CDVFilesystemURL * url = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL]; + Obtener una ruta de acceso para el objeto URL, o nil si no puede ser asignado a una ruta de archivo NSString * = [filePlugin filesystemPathForURL:url]; + + + Obtener un objeto URL CDVFilesystem para una ruta, o / / nula si no se puede representar como una dirección URL cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + Obtener la representación string de la URL objetos NSString * cdvfileURL = [enlace absoluteString]; + + +Si tu plugin crea un archivo y desea devolver un objeto FileEntry para él, usar el plugin de archivos: + + Obtener un objeto URL CDVFilesystem para una ruta, o / / nula si no se puede representar como una dirección URL cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + Conseguir una estructura para volver a JavaScript NSDictionary * entrada = [filePlugin makeEntryForLocalURL:url] + + +#### JavaScript + +En JavaScript, para obtener un `cdvfile://` dirección URL de un objeto FileEntry o DirectoryEntry, simplemente llame al `.toURL()` en él: + + var cdvfileURL = entry.toURL(); + + +En manipuladores de la respuesta del plugin, para convertir de una estructura FileEntry devuelta a un objeto real de la entrada, su código de controlador debe importar el archivo plugin y crear un nuevo objeto: + + crear apropiado objeto var ingreso; + Si (entryStruct.isDirectory) {entrada = new DirectoryEntry (entryStruct.name, entryStruct.fullPath, FileSystem(entryStruct.filesystemName)) nuevo;} else {entrada = nuevo FileEntry (entryStruct.name, entryStruct.fullPath, nuevo FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/fr/README.md b/plugins/cordova-plugin-file/doc/fr/README.md new file mode 100644 index 0000000..6296a84 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/fr/README.md @@ -0,0 +1,328 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +Ce plugin implémente une API de fichier permettant l'accès en lecture/écriture aux fichiers qui résident sur le périphérique. + +Ce plugin est basé sur plusieurs spécifications, y compris : l'API de fichier HTML5 + +Les répertoires (aujourd'hui disparue) et le système des extensions plus récentes : bien que la plupart du code du plugin a été écrit quand une technique antérieure était en vigueur : + +Il met également en Å“uvre la spécification FileWriter : + +Pour son utilisation, veuillez vous reporter au HTML5 Rocks' excellent [article de système de fichiers.](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +Pour un aperçu des autres options de stockage, consultez [guide d'entreposage de Cordova](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html). + +Ce plugin définit global `cordova.file` objet. + +Bien que dans la portée globale, il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(cordova.file);} + + +## Installation + + cordova plugin add cordova-plugin-file + + +## Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 et 8 * + * Windows 8 * + * Windows* + * Navigateur + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## Emplacement de stockage des fichiers + +À partir de v1.2.0, URL vers des répertoires de système de fichiers importants est fournis. Chaque URL est dans la forme *file:///path/to/spot/*et peut être converti en un `DirectoryEntry` à l'aide`window.resolveLocalFileSystemURL()`. + + * `cordova.file.applicationDirectory`-Lecture seule répertoire où l'application est installée. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.applicationStorageDirectory`-Répertoire racine du bac à sable de l'application ; cet endroit est en lecture seule sur iOS (mais les sous-répertoires spécifiques [comme `/Documents` ] sont en lecture / écriture). Toutes les données qu'il contient est privé de l'application. ( *iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.dataDirectory`-Stockage des données persistants et privés au sein de bac à sable de l'application à l'aide de la mémoire interne (sur Android, si vous avez besoin d'utiliser une mémoire externe, utilisez `.externalDataDirectory` ). Sur iOS, ce répertoire n'est pas synchronisé avec iCloud (utiliser `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.cacheDirectory`-Répertoire pour les fichiers de données en mémoire cache ou les fichiers que votre application peut recréer facilement. L'OS peut supprimer ces fichiers lorsque l'appareil faiblit sur stockage, néanmoins, les applications ne doivent pas compter sur l'OS pour supprimer les fichiers ici. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.externalApplicationStorageDirectory`-Espace l'application sur le stockage externe. (*Android*) + + * `cordova.file.externalDataDirectory`-Où placer les fichiers de données d'application spécifiques sur le stockage externe. (*Android*) + + * `cordova.file.externalCacheDirectory`-Cache de l'application sur le stockage externe. (*Android*) + + * `cordova.file.externalRootDirectory`-Racine de stockage externe (carte SD). (*Android*, *BlackBerry 10*) + + * `cordova.file.tempDirectory`-Répertoire temp que l'OS peut effacer à volonté. Ne comptez pas sur l'OS pour effacer ce répertoire ; votre application doit toujours supprimer les fichiers selon le cas. (*iOS*) + + * `cordova.file.syncedDataDirectory`-Contient des fichiers d'app spécifique qui doivent se synchroniser (par exemple à iCloud). (*iOS*) + + * `cordova.file.documentsDirectory`-Fichiers privés à l'app, mais qui sont significatives pour l'autre application (par exemple les fichiers Office). (*iOS*) + + * `cordova.file.sharedDirectory`-Fichiers disponibles globalement à toutes les applications (*BlackBerry 10*) + +## Structures de système de fichiers + +Bien que techniquement un détail d'implémentation, il peut être très utile de savoir comment les `cordova.file.*` carte de propriétés à des chemins d'accès physiques sur un périphérique réel. + +### iOS agencement de système de fichier + +| Chemin de l'unité | `Cordova.file.*` | `iosExtraFileSystems` | r/w ? | persistants ? | OS efface | Sync | privé | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:-----:|:-------------:|:-------------:|:-----:|:-----:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Oui | +|    `appname.app/` | applicationDirectory | Bundle | r | N/A | N/A | N/A | Oui | +|       `www/` | - | - | r | N/A | N/A | N/A | Oui | +|    `Documents/` | documentsDirectory | documents | r/w | Oui | Non | Oui | Oui | +|       `NoCloud/` | - | documents-nosync | r/w | Oui | Non | Non | Oui | +|    `Library` | - | Bibliothèque | r/w | Oui | Non | Oui ? | Oui | +|       `NoCloud/` | dataDirectory | Bibliothèque-nosync | r/w | Oui | Non | Non | Oui | +|       `Cloud/` | syncedDataDirectory | - | r/w | Oui | Non | Oui | Oui | +|       `Caches/` | cacheDirectory | cache | r/w | Oui * | Oui**\* | Non | Oui | +|    `tmp/` | tempDirectory | - | r/w | Non** | Oui**\* | Non | Oui | + +\ * Fichiers persistent à travers l'application redémarre et mises à niveau, mais ce répertoire peut être effacé à chaque fois que le système d'exploitation désire. Votre application doit être en mesure de recréer tout contenu qui pourrait être supprimé. + +** Fichiers peuvent persister redémarrages de l'application, mais ne vous fiez pas ce comportement. Les fichiers ne sont pas garantis à persister dans l'ensemble de mises à jour. Votre application doit supprimer les fichiers de ce répertoire lorsqu'elle s'applique, comme le système d'exploitation ne garantit pas quand (ou même si) ces fichiers sont supprimés. + +**\ * Le système d'exploitation peut effacer le contenu de ce répertoire chaque fois qu'il se sent il est nécessaire, mais ne comptez pas là-dessus. Vous devez supprimer ce répertoire comme approprié pour votre application. + +### Agencement de système de fichiers Android + +| Chemin de l'unité | `Cordova.file.*` | `AndroidExtraFileSystems` | r/w ? | persistants ? | OS efface | privé | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:-----:|:-------------:|:---------:|:-----:| +| `file:///android_asset/` | applicationDirectory | | r | N/A | N/A | Oui | +| `/ données/data/app < id > /` | applicationStorageDirectory | - | r/w | N/A | N/A | Oui | +|    `cache` | cacheDirectory | cache | r/w | Oui | Oui\ * | Oui | +|    `files` | dataDirectory | fichiers | r/w | Oui | Non | Oui | +|       `Documents` | | documents | r/w | Oui | Non | Oui | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Oui | Non | Non | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Oui | Non | Non | +|       `cache` | externalCacheDirectry | cache-externe | r/w | Oui | Non** | Non | +|       `files` | externalDataDirectory | fichiers externes | r/w | Oui | Non | Non | + +\ * L'OS peut effacer périodiquement ce répertoire, mais ne vous fiez pas ce comportement. Effacer le contenu de ce répertoire comme approprié pour votre application. Un utilisateur doit purger le cache manuellement, le contenu de ce répertoire est supprimé. + +** Le système d'exploitation n'efface pas ce répertoire automatiquement ; vous êtes chargé de gérer le contenu vous-même. L'utilisateur devrait purger le cache manuellement, le contenu du répertoire est supprimé. + +**Remarque**: si le stockage externe ne peut pas être monté, les `cordova.file.external*` sont des propriétés`null`. + +### Configuration du système blackBerry 10 fichier + +| Chemin de l'unité | `Cordova.file.*` | r/w ? | persistants ? | OS efface | privé | +|:----------------------------------------------------------- |:--------------------------- |:-----:|:-------------:|:---------:|:-----:| +| `file:///Accounts/1000/AppData/ < id app > /` | applicationStorageDirectory | r | N/A | N/A | Oui | +|    `app/native` | applicationDirectory | r | N/A | N/A | Oui | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | Non | Oui | Oui | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Oui | Non | Oui | +| `file:///Accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Oui | Non | Non | +| `file:///Accounts/1000/Shared` | sharedDirectory | r/w | Oui | Non | Non | + +*Remarque*: lorsque l'application est déployée dans le périmètre de travail, tous les chemins sont par rapport à /accounts/1000-enterprise. + +## Quirks Android + +### Emplacement de stockage persistant Android + +Il y a plusieurs emplacements valides pour stocker des fichiers persistants sur un appareil Android. Voir [cette page](http://developer.android.com/guide/topics/data/data-storage.html) pour une analyse approfondie des diverses possibilités. + +Les versions précédentes du plugin choisirait l'emplacement des fichiers temporaires et persistantes au démarrage, basé sur la question de savoir si le dispositif réclamé que la carte SD (ou une partition de stockage équivalent) a été montée. Si la carte SD a été montée, ou si une partition de stockage interne importante était disponible (comme sur les appareils Nexus,) puis les fichiers persistants seraient stockés dans la racine de cet espace. Cela signifie que toutes les apps de Cordova pouvaient voir tous les fichiers disponibles sur la carte. + +Si la carte SD n'était pas disponible, les versions précédentes pourraient stocker des données sous `/data/data/` , qui isole des apps de l'autre, mais peut encore cause données à partager entre les utilisateurs. + +Il est maintenant possible de choisir de stocker les fichiers dans l'emplacement de stockage de fichier interne, ou en utilisant la logique précédente, avec une préférence au sein de votre application `config.xml` fichier. Pour ce faire, ajoutez l'un de ces deux lignes de `config.xml` : + + < nom de l'option = « AndroidPersistentFileLocation » value = « Internal » / >< nom de préférence = « AndroidPersistentFileLocation » value = « Compatibilité » / > + + +Sans cette ligne, utilisera le fichier plugin `Compatibility` par défaut. Si une balise de préférence est présente et n'est pas une des valeurs suivantes, l'application ne démarrera pas. + +Si votre application a déjà été expédiée aux utilisateurs, en utilisant une ancienne (avant 1.0) version de ce plugin et dispose des fichiers stockés dans le système de fichiers persistant, alors vous devez définir la préférence au `Compatibility` . Commutation de l'emplacement « Internal » signifierait que les utilisateurs existants qui mettre à niveau leur application peuvent être impossible d'accéder à leurs fichiers déjà enregistrés, selon leur appareil. + +Si votre application est nouvelle ou a jamais précédemment stocké les fichiers dans le système de fichiers persistant, puis la `Internal` réglage est généralement recommandé. + +### Opérations récursives lent pour /android_asset + +Liste des répertoires actifs est vraiment lent sur Android. Vous pouvez accélérer il vers le haut, en ajoutant `src/android/build-extras.gradle` à la racine de votre projet android (requiert également cordova-android@4.0.0 ou supérieur). + +## Notes au sujet d'iOS + + * `cordova.file.applicationStorageDirectory`est en lecture seule ; tentative de stocker des fichiers dans le répertoire racine échoue. Utilisez l'une de l'autre `cordova.file.*` les propriétés définies pour iOS (seulement `applicationDirectory` et `applicationStorageDirectory` sont en lecture seule). + * `FileReader.readAsText(blob, encoding)` + * Le `encoding` paramètre n'est pas pris en charge, et le codage UTF-8 est toujours en vigueur. + +### emplacement de stockage persistant d'iOS + +Il y a deux emplacements valides pour stocker des fichiers persistants sur un appareil iOS : le répertoire de Documents et le répertoire de la bibliothèque. Les versions précédentes du plugin stockaient ne jamais fichiers persistants dans le répertoire de Documents. Cela a eu l'effet secondaire de rendre tous les fichiers de l'application visible dans iTunes, qui était souvent inattendus, en particulier pour les applications qui traitent beaucoup de petits fichiers, plutôt que de produire des documents complets destinés à l'exportation, qui est l'objectif visé par le répertoire. + +Il est maintenant possible de choisir de stocker les fichiers dans le répertoire de bibliothèque, avec une préférence au sein de votre application ou de documents `config.xml` fichier. Pour ce faire, ajoutez l'un de ces deux lignes de `config.xml` : + + < nom de l'option = « iosPersistentFileLocation » value = « Library » / >< nom de préférence = « iosPersistentFileLocation » value = « Compatibilité » / > + + +Sans cette ligne, utilisera le fichier plugin `Compatibility` par défaut. Si une balise de préférence est présente et n'est pas une des valeurs suivantes, l'application ne démarrera pas. + +Si votre application a déjà été expédiée aux utilisateurs, en utilisant une ancienne (avant 1.0) version de ce plugin et dispose des fichiers stockés dans le système de fichiers persistant, alors vous devez définir la préférence au `Compatibility` . Changer l'emplacement de `Library` voudrait dire que les utilisateurs existants qui mettre à niveau leur application serait incapables d'accéder à leurs fichiers déjà enregistrés. + +Si votre application est nouvelle ou a jamais précédemment stocké les fichiers dans le système de fichiers persistant, puis la `Library` réglage est généralement recommandé. + +## Firefox OS Quirks + +L'API de système de fichier n'est pas nativement pris en charge par Firefox OS et est implémentée comme une cale d'épaisseur sur le dessus d'indexedDB. + + * Ne manque pas lors de la suppression des répertoires non vide + * Ne supporte pas les métadonnées pour les répertoires + * Méthodes `copyTo` et `moveTo` ne prennent pas en charge les répertoires + +Les chemins de données suivants sont pris en charge: * `applicationDirectory` -utilise `xhr` pour obtenir des fichiers les qui sont emballées avec l'app. * `dataDirectory` - Pour les fichiers de données persistantes de app spécifique. * `cacheDirectory` -Mise en cache de fichiers qui doivent survivre les redémarrages de l'application (les applications ne doivent pas compter sur le système d'exploitation pour supprimer les fichiers ici). + +## Bizarreries navigateur + +### Commune de bizarreries et de remarques + + * Chaque navigateur utilise son propre système de fichiers en bac à sable. IE et Firefox utilisent IndexedDB comme base. Tous les navigateurs utilisent oblique comme séparateur de répertoire dans un chemin d'accès. + * Entrées d'annuaire doivent être créées successivement. Par exemple, l'appel `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` échouera si dir1 n'existait pas. + * Le plugin demande utilisateur l'autorisation d'utiliser le stockage persistant lors du premier démarrage d'application. + * Plugin supporte `cdvfile://localhost` (ressources locales) seulement. C'est-à-dire les ressources externes ne sont pas supportés par l'intermédiaire de `cdvfile`. + * Le plugin ne suit pas les ["Restrictions de nommage des fichiers système API 8.3"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). + * BLOB et le fichier "`close` la fonction n'est pas pris en charge. + * `FileSaver` et `BlobBuilder` ne sont pas pris en charge par ce plugin et n'ont stubs. + * Le plugin ne supporte pas les `requestAllFileSystems`. Cette fonction est également absent dans les cahier des charges. + * Inscriptions dans l'annuaire ne seront pas supprimées si vous utilisez `create: true` drapeau pour le répertoire existant. + * Fichiers créés via le constructeur ne sont pas pris en charge. Vous devez plutôt utiliser entry.file méthode. + * Chaque navigateur utilise sa propre forme de références URL blob. + * `readAsDataURL` fonction est prise en charge, mais le mediatype en Chrome dépend de l'extension entrée, mediatype dans IE est toujours vide (qui est le même que le `texte-plaine` selon la spécification), le mediatype dans Firefox est toujours `application/octet-stream`. Par exemple, si le contenu est `abcdefg` puis Firefox renvoie `données : application / octet-stream ; base64, YWJjZGVmZw ==`, c'est à dire les retours `données:; base64, YWJjZGVmZw ==`, retours de Chrome `données : < mediatype selon l'extension de nom d'entrée > ; base64, YWJjZGVmZw ==`. + * `toInternalURL` retourne le chemin d'accès dans le formulaire `file:///persistent/path/to/entry` (Firefox, IE). Chrome retourne le chemin d'accès dans le formulaire `cdvfile://localhost/persistent/file`. + +### Bizarreries de chrome + + * Chrome filesystem n'est pas prête immédiatement après l'événement ready périphérique. Pour contourner le problème, vous pouvez vous abonner à l'événement `filePluginIsReady`. Exemple : + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +Vous pouvez utiliser la fonction `window.isFilePluginReadyRaised` pour vérifier si les événement était déjà déclenché. -quotas de window.requestFileSystem temporaire et permanent de système de fichiers ne sont pas limités en Chrome. -Pour augmenter le stockage persistant en Chrome, vous devez appeler la méthode `window.initPersistentFileSystem`. Quota de stockage persistant est 5 Mo par défaut. -Chrome nécessite `--permettre-fichier-accès-de-fichiers` exécuter l'argument au support API via le protocole `file:///`. -`Fichier` objet changera pas si vous utilisez le drapeau `{create:true}` lors du passage d'une `entrée` existante. -événements `annulables` propriété a la valeur true dans Chrome. Il s'agit à l'encontre de la [spécification](http://dev.w3.org/2009/dap/file-system/file-writer.html). -`toURL` renvoie à Chrome `système de fichiers :`-préfixe de chemin d'accès selon l'application hôte. Par exemple, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -résultat de la fonction `toURL` ne contient-elle pas de barre oblique dans le cas d'entrée d'annuaire. Chrome résout répertoires avec barre oblique-trainés URL correctement cependant. -`resolveLocalFileSystemURL` méthode nécessite l' entrant `url` préfixe de `système de fichiers`. Par exemple, le paramètre `d'url` pour `resolveLocalFileSystemURL` devrait être dans la forme `filesystem:file:///persistent/somefile.txt` par opposition à la forme `file:///persistent/somefile.txt` dans Android. -Déconseillée `toNativeURL` fonction n'est pas prise en charge et n'est pas une ébauche. -fonction de `setMetadata` n'est pas stipulée dans le devis et pas pris en charge. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de SYNTAX_ERR(code: 8) sur la demande d'un système de fichier inexistant. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de PATH_EXISTS_ERR(code: 12) à essayer de créer exclusivement un fichier ou un répertoire, qui existe déjà. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de NO_MODIFICATION_ALLOWED_ERR(code: 6) à essayer d'appeler removeRecursively sur le système de fichiers racine. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de NOT_FOUND_ERR(code: 1) en essayant de moveTo répertoire qui n'existe pas. + +### Base IndexedDB impl bizarreries (Firefox et IE) + + * `.` et `.` ne sont pas pris en charge. + * IE ne prend pas en charge les `file:///`-mode ; seul le mode hébergé est pris en charge (http://localhost:xxxx). + * Taille de système de fichiers de Firefox n'est pas limité, mais chaque extension de 50Mo demandera une autorisation de l'utilisateur. IE10 permet jusqu'à 10 Mo de combiné AppCache et IndexedDB utilisés dans la mise en Å“uvre du système de fichiers sans demander de confirmation, une fois que vous atteignez ce niveau, Qu'on vous demandera si vous souhaitez lui permettre d'être augmentée jusqu'à un maximum de 250 Mo par site. Si le paramètre de `taille` pour la fonction `requestFileSystem` n'affecte pas le système de fichiers dans Firefox et IE. + * fonction de `readAsBinaryString` n'est pas indiquée dans les spécifications et pas pris en charge dans Internet Explorer et n'a pas une ébauche. + * `file.type` est toujours null. + * Vous ne devez pas créer en utilisant le résultat du callback instance DirectoryEntry qui avait été supprimée. Sinon, vous obtiendrez une « entrée de pendaison ». + * Avant que vous pouvez lire un fichier qui a été écrit juste que vous devez obtenir une nouvelle instance de ce fichier. + * `setMetadata` fonction, qui n'est pas indiquée dans les spécifications supporte `modificationTime` changement de champ seulement. + * fonctions `copyTo` et `moveTo` ne supportent pas les répertoires. + * Répertoires métadonnées ne sont pas pris en charge. + * Les deux Entry.remove et directoryEntry.removeRecursively ne manquent pas lors de la suppression des répertoires non-vides - répertoires retirés sont nettoyés avec contenu au lieu de cela. + * fonctions `abort` et `truncate` ne sont pas supportées. + * événements de progression ne sont pas déclenchés. Par exemple, ce gestionnaire ne sera pas exécuté : + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## Notes de mise à niveau + +V1.0.0 de ce plugin, les structures `FileEntry` et `DirectoryEntry` ont changé, pour être plus conforme à la spécification publiée. + +Les versions précédentes de (pré-1.0.0) du plugin stockaient le dispositif-absolu--emplacement du fichier dans la propriété `fullPath` d'objets `d'entrée`. Ces chemins seraient présente généralement comme + + / var/mobile/Applications/< application UUID >/Documents/chemin/vers/fichier (iOS), /storage/emulated/0/path/to/file (Android) + + +Ces chemins ont été également retournés par la méthode de `toURL()` les objets `d'entrée`. + +Avec v1.0.0, l'attribut `fullPath` est le chemin d'accès au fichier, *par rapport à la racine du système de fichiers HTML*. Ainsi, les chemins d'accès ci-dessus seraient maintenant tous les deux être représentée par un objet `FileEntry` avec un `fullPath` de + + /path/to/file + + +Si votre application fonctionne avec le dispositif-absolu-chemins et que vous avez récupéré précédemment ces chemins d'accès par le biais de la propriété `fullPath` d'objets `d'entrée`, puis vous devez mettre à jour votre code afin d'utiliser `entry.toURL()` à la place. + +Pour vers l'arrière la compatibilité, la méthode `resolveLocalFileSystemURL()` sera un chemin absolu de l'unité et retourne un objet `Entry` correspondant à elle, tant que ce fichier existe au sein des systèmes de fichiers les `TEMPORARY` ou `PERSISTENT`. + +Cela a été particulièrement un problème avec le plugin de transfert de fichiers, qui autrefois périphérique-absolu-chemins (et peut encore accepter). Il a été mis à jour pour fonctionner correctement avec le système de fichiers URL, afin de remplacer `entry.fullPath` par `entry.toURL()` devrait résoudre tout problème obtenir ce plugin pour travailler avec des fichiers sur le périphérique. + +Dans v1.1.0 la valeur de retour de `toURL()` a été changée (voir \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) pour renvoyer une URL absolue "file://". dans la mesure du possible. Pour assurer un ' cdvfile:'-URL, vous pouvez utiliser `toInternalURL()` maintenant. Cette méthode retourne maintenant filesystem URL du formulaire + + cdvfile://localhost/persistent/path/to/file + + +qui peut servir à identifier de manière unique le fichier. + +## Liste des Codes d'erreur et leur signification + +Lorsqu'une erreur est levée, l'un des codes suivants sera utilisé. + +| 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` | + +## Configuration du Plugin (facultatif) + +L'ensemble des systèmes de fichiers disponibles peut être configurée par plate-forme. Les iOS et Android reconnaissent une balise dans le `fichier config.xml` qui nomme les systèmes de fichiers à installer. Par défaut, toutes les racines du système de fichiers sont activées. + + + + + +### Android + + * `files` : répertoire de stockage de fichier interne de l'application + * `files-external` : répertoire de l'application de stockage de fichier externe + * `sdcard` : le répertoire de stockage global fichier externe (c'est la racine de la carte SD, s'il est installé). Vous devez avoir la permission de `android.permission.WRITE_EXTERNAL_STORAGE` de l'utiliser. + * `cache` : répertoire de cache interne de l'application + * `cache-external` : répertoire de cache externe de l'application + * `root` : le système de fichiers de tout dispositif + +Android prend également en charge un système de fichiers spécial nommé « documents », qui représente un sous-répertoire « / Documents / » dans le système de fichiers « files ». + +### iOS + + * `library` : répertoire de bibliothèque de l'application + * `documents` : répertoire de Documents de l'application + * `cache` : répertoire de Cache de l'application + * `bundle` : bundle de l'application ; l'emplacement de l'application elle-même sur disque (lecture seule) + * `root` : le système de fichiers de tout dispositif + +Par défaut, vous peuvent synchroniser les répertoires de la bibliothèque et les documents à iCloud. Vous pouvez également demander des deux systèmes de fichiers supplémentaires, `library-nosync` et `documents-nosync`, qui représentent un répertoire spécial non synchronisées dans le `/Library` ou système de fichiers `/ Documents`. \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/fr/index.md b/plugins/cordova-plugin-file/doc/fr/index.md new file mode 100644 index 0000000..7235522 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/fr/index.md @@ -0,0 +1,331 @@ + + +# cordova-plugin-file + +Ce plugin implémente une API de fichier permettant l'accès en lecture/écriture aux fichiers qui résident sur le périphérique. + +Ce plugin est basé sur plusieurs spécifications, y compris : l'API de fichier HTML5 + +Les répertoires (aujourd'hui disparue) et le système des extensions plus récentes : bien que la plupart du code du plugin a été écrit quand une technique antérieure était en vigueur : + +Il met également en Å“uvre la spécification FileWriter : + +Pour son utilisation, veuillez vous reporter au HTML5 Rocks' excellent [article de système de fichiers.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +Pour un aperçu des autres options de stockage, consultez [guide d'entreposage de Cordova][2]. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +Ce plugin définit global `cordova.file` objet. + +Bien que dans la portée globale, il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener (« deviceready », onDeviceReady, false) ; + function onDeviceReady() {console.log(cordova.file);} + + +## Installation + + Cordova plugin ajouter cordova-plugin-file + + +## Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 et 8 * +* Windows 8 * +* Navigateur + +* *Ces plates-formes ne supportent pas `FileReader.readAsArrayBuffer` ni `FileWriter.write(blob)` .* + +## Emplacement de stockage des fichiers + +À partir de v1.2.0, URL vers des répertoires de système de fichiers importants est fournis. Chaque URL est dans la forme *file:///path/to/spot/*et peut être converti en un `DirectoryEntry` à l'aide`window.resolveLocalFileSystemURL()`. + +* `cordova.file.applicationDirectory`-Lecture seule répertoire où l'application est installée. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.applicationStorageDirectory`-Répertoire racine du bac à sable de l'application ; cet endroit est en lecture seule sur iOS (mais les sous-répertoires spécifiques [comme `/Documents` ] sont en lecture / écriture). Toutes les données qu'il contient est privé de l'application. ( *iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.dataDirectory`-Stockage des données persistants et privés au sein de bac à sable de l'application à l'aide de la mémoire interne (sur Android, si vous avez besoin d'utiliser une mémoire externe, utilisez `.externalDataDirectory` ). Sur iOS, ce répertoire n'est pas synchronisé avec iCloud (utiliser `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.cacheDirectory`-Répertoire pour les fichiers de données en mémoire cache ou les fichiers que votre application peut recréer facilement. L'OS peut supprimer ces fichiers lorsque l'appareil faiblit sur stockage, néanmoins, les applications ne doivent pas compter sur l'OS pour supprimer les fichiers ici. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.externalApplicationStorageDirectory`-Espace l'application sur le stockage externe. (*Android*) + +* `cordova.file.externalDataDirectory`-Où placer les fichiers de données d'application spécifiques sur le stockage externe. (*Android*) + +* `cordova.file.externalCacheDirectory`-Cache de l'application sur le stockage externe. (*Android*) + +* `cordova.file.externalRootDirectory`-Racine de stockage externe (carte SD). (*Android*, *BlackBerry 10*) + +* `cordova.file.tempDirectory`-Répertoire temp que l'OS peut effacer à volonté. Ne comptez pas sur l'OS pour effacer ce répertoire ; votre application doit toujours supprimer les fichiers selon le cas. (*iOS*) + +* `cordova.file.syncedDataDirectory`-Contient des fichiers d'app spécifique qui doivent se synchroniser (par exemple à iCloud). (*iOS*) + +* `cordova.file.documentsDirectory`-Fichiers privés à l'app, mais qui sont significatives pour l'autre application (par exemple les fichiers Office). (*iOS*) + +* `cordova.file.sharedDirectory`-Fichiers disponibles globalement à toutes les applications (*BlackBerry 10*) + +## Structures de système de fichiers + +Bien que techniquement un détail d'implémentation, il peut être très utile de savoir comment les `cordova.file.*` carte de propriétés à des chemins d'accès physiques sur un périphérique réel. + +### iOS agencement de système de fichier + +| Chemin de l'unité | `Cordova.file.*` | `iosExtraFileSystems` | r/w ? | persistants ? | OS efface | Sync | privé | +|:-------------------------------------------- |:--------------------------- |:--------------------- |:-----:|:-------------:|:-----------:|:-----:|:-----:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Oui | +|    `appname.app/` | applicationDirectory | Bundle | r | N/A | N/A | N/A | Oui | +|       `www/` | - | - | r | N/A | N/A | N/A | Oui | +|    `Documents/` | documentsDirectory | documents | r/w | Oui | Non | Oui | Oui | +|       `NoCloud/` | - | documents-nosync | r/w | Oui | Non | Non | Oui | +|    `Library` | - | Bibliothèque | r/w | Oui | Non | Oui ? | Oui | +|       `NoCloud/` | dataDirectory | Bibliothèque-nosync | r/w | Oui | Non | Non | Oui | +|       `Cloud/` | syncedDataDirectory | - | r/w | Oui | Non | Oui | Oui | +|       `Caches/` | cacheDirectory | cache | r/w | Oui * | Oui * * *| | Non | Oui | +|    `tmp/` | tempDirectory | - | r/w | Ne * * | Oui * * *| | Non | Oui | + +* Fichiers persistent à travers les redémarrages de l'application et mises à niveau, mais ce répertoire peut être effacé à chaque fois que les désirs de l'OS. Votre application doit être en mesure de recréer tout contenu qui pourrait être supprimé. + +* * Fichiers peuvent persister redémarrages de l'application, mais ne vous fiez pas ce comportement. Les fichiers ne sont pas garantis à persister dans l'ensemble de mises à jour. Votre application doit supprimer les fichiers de ce répertoire lorsqu'elle s'applique, comme le système d'exploitation ne garantit pas quand (ou même si) ces fichiers sont supprimés. + +* * *| L'OS peut effacer le contenu de ce répertoire chaque fois qu'il se sent il est nécessaire, mais ne comptez pas là-dessus. Vous devez supprimer ce répertoire comme approprié pour votre application. + +### Agencement de système de fichiers Android + +| Chemin de l'unité | `Cordova.file.*` | `AndroidExtraFileSystems` | r/w ? | persistants ? | OS efface | privé | +|:----------------------------------- |:----------------------------------- |:------------------------- |:-----:|:-------------:|:---------:|:-----:| +| `file:///android_asset/` | applicationDirectory | | r | N/A | N/A | Oui | +| `/ données/data/app < id > /` | applicationStorageDirectory | - | r/w | N/A | N/A | Oui | +|    `cache` | cacheDirectory | cache | r/w | Oui | Oui * | Oui | +|    `files` | dataDirectory | fichiers | r/w | Oui | Non | Oui | +|       `Documents` | | documents | r/w | Oui | Non | Oui | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Oui | Non | Non | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Oui | Non | Non | +|       `cache` | externalCacheDirectry | cache-externe | r/w | Oui | Ne * * | Non | +|       `files` | externalDataDirectory | fichiers externes | r/w | Oui | Non | Non | + +* Le système d'exploitation peut effacer périodiquement ce répertoire, mais ne vous fiez pas ce comportement. Effacer le contenu de ce répertoire comme approprié pour votre application. Un utilisateur doit purger le cache manuellement, le contenu de ce répertoire est supprimé. + +* * The OS vous n'effacez pas ce répertoire automatiquement ; vous êtes chargé de gérer le contenu vous-même. L'utilisateur devrait purger le cache manuellement, le contenu du répertoire est supprimé. + +**Remarque**: si le stockage externe ne peut pas être monté, les `cordova.file.external*` sont des propriétés`null`. + +### Configuration du système blackBerry 10 fichier + +| Chemin de l'unité | `Cordova.file.*` | r/w ? | persistants ? | OS efface | privé | +|:--------------------------------------------------- |:--------------------------- |:-----:|:-------------:|:---------:|:-----:| +| `file:///Accounts/1000/AppData/ < id app > /` | applicationStorageDirectory | r | N/A | N/A | Oui | +|    `app/native` | applicationDirectory | r | N/A | N/A | Oui | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | Non | Oui | Oui | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Oui | Non | Oui | +| `file:///Accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Oui | Non | Non | +| `file:///Accounts/1000/Shared` | sharedDirectory | r/w | Oui | Non | Non | + +*Remarque*: lorsque l'application est déployée dans le périmètre de travail, tous les chemins sont par rapport à /accounts/1000-enterprise. + +## Quirks Android + +### Emplacement de stockage persistant Android + +Il y a plusieurs emplacements valides pour stocker des fichiers persistants sur un appareil Android. Voir [cette page][3] pour une analyse approfondie des diverses possibilités. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +Les versions précédentes du plugin choisirait l'emplacement des fichiers temporaires et persistantes au démarrage, basé sur la question de savoir si le dispositif réclamé que la carte SD (ou une partition de stockage équivalent) a été montée. Si la carte SD a été montée, ou si une partition de stockage interne importante était disponible (comme sur les appareils Nexus,) puis les fichiers persistants seraient stockés dans la racine de cet espace. Cela signifie que toutes les apps de Cordova pouvaient voir tous les fichiers disponibles sur la carte. + +Si la carte SD n'était pas disponible, les versions précédentes pourraient stocker des données sous `/data/data/` , qui isole des apps de l'autre, mais peut encore cause données à partager entre les utilisateurs. + +Il est maintenant possible de choisir de stocker les fichiers dans l'emplacement de stockage de fichier interne, ou en utilisant la logique précédente, avec une préférence au sein de votre application `config.xml` fichier. Pour ce faire, ajoutez l'un de ces deux lignes de `config.xml` : + + < nom de l'option = « AndroidPersistentFileLocation » value = « Internal » / >< nom de préférence = « AndroidPersistentFileLocation » value = « Compatibilité » / > + + +Sans cette ligne, utilisera le fichier plugin `Compatibility` par défaut. Si une balise de préférence est présente et n'est pas une des valeurs suivantes, l'application ne démarrera pas. + +Si votre application a déjà été expédiée aux utilisateurs, en utilisant une ancienne (avant 1.0) version de ce plugin et dispose des fichiers stockés dans le système de fichiers persistant, alors vous devez définir la préférence au `Compatibility` . Commutation de l'emplacement « Internal » signifierait que les utilisateurs existants qui mettre à niveau leur application peuvent être impossible d'accéder à leurs fichiers déjà enregistrés, selon leur appareil. + +Si votre application est nouvelle ou a jamais précédemment stocké les fichiers dans le système de fichiers persistant, puis la `Internal` réglage est généralement recommandé. + +## iOS Quirks + +* `cordova.file.applicationStorageDirectory`est en lecture seule ; tentative de stocker des fichiers dans le répertoire racine échoue. Utilisez l'une de l'autre `cordova.file.*` les propriétés définies pour iOS (seulement `applicationDirectory` et `applicationStorageDirectory` sont en lecture seule). +* `FileReader.readAsText(blob, encoding)` + * Le `encoding` paramètre n'est pas pris en charge, et le codage UTF-8 est toujours en vigueur. + +### emplacement de stockage persistant d'iOS + +Il y a deux emplacements valides pour stocker des fichiers persistants sur un appareil iOS : le répertoire de Documents et le répertoire de la bibliothèque. Les versions précédentes du plugin stockaient ne jamais fichiers persistants dans le répertoire de Documents. Cela a eu l'effet secondaire de rendre tous les fichiers de l'application visible dans iTunes, qui était souvent inattendus, en particulier pour les applications qui traitent beaucoup de petits fichiers, plutôt que de produire des documents complets destinés à l'exportation, qui est l'objectif visé par le répertoire. + +Il est maintenant possible de choisir de stocker les fichiers dans le répertoire de bibliothèque, avec une préférence au sein de votre application ou de documents `config.xml` fichier. Pour ce faire, ajoutez l'un de ces deux lignes de `config.xml` : + + < nom de l'option = « iosPersistentFileLocation » value = « Library » / >< nom de préférence = « iosPersistentFileLocation » value = « Compatibilité » / > + + +Sans cette ligne, utilisera le fichier plugin `Compatibility` par défaut. Si une balise de préférence est présente et n'est pas une des valeurs suivantes, l'application ne démarrera pas. + +Si votre application a déjà été expédiée aux utilisateurs, en utilisant une ancienne (avant 1.0) version de ce plugin et dispose des fichiers stockés dans le système de fichiers persistant, alors vous devez définir la préférence au `Compatibility` . Changer l'emplacement de `Library` voudrait dire que les utilisateurs existants qui mettre à niveau leur application serait incapables d'accéder à leurs fichiers déjà enregistrés. + +Si votre application est nouvelle ou a jamais précédemment stocké les fichiers dans le système de fichiers persistant, puis la `Library` réglage est généralement recommandé. + +## Firefox OS Quirks + +L'API de système de fichier n'est pas nativement pris en charge par Firefox OS et est implémentée comme une cale d'épaisseur sur le dessus d'indexedDB. + +* Ne manque pas lors de la suppression des répertoires non vide +* Ne supporte pas les métadonnées pour les répertoires +* Méthodes `copyTo` et `moveTo` ne prennent pas en charge les répertoires + +Les chemins de données suivants sont pris en charge: * `applicationDirectory` -utilise `xhr` pour obtenir des fichiers les qui sont emballées avec l'app. * `dataDirectory` - Pour les fichiers de données persistantes de app spécifique. * `cacheDirectory` -Mise en cache de fichiers qui doivent survivre les redémarrages de l'application (les applications ne doivent pas compter sur le système d'exploitation pour supprimer les fichiers ici). + +## Bizarreries navigateur + +### Commune de bizarreries et de remarques + +* Chaque navigateur utilise son propre système de fichiers en bac à sable. IE et Firefox utilisent IndexedDB comme base. Tous les navigateurs utilisent oblique comme séparateur de répertoire dans un chemin d'accès. +* Entrées d'annuaire doivent être créées successivement. Par exemple, l'appel `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` échouera si dir1 n'existait pas. +* Le plugin demande utilisateur l'autorisation d'utiliser le stockage persistant lors du premier démarrage d'application. +* Plugin supporte `cdvfile://localhost` (ressources locales) seulement. C'est-à-dire les ressources externes ne sont pas supportés par l'intermédiaire de `cdvfile`. +* Le plugin ne suit pas les ["Restrictions de nommage des fichiers système API 8.3"][4]. +* BLOB et le fichier "`close` la fonction n'est pas pris en charge. +* `FileSaver` et `BlobBuilder` ne sont pas pris en charge par ce plugin et n'ont stubs. +* Le plugin ne supporte pas les `requestAllFileSystems`. Cette fonction est également absent dans les cahier des charges. +* Inscriptions dans l'annuaire ne seront pas supprimées si vous utilisez `create: true` drapeau pour le répertoire existant. +* Fichiers créés via le constructeur ne sont pas pris en charge. Vous devez plutôt utiliser entry.file méthode. +* Chaque navigateur utilise sa propre forme de références URL blob. +* `readAsDataURL` fonction est prise en charge, mais le mediatype en Chrome dépend de l'extension entrée, mediatype dans IE est toujours vide (qui est le même que le `texte-plaine` selon la spécification), le mediatype dans Firefox est toujours `application/octet-stream`. Par exemple, si le contenu est `abcdefg` puis Firefox renvoie `données : application / octet-stream ; base64, YWJjZGVmZw ==`, c'est à dire les retours `données:; base64, YWJjZGVmZw ==`, retours de Chrome `données : < mediatype selon l'extension de nom d'entrée > ; base64, YWJjZGVmZw ==`. +* `toInternalURL` retourne le chemin d'accès dans le formulaire `file:///persistent/path/to/entry` (Firefox, IE). Chrome retourne le chemin d'accès dans le formulaire `cdvfile://localhost/persistent/file`. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### Bizarreries de chrome + +* Chrome filesystem n'est pas prête immédiatement après l'événement ready périphérique. Pour contourner le problème, vous pouvez vous abonner à l'événement `filePluginIsReady`. Exemple : + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +Vous pouvez utiliser la fonction `window.isFilePluginReadyRaised` pour vérifier si les événement était déjà déclenché. -quotas de window.requestFileSystem temporaire et permanent de système de fichiers ne sont pas limités en Chrome. -Pour augmenter le stockage persistant en Chrome, vous devez appeler la méthode `window.initPersistentFileSystem`. Quota de stockage persistant est 5 Mo par défaut. -Chrome nécessite `--permettre-fichier-accès-de-fichiers` exécuter l'argument au support API via le protocole `file:///`. -`Fichier` objet changera pas si vous utilisez le drapeau `{create:true}` lors du passage d'une `entrée` existante. -événements `annulables` propriété a la valeur true dans Chrome. Il s'agit à l'encontre de la [spécification][5]. -`toURL` renvoie à Chrome `système de fichiers :`-préfixe de chemin d'accès selon l'application hôte. Par exemple, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -résultat de la fonction `toURL` ne contient-elle pas de barre oblique dans le cas d'entrée d'annuaire. Chrome résout répertoires avec barre oblique-trainés URL correctement cependant. -`resolveLocalFileSystemURL` méthode nécessite l' entrant `url` préfixe de `système de fichiers`. Par exemple, le paramètre `d'url` pour `resolveLocalFileSystemURL` devrait être dans la forme `filesystem:file:///persistent/somefile.txt` par opposition à la forme `file:///persistent/somefile.txt` dans Android. -Déconseillée `toNativeURL` fonction n'est pas prise en charge et n'est pas une ébauche. -fonction de `setMetadata` n'est pas stipulée dans le devis et pas pris en charge. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de SYNTAX_ERR(code: 8) sur la demande d'un système de fichier inexistant. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de PATH_EXISTS_ERR(code: 12) à essayer de créer exclusivement un fichier ou un répertoire, qui existe déjà. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de NO_MODIFICATION_ALLOWED_ERR(code: 6) à essayer d'appeler removeRecursively sur le système de fichiers racine. -INVALID_MODIFICATION_ERR (code: 9) est levée au lieu de NOT_FOUND_ERR(code: 1) en essayant de moveTo répertoire qui n'existe pas. + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### Base IndexedDB impl bizarreries (Firefox et IE) + +* `.` et `.` ne sont pas pris en charge. +* IE ne prend pas en charge les `file:///`-mode ; seul le mode hébergé est pris en charge (http://localhost:xxxx). +* Taille de système de fichiers de Firefox n'est pas limité, mais chaque extension de 50Mo demandera une autorisation de l'utilisateur. IE10 permet jusqu'à 10 Mo de combiné AppCache et IndexedDB utilisés dans la mise en Å“uvre du système de fichiers sans demander de confirmation, une fois que vous atteignez ce niveau, Qu'on vous demandera si vous souhaitez lui permettre d'être augmentée jusqu'à un maximum de 250 Mo par site. Si le paramètre de `taille` pour la fonction `requestFileSystem` n'affecte pas le système de fichiers dans Firefox et IE. +* fonction de `readAsBinaryString` n'est pas indiquée dans les spécifications et pas pris en charge dans Internet Explorer et n'a pas une ébauche. +* `file.type` est toujours null. +* Vous ne devez pas créer en utilisant le résultat du callback instance DirectoryEntry qui avait été supprimée. Sinon, vous obtiendrez une « entrée de pendaison ». +* Avant que vous pouvez lire un fichier qui a été écrit juste que vous devez obtenir une nouvelle instance de ce fichier. +* `setMetadata` fonction, qui n'est pas indiquée dans les spécifications supporte `modificationTime` changement de champ seulement. +* fonctions `copyTo` et `moveTo` ne supportent pas les répertoires. +* Répertoires métadonnées ne sont pas pris en charge. +* Les deux Entry.remove et directoryEntry.removeRecursively ne manquent pas lors de la suppression des répertoires non-vides - répertoires retirés sont nettoyés avec contenu au lieu de cela. +* fonctions `abort` et `truncate` ne sont pas supportées. +* événements de progression ne sont pas déclenchés. Par exemple, ce gestionnaire ne sera pas exécuté : + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## Notes de mise à niveau + +V1.0.0 de ce plugin, les structures `FileEntry` et `DirectoryEntry` ont changé, pour être plus conforme à la spécification publiée. + +Les versions précédentes de (pré-1.0.0) du plugin stockaient le dispositif-absolu--emplacement du fichier dans la propriété `fullPath` d'objets `d'entrée`. Ces chemins seraient présente généralement comme + + / var/mobile/Applications/< application UUID >/Documents/chemin/vers/fichier (iOS), /storage/emulated/0/path/to/file (Android) + + +Ces chemins ont été également retournés par la méthode de `toURL()` les objets `d'entrée`. + +Avec v1.0.0, l'attribut `fullPath` est le chemin d'accès au fichier, *par rapport à la racine du système de fichiers HTML*. Ainsi, les chemins d'accès ci-dessus seraient maintenant tous les deux être représentée par un objet `FileEntry` avec un `fullPath` de + + /path/to/file + + +Si votre application fonctionne avec le dispositif-absolu-chemins et que vous avez récupéré précédemment ces chemins d'accès par le biais de la propriété `fullPath` d'objets `d'entrée`, puis vous devez mettre à jour votre code afin d'utiliser `entry.toURL()` à la place. + +Pour vers l'arrière la compatibilité, la méthode `resolveLocalFileSystemURL()` sera un chemin absolu de l'unité et retourne un objet `Entry` correspondant à elle, tant que ce fichier existe au sein des systèmes de fichiers les `TEMPORARY` ou `PERSISTENT`. + +Cela a été particulièrement un problème avec le plugin de transfert de fichiers, qui autrefois périphérique-absolu-chemins (et peut encore accepter). Il a été mis à jour pour fonctionner correctement avec le système de fichiers URL, afin de remplacer `entry.fullPath` par `entry.toURL()` devrait résoudre tout problème obtenir ce plugin pour travailler avec des fichiers sur le périphérique. + +Dans v1.1.0 la valeur de retour de `toURL()` a été changée (voir \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) pour renvoyer une URL absolue "file://". dans la mesure du possible. Pour assurer un ' cdvfile:'-URL, vous pouvez utiliser `toInternalURL()` maintenant. Cette méthode retourne maintenant filesystem URL du formulaire + + cdvfile://localhost/persistent/path/to/file + + +qui peut servir à identifier de manière unique le fichier. + +## Liste des Codes d'erreur et leur signification + +Lorsqu'une erreur est levée, l'un des codes suivants sera utilisé. + +| 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` | + +## Configuration du Plugin (facultatif) + +L'ensemble des systèmes de fichiers disponibles peut être configurée par plate-forme. Les iOS et Android reconnaissent une balise dans le `fichier config.xml` qui nomme les systèmes de fichiers à installer. Par défaut, toutes les racines du système de fichiers sont activées. + + + + + +### Android + +* `files` : répertoire de stockage de fichier interne de l'application +* `files-external` : répertoire de l'application de stockage de fichier externe +* `sdcard` : le répertoire de stockage global fichier externe (c'est la racine de la carte SD, s'il est installé). Vous devez avoir la permission de `android.permission.WRITE_EXTERNAL_STORAGE` de l'utiliser. +* `cache` : répertoire de cache interne de l'application +* `cache-external` : répertoire de cache externe de l'application +* `root` : le système de fichiers de tout dispositif + +Android prend également en charge un système de fichiers spécial nommé « documents », qui représente un sous-répertoire « / Documents / » dans le système de fichiers « files ». + +### iOS + +* `library` : répertoire de bibliothèque de l'application +* `documents` : répertoire de Documents de l'application +* `cache` : répertoire de Cache de l'application +* `bundle` : bundle de l'application ; l'emplacement de l'application elle-même sur disque (lecture seule) +* `root` : le système de fichiers de tout dispositif + +Par défaut, vous peuvent synchroniser les répertoires de la bibliothèque et les documents à iCloud. Vous pouvez également demander des deux systèmes de fichiers supplémentaires, `library-nosync` et `documents-nosync`, qui représentent un répertoire spécial non synchronisées dans le `/Library` ou système de fichiers `/ Documents`. diff --git a/plugins/cordova-plugin-file/doc/fr/plugins.md b/plugins/cordova-plugin-file/doc/fr/plugins.md new file mode 100644 index 0000000..b04e17f --- /dev/null +++ b/plugins/cordova-plugin-file/doc/fr/plugins.md @@ -0,0 +1,101 @@ + + +# Notes pour les développeurs de plugins + +Ces notes sont principalement destinés à des développeurs Android et iOS qui veulent écrire des plugins qui interface avec le système de fichiers en utilisant le fichier plugin. + +## Travailler avec Cordova fichier système URL + +Depuis la version 1.0.0, ce plugin utilise des URL avec un `cdvfile` guichet pour toutes les communications sur le pont, plutôt que d'exposer des chemins de système de fichiers de périphérique brut à JavaScript. + +Du côté du JavaScript, cela signifie que les objets FileEntry et DirectoryEntry ont un attribut fullPath qui est relatif à la racine du système de fichiers HTML. Si votre plugin JavaScript API accepte un objet FileEntry ou DirectoryEntry, vous devez appeler `.toURL()` sur cet objet avant de le passer sur le pont en code natif. + +### Conversion de cdvfile: / / URL aux chemins d'accès fileystem + +Plugins qui ont besoin d'écrire dans le système de fichiers pouvez convertir un fichier reçu système URL vers un emplacement de système de fichiers réels. Il y a plusieurs façons de le faire, selon la plate-forme native. + +Il est important de rappeler que pas tous les `cdvfile://` URL sont cartographiables à des fichiers sur le périphérique. Certaines URL peut faire référence aux actifs sur les périphériques qui ne sont pas représentés par des fichiers, ou peuvent même faire référence aux ressources distantes. En raison de ces possibilités, plugins devraient toujours tester si ils obtiennent un résultat significatif retour lorsque vous essayez de convertir les URL aux chemins d'accès. + +#### Androïde + +Sur Android, la méthode la plus simple pour convertir un `cdvfile://` URL vers un chemin d'accès de système de fichiers est d'utiliser `org.apache.cordova.CordovaResourceApi` . `CordovaResourceApi`possède plusieurs méthodes qui peuvent gérer `cdvfile://` URL : + + webView est membre de la Plugin classe CordovaResourceApi resourceApi = webView.getResourceApi() ; + + Obtenir une URL file:/// représentant ce fichier sur le périphérique, / / ou le même URL inchangée si elle ne peut pas être mappée à un fichier Uri fileURL = resourceApi.remapUri(Uri.parse(cdvfileURL)) ; + + +Il est également possible d'utiliser le fichier plugin directement : + + Import org.apache.cordova.file.FileUtils ; + Import org.apache.cordova.file.FileSystem ; + java.net.MalformedURLException d'importation ; + + Téléchargez le fichier plugin depuis le gestionnaire de plugin FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File") ; + + En donnant une URL, obtenir un chemin d'accès pour essayer {String path = filePlugin.filesystemPathForURL(cdvfileURL);} catch (MalformedURLException e) {/ / l'url du système de fichiers n'a pas été reconnu} + + +Pour convertir un chemin d'accès à un `cdvfile://` URL : + + Import org.apache.cordova.file.LocalFilesystemURL ; + + Obtenir un objet LocalFilesystemURL pour un chemin de périphérique, / / ou null si elle ne peut être représentée sous forme d'URL cdvfile. + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path) ; + Obtenir la chaîne représentant l'URL objet String cdvfileURL = url.toString() ; + + +Si votre plugin crée un fichier et que vous souhaitez renvoyer un objet FileEntry pour cela, utilisez le fichier plugin : + + Retourne une structure JSON approprié pour revenir en JavaScript, / / ou null si ce fichier n'est pas représentable sous forme d'URL cdvfile. + JSONObject entrée = filePlugin.getEntryForFile(file) ; + + +#### iOS + +Cordova sur iOS n'utilise pas le même `CordovaResourceApi` concept d'Android. Sur iOS, vous devez utiliser le fichier plugin pour convertir entre les URL et les chemins d'accès de système de fichiers. + + Obtenir un objet URL CDVFilesystem partir d'une chaîne d'URL CDVFilesystemURL * url = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL] ; + Obtenir un chemin d'accès de l'objet URL, ou zéro si elle ne peut pas être mappée à un chemin de fichier NSString * = [filePlugin filesystemPathForURL:url] ; + + + Obtenir un objet CDVFilesystem URL pour un chemin de périphérique, ou / / zéro si elle ne peut être représentée sous forme d'URL cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path] ; + Obtenir la représentation de chaîne de l'objet NSString * cdvfileURL URL = [url absoluteString] ; + + +Si votre plugin crée un fichier et que vous souhaitez renvoyer un objet FileEntry pour cela, utilisez le fichier plugin : + + Obtenir un objet CDVFilesystem URL pour un chemin de périphérique, ou / / zéro si elle ne peut être représentée sous forme d'URL cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path] ; + Obtenir une structure pour revenir à JavaScript NSDictionary * entrée = [filePlugin makeEntryForLocalURL:url] + + +#### JavaScript + +En JavaScript, pour obtenir un `cdvfile://` URL d'un objet FileEntry ou DirectoryEntry, il suffit d'appeler `.toURL()` à ce sujet : + + var cdvfileURL = entry.toURL() ; + + +Dans gestionnaires de plugin de réponse, pour convertir une structure FileEntry retournée vers un objet réel de l'entrée, votre code de gestionnaire doit importer le fichier plugin et créer un nouvel objet : + + créer l'entrée de var d'objet entrée appropriée ; + Si (entryStruct.isDirectory) {entrée = new DirectoryEntry (entryStruct.name, entryStruct.fullPath, nouveau FileSystem(entryStruct.filesystemName));} else {entrée = nouvelle FileEntry (entryStruct.name, entryStruct.fullPath, nouvelle FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/it/README.md b/plugins/cordova-plugin-file/doc/it/README.md new file mode 100644 index 0000000..f8e302f --- /dev/null +++ b/plugins/cordova-plugin-file/doc/it/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +Questo plugin implementa un API File permettendo l'accesso di lettura/scrittura ai file che risiedono sul dispositivo. + +Questo plugin si basa su diverse specifiche, tra cui: The HTML5 File API + +Le directory (ormai defunta) e il sistema delle estensioni più recenti: anche se la maggior parte del codice plugin è stato scritto quando una spec precedenti era corrente: + +Implementa inoltre FileWriter spec: + +Per l'utilizzo, fare riferimento a HTML5 Rocks' eccellente [articolo FileSystem.](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +Per una panoramica delle altre opzioni di archiviazione, consultare [Guida di archiviazione di Cordova](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html). + +Questo plugin definisce oggetto global `cordova.file`. + +Anche se in ambito globale, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Installazione + + cordova plugin add cordova-plugin-file + + +## Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 e 8 * + * Windows 8 * + * Windows* + * Browser + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## Dove memorizzare i file + +A partire dalla v 1.2.0, vengono forniti gli URL per le directory importanti file di sistema. Ogni URL è nella forma *file:///path/to/spot/* e può essere convertito in un `DirectoryEntry` utilizzando `window.resolveLocalFileSystemURL()`. + + * `cordova.file.applicationDirectory`-Sola lettura directory dove è installato l'applicazione. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.applicationStorageDirectory`-Directory radice di sandbox dell'applicazione; su iOS questa posizione è in sola lettura (ma sottodirectory specifiche [come `/Documents` ] sono di sola lettura). Tutti i dati contenuti all'interno è privato all'app. ( *iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.dataDirectory`-Archiviazione dati persistente e privati nella sandbox dell'applicazione utilizzando la memoria interna (su Android, se è necessario utilizzare la memoria esterna, utilizzare `.externalDataDirectory` ). IOS, questa directory non è sincronizzata con iCloud (utilizzare `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.cacheDirectory`-Directory per i file memorizzati nella cache di dati o qualsiasi file che app possibile ricreare facilmente. L'OS può eliminare questi file quando il dispositivo viene eseguito basso sull'archiviazione, tuttavia, apps non deve basarsi sul sistema operativo per cancellare i file qui. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.externalApplicationStorageDirectory`-Spazio applicazione su storage esterno. (*Android*) + + * `cordova.file.externalDataDirectory`-Dove mettere i file di dati specifico app su storage esterno. (*Android*) + + * `cordova.file.externalCacheDirectory`-Cache applicazione su storage esterno. (*Android*) + + * `cordova.file.externalRootDirectory`-Radice di archiviazione esterna (scheda SD). (*Android*, *BlackBerry, 10*) + + * `cordova.file.tempDirectory`-Temp directory che l'OS è possibile cancellare a volontà. Non fare affidamento sul sistema operativo per cancellare questa directory; l'app deve sempre rimuovere file come applicabile. (*iOS*) + + * `cordova.file.syncedDataDirectory`-Contiene i file app specifiche che devono essere sincronizzati (per esempio a iCloud). (*iOS*) + + * `cordova.file.documentsDirectory`-I file privati per le app, ma che sono significativi per altre applicazioni (ad esempio i file di Office). (*iOS*) + + * `cordova.file.sharedDirectory`-File disponibili globalmente a tutte le applicazioni (*BlackBerry 10*) + +## Layout dei file di sistema + +Anche se tecnicamente un dettaglio di implementazione, può essere molto utile per conoscere come le proprietà `cordova.file.*` mappa di percorsi fisici su un dispositivo reale. + +### iOS File sistema Layout + +| Percorso dispositivo | `Cordova.file.*` | `iosExtraFileSystems` | r/w? | persistente? | OS cancella | sincronizzazione | privato | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:----:|:------------:|:------------:|:----------------:|:-------:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Sì | +|    `appname.app/` | applicationDirectory | bundle | r | N/A | N/A | N/A | Sì | +|       `www/` | - | - | r | N/A | N/A | N/A | Sì | +|    `Documents/` | documentsDirectory | documenti | r/w | Sì | No | Sì | Sì | +|       `NoCloud/` | - | nosync-documenti | r/w | Sì | No | No | Sì | +|    `Library` | - | libreria | r/w | Sì | No | Sì? | Sì | +|       `NoCloud/` | dataDirectory | nosync-libreria | r/w | Sì | No | No | Sì | +|       `Cloud/` | syncedDataDirectory | - | r/w | Sì | No | Sì | Sì | +|       `Caches/` | cacheDirectory | cache | r/w | Sì * | Sì**\* | No | Sì | +|    `tmp/` | tempDirectory | - | r/w | No** | Sì**\* | No | Sì | + +\ * File persistono tra riavvii app e aggiornamenti, ma questa directory può essere cancellata ogni volta che il sistema operativo desideri. L'app dovrebbe essere in grado di ricreare qualsiasi contenuto che potrebbe essere eliminato. + +** File possono persistere riavvii del app, ma non fare affidamento su questo comportamento. I file non sono garantiti a persistere attraverso gli aggiornamenti. L'app deve rimuovere i file dalla directory quando è applicabile, come il sistema operativo non garantisce quando (o anche se) questi file vengono rimossi. + +**\ * The OS può cancellare il contenuto di questa directory ogni volta che si sente è necessario, ma non fare affidamento su questo. Si dovrebbe cancellare questa directory come adatto per l'applicazione. + +### Layout sistema Android File + +| Percorso dispositivo | `Cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistente? | OS cancella | privato | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:----:|:------------:|:-----------:|:-------:| +| `File:///android_asset/` | applicationDirectory | | r | N/A | N/A | Sì | +| `< app-id > /dati/dati / /` | applicationStorageDirectory | - | r/w | N/A | N/A | Sì | +|    `cache` | cacheDirectory | cache | r/w | Sì | Sì\* | Sì | +|    `files` | dataDirectory | file | r/w | Sì | No | Sì | +|       `Documents` | | documenti | r/w | Sì | No | Sì | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Sì | No | No | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Sì | No | No | +|       `cache` | externalCacheDirectry | cache-esterno | r/w | Sì | No** | No | +|       `files` | externalDataDirectory | file-esterno | r/w | Sì | No | No | + +\ * Il sistema operativo può cancellare periodicamente questa directory, ma non fare affidamento su questo comportamento. Cancellare il contenuto di questa directory come adatto per l'applicazione. Il contenuto di questa directory dovrebbe un utente eliminare manualmente la cache, vengono rimossi. + +** Il sistema operativo non cancella questa directory automaticamente; Siete responsabili di gestire i contenuti da soli. Il contenuto della directory dovrebbe l'utente eliminare manualmente la cache, vengono rimossi. + +**Nota**: se la memorizzazione esterna non può essere montato, le proprietà `cordova.file.external*` sono `null`. + +### BlackBerry 10 File sistema Layout + +| Percorso dispositivo | `Cordova.file.*` | r/w? | persistente? | OS cancella | privato | +|:----------------------------------------------------------- |:--------------------------- |:----:|:------------:|:-----------:|:-------:| +| `File:///accounts/1000/AppData/ < id app > /` | applicationStorageDirectory | r | N/A | N/A | Sì | +|    `app/native` | applicationDirectory | r | N/A | N/A | Sì | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | No | Sì | Sì | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Sì | No | Sì | +| `File:///accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Sì | No | No | +| `File:///accounts/1000/Shared` | sharedDirectory | r/w | Sì | No | No | + +*Nota*: quando l'applicazione viene distribuita a lavorare perimetrale, tutti i percorsi sono relativi a /accounts/1000-enterprise. + +## Stranezze Android + +### Posizione di archiviazione persistente Android + +Ci sono più percorsi validi per memorizzare i file persistenti su un dispositivo Android. Vedi [questa pagina](http://developer.android.com/guide/topics/data/data-storage.html) per un'ampia discussione delle varie possibilità. + +Versioni precedenti del plugin avrebbe scelto il percorso dei file temporanei e permanenti su avvio, in base se il dispositivo ha sostenuto che la scheda SD (o partizione storage equivalente) è stato montato. Se è stata montata sulla scheda SD o una partizione di storage interno grande era disponibile (come sui dispositivi Nexus,) allora saranno memorizzati i file persistenti nella radice di quello spazio. Questo significava che tutte le apps di Cordova poteva vedere tutti i file disponibili sulla carta. + +Se la scheda SD non era disponibile, poi versioni precedenti vuoi memorizzare dati sotto `/data/data/`, che isola i apps da altro, ma può ancora causa dati da condividere tra gli utenti. + +Ora è possibile scegliere se memorizzare i file nel percorso di archiviazione di file interno o utilizzando la logica precedente, con una preferenza nel file `config. xml` dell'applicazione. Per fare questo, aggiungere una di queste due linee al `file config. xml`: + + + + + + +Senza questa linea, il File del plugin utilizzerà la `Compatibility` come predefinito. Se è presente un tag di preferenza e non è uno di questi valori, l'applicazione non si avvia. + +Se l'applicazione è stato spedito in precedenza agli utenti, utilizzando un vecchio (pre-1.0) versione di questo plugin e ha i file memorizzati nel filesystem persistente, allora si dovrebbe impostare la preferenza di `Compatibility`. La posizione su "Interno" di commutazione significherebbe che gli utenti esistenti che aggiornare la loro applicazione potrebbero essere Impossibile accedere ai loro file precedentemente memorizzati, a seconda del loro dispositivo. + +Se l'applicazione è nuova, o ha mai precedentemente memorizzati i file nel filesystem persistente, è generalmente consigliato l'impostazione `Internal`. + +### Operazioni ricorsive lento per /android_asset + +L'elencazione delle directory asset è veramente lento su Android. È possibile velocizzare e fino anche se, con l'aggiunta di `src/android/build-extras.gradle` alla radice del tuo progetto android (richiede anche cordova-android@4.0.0 o superiore). + +## iOS stranezze + + * `cordova.file.applicationStorageDirectory`è di sola lettura; tentativo di memorizzare i file all'interno della directory radice avrà esito negativo. Utilizzare uno degli altri `cordova.file.*` proprietà definite per iOS (solo `applicationDirectory` e `applicationStorageDirectory` sono di sola lettura). + * `FileReader.readAsText(blob, encoding)` + * Il `encoding` parametro non è supportato, e codifica UTF-8 è sempre attivo. + +### posizione di archiviazione persistente di iOS + +Ci sono due percorsi validi per memorizzare i file persistenti su un dispositivo iOS: la directory documenti e la biblioteca. Precedenti versioni del plugin archiviati solo mai persistenti file nella directory documenti. Questo ha avuto l'effetto collaterale di tutti i file di un'applicazione che rende visibili in iTunes, che era spesso involontaria, soprattutto per le applicazioni che gestiscono un sacco di piccoli file, piuttosto che produrre documenti completi per l'esportazione, che è la destinazione della directory. + +Ora è possibile scegliere se memorizzare i file nella directory di libreria, con una preferenza nel file `config. xml` dell'applicazione o documenti. Per fare questo, aggiungere una di queste due linee al `file config. xml`: + + + + + + +Senza questa linea, il File del plugin utilizzerà la `Compatibility` come predefinito. Se è presente un tag di preferenza e non è uno di questi valori, l'applicazione non si avvia. + +Se l'applicazione è stato spedito in precedenza agli utenti, utilizzando un vecchio (pre-1.0) versione di questo plugin e ha i file memorizzati nel filesystem persistente, allora si dovrebbe impostare la preferenza di `Compatibility`. La posizione di commutazione alla `libreria` significherebbe che gli utenti esistenti che aggiornare la loro applicazione è in grado di accedere ai loro file precedentemente memorizzati. + +Se l'applicazione è nuova, o ha mai precedentemente memorizzati i file nel filesystem persistente, è generalmente consigliato l'impostazione della `Library`. + +## Firefox OS stranezze + +L'API di sistema del File non è supportato nativamente dal sistema operativo Firefox e viene implementato come uno spessore in cima indexedDB. + + * Non manca quando si rimuove le directory non vuota + * Non supporta i metadati per le directory + * Metodi `copyTo` e `moveTo` non supporta le directory + +Sono supportati i seguenti percorsi di dati: * `applicationDirectory` - utilizza `xhr` per ottenere i file locali che sono confezionati con l'app. *`dataDirectory` - per i file di dati persistenti app specifiche. *`cacheDirectory` - file memorizzati nella cache che dovrebbe sopravvivere si riavvia app (applicazioni non devono basarsi sull'OS di eliminare i file qui). + +## Stranezze browser + +### Stranezze e osservazioni comuni + + * Ogni browser utilizza il proprio filesystem in modalità sandbox. IE e Firefox utilizzare IndexedDB come base. Tutti i browser utilizzano barra come separatore di directory in un percorso. + * Le voci di directory devono essere creato successivamente. Ad esempio, la chiamata `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` non riuscirà se non esistesse dir1. + * Il plugin richiede autorizzazione utente per utilizzare un archivio permanente presso il primo avvio dell'applicazione. + * Plugin supporta `cdvfile://localhost` (risorse locali) solo. Cioè risorse esterne non sono supportate tramite `cdvfile`. + * Il plugin non segue ["Limitazioni di denominazione 8.3 File sistema API"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). + * BLOB e File' `close` la funzione non è supportata. + * `FileSaver` e `BlobBuilder` non sono supportati da questo plugin e non hanno gli stub. + * Il plugin non supporta `requestAllFileSystems`. Questa funzione manca anche nelle specifiche. + * Entrate nella directory non verranno rimossi se si utilizza `create: true` bandiera per directory esistente. + * Non sono supportati i file creati tramite il costruttore. È invece necessario utilizzare il metodo entry.file. + * Ogni browser utilizza la propria forma per riferimenti URL blob. + * `readAsDataURL` funzione è supportata, ma il mediatype in Chrome dipende dall'estensione di voce, mediatype in IE è sempre vuota (che è lo stesso come `text-plain` secondo la specifica), il mediatype in Firefox è sempre `application/octet-stream`. Ad esempio, se il contenuto è `abcdefg` quindi Firefox restituisce `dati: applicazione / octet-stream; base64, YWJjZGVmZw = =`, cioè restituisce `dati:; base64, YWJjZGVmZw = =`, Chrome restituisce `dati: < mediatype a seconda dell'estensione del nome della voce >; base64, YWJjZGVmZw = =`. + * `toInternalURL` restituisce il percorso in forma `file:///persistent/path/to/entry` (Firefox, IE). Chrome restituisce il percorso nella forma `cdvfile://localhost/persistent/file`. + +### Stranezze di cromo + + * Cromo filesystem non è subito pronto dopo evento ready dispositivo. Come soluzione alternativa, è possibile iscriversi all'evento `filePluginIsReady`. Esempio: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +È possibile utilizzare la funzione `window.isFilePluginReadyRaised` per verificare se evento già è stato generato. -quote di filesystem TEMPORARY e PERSISTENT window.requestFileSystem non sono limitate in Chrome. -Per aumentare la memoria persistente in Chrome è necessario chiamare il metodo `window.initPersistentFileSystem`. Quota di archiviazione persistente è di 5 MB per impostazione predefinita. -Chrome richiede `-consentire-file-accesso-da-file` eseguire argomento a supporto API tramite protocollo `file:///`. -`File` oggetto non cambierà se si utilizza il flag `{create:true}` quando ottenendo un' esistente `entrata`. -eventi `cancelable` è impostata su true in Chrome. Ciò è in contrasto con la [specifica](http://dev.w3.org/2009/dap/file-system/file-writer.html). -funzione `toURL` Chrome restituisce `filesystem:`-premessi percorso a seconda dell'applicazione host. Ad esempio, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -`toURL` risultato di funzione non contiene una barra finale in caso di voce di directory. Chrome risolve le directory con gli URL slash-trainati però correttamente. -`resolveLocalFileSystemURL` metodo richiede in ingresso `url` avere il prefisso del `file System`. Ad esempio, il parametro `url` per `resolveLocalFileSystemURL` dovrebbe essere nella forma `filesystem:file:///persistent/somefile.txt` in contrasto con la forma `file:///persistent/somefile.txt` in Android. -Obsoleto `toNativeURL` funzione non è supportata e non dispone di uno stub. -funzione `setMetadata` non è indicato nelle specifiche e non supportato. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di SYNTAX_ERR(code: 8) su richiesta di un filesystem inesistente. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di PATH_EXISTS_ERR(code: 12) sul tentativo di creare esclusivamente un file o una directory, che esiste già. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di NO_MODIFICATION_ALLOWED_ERR(code: 6) sul tentativo di chiamare removeRecursively su file system root. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di NOT_FOUND_ERR(code: 1) sul tentativo moveTo directory che non esiste. + +### Stranezze impl IndexedDB-basato (Firefox e IE) + + * `.` e `.` non sono supportati. + * IE non supporta `file:///`-modalità; modalità solo ospitata è supportato (http://localhost:xxxx). + * Dimensione filesystem Firefox non è limitata, ma ogni estensione 50MB sarà richiesta un'autorizzazione dell'utente. IE10 consente fino a 10mb di combinato AppCache e IndexedDB utilizzato nell'implementazione del filesystem senza chiedere conferma, una volta premuto quel livello che vi verrà chiesto se si desidera consentire ad essere aumentata fino a un max di 250 mb per ogni sito. Quindi la `size` parametro per la funzione `requestFileSystem` non influisce il filesystem in Firefox e IE. + * `readAsBinaryString` funzione non è indicato nelle specifiche e non supportati in IE e non dispone di uno stub. + * `file.Type` è sempre null. + * Non è necessario creare la voce utilizzando il risultato del callback istanza DirectoryEntry che è stato eliminato. In caso contrario, si otterrà una 'voce di sospensione'. + * Prima è possibile leggere un file che è stato appena scritto è necessario ottenere una nuova istanza di questo file. + * supporta la funzione `setMetadata`, che non è indicato nelle specifiche `modificationTime` cambiamento di campo solo. + * funzioni `copyTo` e `moveTo` non supporta le directory. + * Le directory metadati non sono supportato. + * Sia Entry.remove e directoryEntry.removeRecursively non fallire quando si rimuove le directory non vuota - directory da rimuovere vengono pulite invece insieme al contenuto. + * `abort` e `truncate` le funzioni non sono supportate. + * non vengono generati eventi di progresso. Ad esempio, questo gestore verrà non eseguito: + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## Note di aggiornamento + +In v 1.0.0 di questo plugin, le strutture `FileEntry` e `DirectoryEntry` sono cambiati, per essere più in linea con le specifiche pubblicate. + +Versioni precedenti (pre-1.0.0) del plugin archiviati il dispositivo-assoluto--percorso del file nella proprietà `fullPath` di oggetti della `voce`. In genere questi percorsi si sarebbe simile + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Questi percorsi sono stati anche restituiti dal metodo `toURL()` degli oggetti `Entry`. + +Con v 1.0.0, l'attributo `fullPath` è il percorso del file, *rispetto alla radice del filesystem HTML*. Così, i percorsi sopra sarebbe ora sia rappresentato da un oggetto `FileEntry` con un `fullPath` di + + /path/to/file + + +Se l'applicazione funziona con dispositivo-assoluto-percorsi, e precedentemente recuperato quei percorsi attraverso la proprietà `fullPath` della `voce` oggetti, è necessario aggiornare il codice per utilizzare `entry.toURL()` invece. + +Per indietro la compatibilità, il metodo `resolveLocalFileSystemURL()` verrà accettare un dispositivo-assoluto-percorso e restituirà un oggetto di `entrata` corrispondente ad essa, fintanto che il file esiste all'interno del filesystem la `temporanea` o `permanente`. + +Questo particolare è stato un problema con il plugin di trasferimento File, che in precedenza utilizzati percorsi-dispositivo-assoluto (e ancora può accoglierli). Esso è stato aggiornato per funzionare correttamente con gli URL di FileSystem, così sostituendo `entry.fullPath` con `entry.toURL()` dovrebbe risolvere eventuali problemi ottenendo quel plugin per lavorare con i file nel dispositivo. + +In v 1.1.0 il valore restituito di `toURL()` è stato cambiato (vedere \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) per restituire un URL assoluto 'file://'. ove possibile. Per assicurare un ' cdvfile:'-URL, è possibile utilizzare `toInternalURL()` ora. Questo metodo restituirà ora filesystem URL del modulo + + cdvfile://localhost/persistent/path/to/file + + +che può essere utilizzato per identificare univocamente il file. + +## Elenco dei codici di errore e significati + +Quando viene generato un errore, uno dei seguenti codici da utilizzare. + +| Codice | Costante | +| ------:|:----------------------------- | +| 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` | + +## Configurare il Plugin (opzionale) + +Il set di filesystem disponibili può essere configurato per ogni piattaforma. Sia iOS che Android riconoscere un Tag nel `file config. xml` che nomina il filesystem per essere installato. Per impostazione predefinita, tutte le radici del file system sono abilitate. + + + + + +### Android + + * `files`: directory di archiviazione di file interno dell'applicazione + * `files-external`: directory di archiviazione dell'applicazione file esterno + * `sdcard`: la directory di archiviazione di file esterni globale (questa è la radice della scheda SD, se uno è installato). È necessario disporre dell'autorizzazione `android.permission.WRITE_EXTERNAL_STORAGE` utilizzare questo. + * `cache`: la cache interna directory applicazione + * `cache-external`: directory di cache esterna dell'applicazione + * `root`: il dispositivo intero filesystem + +Android supporta anche un filesystem speciale denominato "documenti", che rappresenta una sottodirectory "/ documenti /" all'interno del filesystem "files". + +### iOS + + * `library`: la directory dell'applicazione libreria + * `documents`: la directory dell'applicazione documenti + * `cache`: la Cache directory applicazione + * `bundle`: bundle dell'applicazione; la posizione dell'app sul disco (sola lettura) + * `root`: il dispositivo intero filesystem + +Per impostazione predefinita, la directory di libreria e documenti può essere sincronizzata a iCloud. È anche possibile richiedere due filesystem aggiuntivi, `library-nosync` e `documents-nosync`, che rappresentano una speciale directory non sincronizzati entro il `/Library` o filesystem `/Documents`. \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/it/index.md b/plugins/cordova-plugin-file/doc/it/index.md new file mode 100644 index 0000000..f3cd731 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/it/index.md @@ -0,0 +1,338 @@ + + +# cordova-plugin-file + +Questo plugin implementa un API File permettendo l'accesso di lettura/scrittura ai file che risiedono sul dispositivo. + +Questo plugin si basa su diverse specifiche, tra cui: The HTML5 File API + +Le directory (ormai defunta) e il sistema delle estensioni più recenti: anche se la maggior parte del codice plugin è stato scritto quando una spec precedenti era corrente: + +Implementa inoltre FileWriter spec: + +Per l'utilizzo, fare riferimento a HTML5 Rocks' eccellente [articolo FileSystem.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +Per una panoramica delle altre opzioni di archiviazione, consultare [Guida di archiviazione di Cordova][2]. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +Questo plugin definisce oggetto global `cordova.file`. + +Anche se in ambito globale, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Installazione + + cordova plugin add cordova-plugin-file + + +## Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 e 8 * +* Windows 8 * +* Browser + +* *Queste piattaforme non supportano `FileReader.readAsArrayBuffer` né `FileWriter.write(blob)`.* + +## Dove memorizzare i file + +A partire dalla v 1.2.0, vengono forniti gli URL per le directory importanti file di sistema. Ogni URL è nella forma *file:///path/to/spot/* e può essere convertito in un `DirectoryEntry` utilizzando `window.resolveLocalFileSystemURL()`. + +* `cordova.file.applicationDirectory`-Sola lettura directory dove è installato l'applicazione. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.applicationStorageDirectory`-Directory radice di sandbox dell'applicazione; su iOS questa posizione è in sola lettura (ma sottodirectory specifiche [come `/Documents` ] sono di sola lettura). Tutti i dati contenuti all'interno è privato all'app. ( *iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.dataDirectory`-Archiviazione dati persistente e privati nella sandbox dell'applicazione utilizzando la memoria interna (su Android, se è necessario utilizzare la memoria esterna, utilizzare `.externalDataDirectory` ). IOS, questa directory non è sincronizzata con iCloud (utilizzare `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.cacheDirectory`-Directory per i file memorizzati nella cache di dati o qualsiasi file che app possibile ricreare facilmente. L'OS può eliminare questi file quando il dispositivo viene eseguito basso sull'archiviazione, tuttavia, apps non deve basarsi sul sistema operativo per cancellare i file qui. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.externalApplicationStorageDirectory`-Spazio applicazione su storage esterno. (*Android*) + +* `cordova.file.externalDataDirectory`-Dove mettere i file di dati specifico app su storage esterno. (*Android*) + +* `cordova.file.externalCacheDirectory`-Cache applicazione su storage esterno. (*Android*) + +* `cordova.file.externalRootDirectory`-Radice di archiviazione esterna (scheda SD). (*Android*, *BlackBerry, 10*) + +* `cordova.file.tempDirectory`-Temp directory che l'OS è possibile cancellare a volontà. Non fare affidamento sul sistema operativo per cancellare questa directory; l'app deve sempre rimuovere file come applicabile. (*iOS*) + +* `cordova.file.syncedDataDirectory`-Contiene i file app specifiche che devono essere sincronizzati (per esempio a iCloud). (*iOS*) + +* `cordova.file.documentsDirectory`-I file privati per le app, ma che sono significativi per altre applicazioni (ad esempio i file di Office). (*iOS*) + +* `cordova.file.sharedDirectory`-File disponibili globalmente a tutte le applicazioni (*BlackBerry 10*) + +## Layout dei file di sistema + +Anche se tecnicamente un dettaglio di implementazione, può essere molto utile per conoscere come le proprietà `cordova.file.*` mappa di percorsi fisici su un dispositivo reale. + +### iOS File sistema Layout + +| Percorso dispositivo | `Cordova.file.*` | `iosExtraFileSystems` | r/w? | persistente? | OS cancella | sincronizzazione | privato | +|:-------------------------------------------- |:--------------------------- |:--------------------- |:----:|:------------:|:-----------:|:----------------:|:-------:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Sì | +|    `appname.app/` | applicationDirectory | bundle | r | N/A | N/A | N/A | Sì | +|       `www/` | - | - | r | N/A | N/A | N/A | Sì | +|    `Documents/` | documentsDirectory | documenti | r/w | Sì | No | Sì | Sì | +|       `NoCloud/` | - | nosync-documenti | r/w | Sì | No | No | Sì | +|    `Library` | - | libreria | r/w | Sì | No | Sì? | Sì | +|       `NoCloud/` | dataDirectory | nosync-libreria | r/w | Sì | No | No | Sì | +|       `Cloud/` | syncedDataDirectory | - | r/w | Sì | No | Sì | Sì | +|       `Caches/` | cacheDirectory | cache | r/w | Sì * | Sì * * *| | No | Sì | +|    `tmp/` | tempDirectory | - | r/w | No * * | Sì * * *| | No | Sì | + +* File persistono attraverso riavvii app e aggiornamenti, ma questa directory può essere azzerata ogni volta che desideri l'OS. L'app dovrebbe essere in grado di ricreare qualsiasi contenuto che potrebbe essere eliminato. + +* * File può persistere attraverso app riavvii, ma non fare affidamento su questo comportamento. I file non sono garantiti a persistere attraverso gli aggiornamenti. L'app deve rimuovere i file dalla directory quando è applicabile, come il sistema operativo non garantisce quando (o anche se) questi file vengono rimossi. + +* * *| Il sistema operativo può cancellare il contenuto di questa directory ogni volta che si sente è necessario, ma non fare affidamento su questo. Si dovrebbe cancellare questa directory come adatto per l'applicazione. + +### Layout sistema Android File + +| Percorso dispositivo | `Cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistente? | OS cancella | privato | +|:--------------------------------- |:----------------------------------- |:------------------------- |:----:|:------------:|:-----------:|:-------:| +| `File:///android_asset/` | applicationDirectory | | r | N/A | N/A | Sì | +| `< app-id > /dati/dati / /` | applicationStorageDirectory | - | r/w | N/A | N/A | Sì | +|    `cache` | cacheDirectory | cache | r/w | Sì | Sì * | Sì | +|    `files` | dataDirectory | file | r/w | Sì | No | Sì | +|       `Documents` | | documenti | r/w | Sì | No | Sì | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Sì | No | No | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Sì | No | No | +|       `cache` | externalCacheDirectry | cache-esterno | r/w | Sì | No * * | No | +|       `files` | externalDataDirectory | file-esterno | r/w | Sì | No | No | + +* Il sistema operativo può cancellare periodicamente questa directory, ma non fare affidamento su questo comportamento. Cancellare il contenuto di questa directory come adatto per l'applicazione. Il contenuto di questa directory dovrebbe un utente eliminare manualmente la cache, vengono rimossi. + +* * Il sistema operativo non cancella questa directory automaticamente; Tu sei responsabile per la gestione dei contenuti da soli. Il contenuto della directory dovrebbe l'utente eliminare manualmente la cache, vengono rimossi. + +**Nota**: se la memorizzazione esterna non può essere montato, le proprietà `cordova.file.external*` sono `null`. + +### BlackBerry 10 File sistema Layout + +| Percorso dispositivo | `Cordova.file.*` | r/w? | persistente? | OS cancella | privato | +|:--------------------------------------------------- |:--------------------------- |:----:|:------------:|:-----------:|:-------:| +| `File:///accounts/1000/AppData/ < id app > /` | applicationStorageDirectory | r | N/A | N/A | Sì | +|    `app/native` | applicationDirectory | r | N/A | N/A | Sì | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | No | Sì | Sì | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Sì | No | Sì | +| `File:///accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Sì | No | No | +| `File:///accounts/1000/Shared` | sharedDirectory | r/w | Sì | No | No | + +*Nota*: quando l'applicazione viene distribuita a lavorare perimetrale, tutti i percorsi sono relativi a /accounts/1000-enterprise. + +## Stranezze Android + +### Posizione di archiviazione persistente Android + +Ci sono più percorsi validi per memorizzare i file persistenti su un dispositivo Android. Vedi [questa pagina][3] per un'ampia discussione delle varie possibilità. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +Versioni precedenti del plugin avrebbe scelto il percorso dei file temporanei e permanenti su avvio, in base se il dispositivo ha sostenuto che la scheda SD (o partizione storage equivalente) è stato montato. Se è stata montata sulla scheda SD o una partizione di storage interno grande era disponibile (come sui dispositivi Nexus,) allora saranno memorizzati i file persistenti nella radice di quello spazio. Questo significava che tutte le apps di Cordova poteva vedere tutti i file disponibili sulla carta. + +Se la scheda SD non era disponibile, poi versioni precedenti vuoi memorizzare dati sotto `/data/data/`, che isola i apps da altro, ma può ancora causa dati da condividere tra gli utenti. + +Ora è possibile scegliere se memorizzare i file nel percorso di archiviazione di file interno o utilizzando la logica precedente, con una preferenza nel file `config. xml` dell'applicazione. Per fare questo, aggiungere una di queste due linee al `file config. xml`: + + + + + + +Senza questa linea, il File del plugin utilizzerà la `Compatibility` come predefinito. Se è presente un tag di preferenza e non è uno di questi valori, l'applicazione non si avvia. + +Se l'applicazione è stato spedito in precedenza agli utenti, utilizzando un vecchio (pre-1.0) versione di questo plugin e ha i file memorizzati nel filesystem persistente, allora si dovrebbe impostare la preferenza di `Compatibility`. La posizione su "Interno" di commutazione significherebbe che gli utenti esistenti che aggiornare la loro applicazione potrebbero essere Impossibile accedere ai loro file precedentemente memorizzati, a seconda del loro dispositivo. + +Se l'applicazione è nuova, o ha mai precedentemente memorizzati i file nel filesystem persistente, è generalmente consigliato l'impostazione `Internal`. + +## iOS stranezze + +* `cordova.file.applicationStorageDirectory`è di sola lettura; tentativo di memorizzare i file all'interno della directory radice avrà esito negativo. Utilizzare uno degli altri `cordova.file.*` proprietà definite per iOS (solo `applicationDirectory` e `applicationStorageDirectory` sono di sola lettura). +* `FileReader.readAsText(blob, encoding)` + * Il `encoding` parametro non è supportato, e codifica UTF-8 è sempre attivo. + +### posizione di archiviazione persistente di iOS + +Ci sono due percorsi validi per memorizzare i file persistenti su un dispositivo iOS: la directory documenti e la biblioteca. Precedenti versioni del plugin archiviati solo mai persistenti file nella directory documenti. Questo ha avuto l'effetto collaterale di tutti i file di un'applicazione che rende visibili in iTunes, che era spesso involontaria, soprattutto per le applicazioni che gestiscono un sacco di piccoli file, piuttosto che produrre documenti completi per l'esportazione, che è la destinazione della directory. + +Ora è possibile scegliere se memorizzare i file nella directory di libreria, con una preferenza nel file `config. xml` dell'applicazione o documenti. Per fare questo, aggiungere una di queste due linee al `file config. xml`: + + + + + + +Senza questa linea, il File del plugin utilizzerà la `Compatibility` come predefinito. Se è presente un tag di preferenza e non è uno di questi valori, l'applicazione non si avvia. + +Se l'applicazione è stato spedito in precedenza agli utenti, utilizzando un vecchio (pre-1.0) versione di questo plugin e ha i file memorizzati nel filesystem persistente, allora si dovrebbe impostare la preferenza di `Compatibility`. La posizione di commutazione alla `libreria` significherebbe che gli utenti esistenti che aggiornare la loro applicazione è in grado di accedere ai loro file precedentemente memorizzati. + +Se l'applicazione è nuova, o ha mai precedentemente memorizzati i file nel filesystem persistente, è generalmente consigliato l'impostazione della `Library`. + +## Firefox OS stranezze + +L'API di sistema del File non è supportato nativamente dal sistema operativo Firefox e viene implementato come uno spessore in cima indexedDB. + +* Non manca quando si rimuove le directory non vuota +* Non supporta i metadati per le directory +* Metodi `copyTo` e `moveTo` non supporta le directory + +Sono supportati i seguenti percorsi di dati: * `applicationDirectory` - utilizza `xhr` per ottenere i file locali che sono confezionati con l'app. *`dataDirectory` - per i file di dati persistenti app specifiche. *`cacheDirectory` - file memorizzati nella cache che dovrebbe sopravvivere si riavvia app (applicazioni non devono basarsi sull'OS di eliminare i file qui). + +## Stranezze browser + +### Stranezze e osservazioni comuni + +* Ogni browser utilizza il proprio filesystem in modalità sandbox. IE e Firefox utilizzare IndexedDB come base. Tutti i browser utilizzano barra come separatore di directory in un percorso. +* Le voci di directory devono essere creato successivamente. Ad esempio, la chiamata `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` non riuscirà se non esistesse dir1. +* Il plugin richiede autorizzazione utente per utilizzare un archivio permanente presso il primo avvio dell'applicazione. +* Plugin supporta `cdvfile://localhost` (risorse locali) solo. Cioè risorse esterne non sono supportate tramite `cdvfile`. +* Il plugin non segue ["Limitazioni di denominazione 8.3 File sistema API"][4]. +* BLOB e File' `close` la funzione non è supportata. +* `FileSaver` e `BlobBuilder` non sono supportati da questo plugin e non hanno gli stub. +* Il plugin non supporta `requestAllFileSystems`. Questa funzione manca anche nelle specifiche. +* Entrate nella directory non verranno rimossi se si utilizza `create: true` bandiera per directory esistente. +* Non sono supportati i file creati tramite il costruttore. È invece necessario utilizzare il metodo entry.file. +* Ogni browser utilizza la propria forma per riferimenti URL blob. +* `readAsDataURL` funzione è supportata, ma il mediatype in Chrome dipende dall'estensione di voce, mediatype in IE è sempre vuota (che è lo stesso come `text-plain` secondo la specifica), il mediatype in Firefox è sempre `application/octet-stream`. Ad esempio, se il contenuto è `abcdefg` quindi Firefox restituisce `dati: applicazione / octet-stream; base64, YWJjZGVmZw = =`, cioè restituisce `dati:; base64, YWJjZGVmZw = =`, Chrome restituisce `dati: < mediatype a seconda dell'estensione del nome della voce >; base64, YWJjZGVmZw = =`. +* `toInternalURL` restituisce il percorso in forma `file:///persistent/path/to/entry` (Firefox, IE). Chrome restituisce il percorso nella forma `cdvfile://localhost/persistent/file`. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### Stranezze di cromo + +* Cromo filesystem non è subito pronto dopo evento ready dispositivo. Come soluzione alternativa, è possibile iscriversi all'evento `filePluginIsReady`. Esempio: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +È possibile utilizzare la funzione `window.isFilePluginReadyRaised` per verificare se evento già è stato generato. -quote di filesystem TEMPORARY e PERSISTENT window.requestFileSystem non sono limitate in Chrome. -Per aumentare la memoria persistente in Chrome è necessario chiamare il metodo `window.initPersistentFileSystem`. Quota di archiviazione persistente è di 5 MB per impostazione predefinita. -Chrome richiede `-consentire-file-accesso-da-file` eseguire argomento a supporto API tramite protocollo `file:///`. -`File` oggetto non cambierà se si utilizza il flag `{create:true}` quando ottenendo un' esistente `entrata`. -eventi `cancelable` è impostata su true in Chrome. Ciò è in contrasto con la [specifica][5]. -funzione `toURL` Chrome restituisce `filesystem:`-premessi percorso a seconda dell'applicazione host. Ad esempio, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -`toURL` risultato di funzione non contiene una barra finale in caso di voce di directory. Chrome risolve le directory con gli URL slash-trainati però correttamente. -`resolveLocalFileSystemURL` metodo richiede in ingresso `url` avere il prefisso del `file System`. Ad esempio, il parametro `url` per `resolveLocalFileSystemURL` dovrebbe essere nella forma `filesystem:file:///persistent/somefile.txt` in contrasto con la forma `file:///persistent/somefile.txt` in Android. -Obsoleto `toNativeURL` funzione non è supportata e non dispone di uno stub. -funzione `setMetadata` non è indicato nelle specifiche e non supportato. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di SYNTAX_ERR(code: 8) su richiesta di un filesystem inesistente. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di PATH_EXISTS_ERR(code: 12) sul tentativo di creare esclusivamente un file o una directory, che esiste già. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di NO_MODIFICATION_ALLOWED_ERR(code: 6) sul tentativo di chiamare removeRecursively su file system root. -INVALID_MODIFICATION_ERR (codice: 9) viene generata invece di NOT_FOUND_ERR(code: 1) sul tentativo moveTo directory che non esiste. + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### Stranezze impl IndexedDB-basato (Firefox e IE) + +* `.` e `.` non sono supportati. +* IE non supporta `file:///`-modalità; modalità solo ospitata è supportato (http://localhost:xxxx). +* Dimensione filesystem Firefox non è limitata, ma ogni estensione 50MB sarà richiesta un'autorizzazione dell'utente. IE10 consente fino a 10mb di combinato AppCache e IndexedDB utilizzato nell'implementazione del filesystem senza chiedere conferma, una volta premuto quel livello che vi verrà chiesto se si desidera consentire ad essere aumentata fino a un max di 250 mb per ogni sito. Quindi la `size` parametro per la funzione `requestFileSystem` non influisce il filesystem in Firefox e IE. +* `readAsBinaryString` funzione non è indicato nelle specifiche e non supportati in IE e non dispone di uno stub. +* `file.Type` è sempre null. +* Non è necessario creare la voce utilizzando il risultato del callback istanza DirectoryEntry che è stato eliminato. In caso contrario, si otterrà una 'voce di sospensione'. +* Prima è possibile leggere un file che è stato appena scritto è necessario ottenere una nuova istanza di questo file. +* supporta la funzione `setMetadata`, che non è indicato nelle specifiche `modificationTime` cambiamento di campo solo. +* funzioni `copyTo` e `moveTo` non supporta le directory. +* Le directory metadati non sono supportato. +* Sia Entry.remove e directoryEntry.removeRecursively non fallire quando si rimuove le directory non vuota - directory da rimuovere vengono pulite invece insieme al contenuto. +* `abort` e `truncate` le funzioni non sono supportate. +* non vengono generati eventi di progresso. Ad esempio, questo gestore verrà non eseguito: + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## Note di aggiornamento + +In v 1.0.0 di questo plugin, le strutture `FileEntry` e `DirectoryEntry` sono cambiati, per essere più in linea con le specifiche pubblicate. + +Versioni precedenti (pre-1.0.0) del plugin archiviati il dispositivo-assoluto--percorso del file nella proprietà `fullPath` di oggetti della `voce`. In genere questi percorsi si sarebbe simile + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Questi percorsi sono stati anche restituiti dal metodo `toURL()` degli oggetti `Entry`. + +Con v 1.0.0, l'attributo `fullPath` è il percorso del file, *rispetto alla radice del filesystem HTML*. Così, i percorsi sopra sarebbe ora sia rappresentato da un oggetto `FileEntry` con un `fullPath` di + + /path/to/file + + +Se l'applicazione funziona con dispositivo-assoluto-percorsi, e precedentemente recuperato quei percorsi attraverso la proprietà `fullPath` della `voce` oggetti, è necessario aggiornare il codice per utilizzare `entry.toURL()` invece. + +Per indietro la compatibilità, il metodo `resolveLocalFileSystemURL()` verrà accettare un dispositivo-assoluto-percorso e restituirà un oggetto di `entrata` corrispondente ad essa, fintanto che il file esiste all'interno del filesystem la `temporanea` o `permanente`. + +Questo particolare è stato un problema con il plugin di trasferimento File, che in precedenza utilizzati percorsi-dispositivo-assoluto (e ancora può accoglierli). Esso è stato aggiornato per funzionare correttamente con gli URL di FileSystem, così sostituendo `entry.fullPath` con `entry.toURL()` dovrebbe risolvere eventuali problemi ottenendo quel plugin per lavorare con i file nel dispositivo. + +In v 1.1.0 il valore restituito di `toURL()` è stato cambiato (vedere \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) per restituire un URL assoluto 'file://'. ove possibile. Per assicurare un ' cdvfile:'-URL, è possibile utilizzare `toInternalURL()` ora. Questo metodo restituirà ora filesystem URL del modulo + + cdvfile://localhost/persistent/path/to/file + + +che può essere utilizzato per identificare univocamente il file. + +## Elenco dei codici di errore e significati + +Quando viene generato un errore, uno dei seguenti codici da utilizzare. + +| Codice | Costante | +| ------:|:----------------------------- | +| 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` | + +## Configurare il Plugin (opzionale) + +Il set di filesystem disponibili può essere configurato per ogni piattaforma. Sia iOS che Android riconoscere un Tag nel `file config. xml` che nomina il filesystem per essere installato. Per impostazione predefinita, tutte le radici del file system sono abilitate. + + + + + +### Android + +* `files`: directory di archiviazione di file interno dell'applicazione +* `files-external`: directory di archiviazione dell'applicazione file esterno +* `sdcard`: la directory di archiviazione di file esterni globale (questa è la radice della scheda SD, se uno è installato). È necessario disporre dell'autorizzazione `android.permission.WRITE_EXTERNAL_STORAGE` utilizzare questo. +* `cache`: la cache interna directory applicazione +* `cache-external`: directory di cache esterna dell'applicazione +* `root`: il dispositivo intero filesystem + +Android supporta anche un filesystem speciale denominato "documenti", che rappresenta una sottodirectory "/ documenti /" all'interno del filesystem "files". + +### iOS + +* `library`: la directory dell'applicazione libreria +* `documents`: la directory dell'applicazione documenti +* `cache`: la Cache directory applicazione +* `bundle`: bundle dell'applicazione; la posizione dell'app sul disco (sola lettura) +* `root`: il dispositivo intero filesystem + +Per impostazione predefinita, la directory di libreria e documenti può essere sincronizzata a iCloud. È anche possibile richiedere due filesystem aggiuntivi, `library-nosync` e `documents-nosync`, che rappresentano una speciale directory non sincronizzati entro il `/Library` o filesystem `/Documents`. diff --git a/plugins/cordova-plugin-file/doc/it/plugins.md b/plugins/cordova-plugin-file/doc/it/plugins.md new file mode 100644 index 0000000..d3f1465 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/it/plugins.md @@ -0,0 +1,101 @@ + + +# Note per gli sviluppatori di plugin + +Queste note sono destinate principalmente per gli sviluppatori di Android e iOS che vogliono scrivere plugin quale interfaccia con il sistema di file utilizzando il File plugin. + +## Lavorando con URL di sistema file di Cordova + +Dalla versione 1.0.0, questo plugin ha utilizzato gli URL con un `cdvfile` schema per tutte le comunicazioni oltre il ponte, piuttosto che esporre i percorsi del file system di dispositivo raw a JavaScript. + +Sul lato JavaScript, questo significa che gli oggetti FileEntry e DirectoryEntry dispongano di un attributo fullPath che è relativo alla directory principale del sistema di file HTML. Se API JavaScript del vostro plugin accetta un oggetto FileEntry o DirectoryEntry, è necessario chiamare `.toURL()` su quell'oggetto prima di passarlo attraverso il ponte in codice nativo. + +### Conversione cdvfile: / / URL ai percorsi fileystem + +Plugin che hanno bisogno di scrivere il filesystem può essere necessario convertire un URL di sistema del file ricevuto in un percorso effettivo filesystem. Ci sono diversi modi di fare questo, a seconda della piattaforma nativa. + +È importante ricordare che non tutti i `cdvfile://` URL sono mappabili ai veri file sul dispositivo. Alcuni URL può riferirsi a beni sul dispositivo che non sono rappresentate da file, o possono anche fare riferimento a risorse remote. A causa di queste possibilità, plugin dovrebbe sempre verificare se ottengono un risultato significativo indietro quando si tenta di convertire gli URL in percorsi. + +#### Android + +Su Android, il metodo più semplice per convertire un `cdvfile://` URL a un percorso di file System è quello di utilizzare `org.apache.cordova.CordovaResourceApi` . `CordovaResourceApi`dispone di diversi metodi che è possono gestire `cdvfile://` URL: + + webView è un membro del Plugin classe CordovaResourceApi resourceApi = webView.getResourceApi(); + + Ottenere un URL file:/// che rappresenta questo file sul dispositivo, / / o lo stesso URL invariata se non può essere mappato a un file Uri fileURL = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +È anche possibile utilizzare direttamente il File plugin: + + importazione org.apache.cordova.file.FileUtils; + importazione org.apache.cordova.file.FileSystem; + importazione java.net.MalformedURLException; + + Ottenere il File plugin dal gestore plugin FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + Dato un URL, ottenere un percorso per esso prova {String path = filePlugin.filesystemPathForURL(cdvfileURL);}} catch (MalformedURLException e) {/ / l'url del file System non è stato riconosciuto} + + +Convertire da un percorso a un `cdvfile://` URL: + + importazione org.apache.cordova.file.LocalFilesystemURL; + + Ottenere un oggetto LocalFilesystemURL per un percorso di dispositivo, / / oppure null se non può essere rappresentata come un URL di cdvfile. + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path); + Ottenere la rappresentazione di stringa dell'URL oggetto String cdvfileURL = url.toString(); + + +Se il vostro plugin crea un file e si desidera restituire un oggetto FileEntry per esso, utilizzare il File plugin: + + Restituire una struttura JSON appropriato per restituire a JavaScript, / / o null se questo file non è rappresentabile come un URL di cdvfile. + Voce di JSONObject = filePlugin.getEntryForFile(file); + + +#### iOS + +Cordova su iOS non utilizza lo stesso `CordovaResourceApi` concetto come Android. Su iOS, si dovrebbe utilizzare il plugin File per convertire tra URL e percorsi di file System. + + Ottenere un oggetto CDVFilesystem URL da una stringa CDVFilesystemURL * url = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL]; + Ottenere un percorso per l'oggetto URL, o zero se non può essere mappato a un percorso di file NSString * = [filePlugin filesystemPathForURL:url]; + + + Ottenere un oggetto CDVFilesystem URL per un percorso di dispositivo, o / / nullo se non può essere rappresentata come un URL di cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + Ottenere la rappresentazione di stringa dell'URL oggetto NSString * cdvfileURL = [absoluteString url]; + + +Se il vostro plugin crea un file e si desidera restituire un oggetto FileEntry per esso, utilizzare il File plugin: + + Ottenere un oggetto CDVFilesystem URL per un percorso di dispositivo, o / / nullo se non può essere rappresentata come un URL di cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + Ottenere una struttura per tornare alla voce JavaScript NSDictionary * = [filePlugin makeEntryForLocalURL:url] + + +#### JavaScript + +In JavaScript, per ottenere un `cdvfile://` URL da un oggetto FileEntry o DirectoryEntry, semplicemente chiamare `.toURL()` su di esso: + + var cdvfileURL = entry.toURL(); + + +Nei gestori di risposta del plugin, per convertire da una struttura FileEntry restituita in un oggetto reale di voce, il codice del gestore dovrebbe importare il File plugin e creare un nuovo oggetto: + + creare la voce appropriata a voce oggetto var; + Se (entryStruct.isDirectory) {voce = new DirectoryEntry (entryStruct.name, entryStruct.fullPath, nuovo FileSystem(entryStruct.filesystemName));} altro {voce = FileEntry nuovo (entryStruct.name, entryStruct.fullPath, nuovo FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/ja/README.md b/plugins/cordova-plugin-file/doc/ja/README.md new file mode 100644 index 0000000..96e8ff0 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ja/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã¸ã®èª­ã¿å–り/書ãè¾¼ã¿ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹ãƒ•ァイル API を実装ã—ã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインをå«ã‚€ã€ã„ãã¤ã‹ã®ä»•様ã«åŸºã¥ã„ã¦ã„ã¾ã™ï¼šã€HTML5 File API ã® + +(今ã¯äº¡ã) ディレクトリã¨ã‚·ã‚¹ãƒ†ãƒ ã¯ã€æœ€æ–°ã®æ‹¡å¼µæ©Ÿèƒ½: プラグインã®ã‚³ãƒ¼ãƒ‰ã®ã»ã¨ã‚“ã©ã¯ã¨ãã«ã€ä»¥å‰ã®ä»•æ§˜ã«æ›¸ã‹ã‚Œã¦ã„ãŸãŒç¾åœ¨ã¯ï¼š + +FileWriter 仕様も実装ã—ã¦ã„ã¾ã™: + +使用法をå‚ç…§ã—ã¦ãã ã•ã„ HTML5 岩 ' 優秀ãª[ファイルシステム記事](http://www.html5rocks.com/en/tutorials/file/filesystem/)。 + +ä»–ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ã‚ªãƒ—ã‚·ãƒ§ãƒ³ã®æ¦‚è¦ã«ã¤ã„ã¦ã¯ã€ã‚³ãƒ«ãƒ‰ãƒã®[ストレージ ・ ガイド](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html)ã‚’å‚ç…§ã—ã¦ãã ã•ã„。. + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `cordova.file` オブジェクトを定義ã—ã¾ã™ã€‚ + +グローãƒãƒ« スコープã§ã¯ã‚ã‚‹ãŒãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã§ã™ã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## インストール + + cordova plugin add cordova-plugin-file + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * Firefox ã® OS + * iOS + * Windows Phone 7 㨠8 * + * Windows 8 * + * Windows* + * ブラウザー + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## ファイルをä¿å­˜ã™ã‚‹å ´æ‰€ + +V1.2.0ã€ç¾åœ¨é‡è¦ãªãƒ•ァイル システム ディレクトリã¸ã® Url ã‚’æä¾›ã—ã¦ã„ã¾ã™ã€‚ å„ URL ã¯ãƒ•ォーム *file:///path/to/spot/* ã§ã€`window.resolveLocalFileSystemURL()` を使用ã™ã‚‹ `DirectoryEntry` ã«å¤‰æ›ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚. + + * `cordova.file.applicationDirectory`-読ã¿å–り専用ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ã€ã‚¢ãƒ—リケーションãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã™ã€‚(*iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + + * `cordova.file.applicationStorageDirectory`-アプリケーションã®ã‚µãƒ³ãƒ‰ ボックス; ã®ãƒ«ãƒ¼ãƒˆ ディレクトリiOS ã§ã“ã®å ´æ‰€ãŒèª­ã¿å–り専用 (特定ã®ã‚µãƒ–ディレクトリ㌠[ã®ã‚ˆã†ãª `/Documents` ] ã¯ã€èª­ã¿å–り/書ãè¾¼ã¿)。 内ã«å«ã¾ã‚Œã‚‹ã™ã¹ã¦ã®ãƒ‡ãƒ¼ã‚¿ã¯ã€ã‚¢ãƒ—リケーションã«ãƒ—ライベートã§ã™ã€‚ ( *iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + + * `cordova.file.dataDirectory`内部メモリを使用ã—ã¦ã€ã‚¢ãƒ—リケーションã®ã‚µãƒ³ãƒ‰ ãƒœãƒƒã‚¯ã‚¹å†…ã§æ°¸ç¶šãªãƒ—ライベート データ ストレージ (外部メモリを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚‹å ´åˆä½¿ç”¨ã—㦠Android 上㧠`.externalDataDirectory` )。 IOS ã¯ã€ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ iCloud ã¨åŒæœŸã•れã¾ã›ã‚“ (使用ã™ã‚‹ `.syncedDataDirectory` )。 (*iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + + * `cordova.file.cacheDirectory`-キャッシュã•れãŸãƒ‡ãƒ¼ã‚¿ ファイルやアプリã«ç°¡å˜ã«å†ä½œæˆã§ãã‚‹ä»»æ„ã®ãƒ•ァイルã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã€‚ ストレージ デãƒã‚¤ã‚¹ãŒä¸è¶³ã—ãŸã¨ãã«ã€OS ãŒã“れらã®ãƒ•ァイルを削除å¯èƒ½æ€§ãŒã‚りã¾ã™ã€ãれã«ã‚‚ã‹ã‹ã‚らãšã€ã‚¢ãƒ—リã¯ã“ã“ã«ãƒ•ァイルを削除ã™ã‚‹ OS ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„。 (*iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + + * `cordova.file.externalApplicationStorageDirectory`外部ストレージã®ã‚¢ãƒ—リケーション領域。(*アンドロイド*) + + * `cordova.file.externalDataDirectory`-外部ストレージ上ã®ã‚¢ãƒ—リ固有ã®ãƒ‡ãƒ¼ã‚¿ ファイルをé…ç½®ã™ã‚‹å ´æ‰€ã€‚(*アンドロイド*) + + * `cordova.file.externalCacheDirectory`外部ストレージã«ã‚¢ãƒ—リケーション キャッシュ。(*アンドロイド*) + + * `cordova.file.externalRootDirectory`-外部ストレージ (SD カード) ルート。(*アンドロイド*ã€*ブラックベリー 10*) + + * `cordova.file.tempDirectory`-OS をクリアã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ temp ディレクトリãŒã€‚ ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª; オフ㫠OS ã«ä¾å­˜ã—ã¾ã›ã‚“。アプリãŒå¸¸ã«è©²å½“ã™ã‚‹ãƒ•ァイルを削除ã—ã¾ã™ã€‚ (*iOS*) + + * `cordova.file.syncedDataDirectory`-(例ãˆã° iCloud) ã«åŒæœŸã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã‚¢ãƒ—リケーション固有ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ä¿æŒã—ã¾ã™ã€‚(*iOS*) + + * `cordova.file.documentsDirectory`-ファイルã€ã‚¢ãƒ—リケーションã«ãƒ—ライベートã¯ä»–ã®ã‚¢ãƒ—リケーション (Office ファイルãªã©ï¼‰ ã‚’æ„味ã§ã™ã€‚(*iOS*) + + * `cordova.file.sharedDirectory`ã™ã¹ã¦ã®ã‚¢ãƒ—リケーション (*ブラックベリー 10*ã«ã‚°ãƒ­ãƒ¼ãƒãƒ«ã«ä½¿ç”¨ã§ãるファイル) + +## ファイル ・ システム ・ レイアウト + +技術的ã«å®Ÿè£…ã®è©³ç´°ã€éžå¸¸ã«ã©ã®ã‚ˆã†ã« `cordova.file.*` プロパティã¯ã€å®Ÿéš›ã®ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ç‰©ç†ãƒ‘スã«ãƒžãƒƒãƒ—を知ã£ã¦ãŠãã¨ä¾¿åˆ©ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + +### iOS ファイル システムã®ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆ + +| デãƒã‚¤ã‚¹ ・ パス | `cordova.file.*` | `iosExtraFileSystems` | r/w ã§ã™ã‹ï¼Ÿ | 永続的ãªã§ã™ã‹ï¼Ÿ | OS を消去ã—ã¾ã™ | åŒæœŸ | プライベート | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:--------:|:---------:|:------------:|:------:|:------:| +| `/var/モãƒã‚¤ãƒ«/アプリケーション/< UUID >/` | applicationStorageDirectory | - | r | N/A | N/A | N/A | ã¯ã„ | +|    `appname.app/` | ディレクトリ | ãƒãƒ³ãƒ‰ãƒ« | r | N/A | N/A | N/A | ã¯ã„ | +|       `www/` | - | - | r | N/A | N/A | N/A | ã¯ã„ | +|    `Documents/` | documentsDirectory | ドキュメント | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | ã¯ã„ | +|       `NoCloud/` | - | ドキュメント nosync | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| ã¯ã„ | +|    `Library` | - | ライブラリ | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ã§ã™ã‹ï¼Ÿ | ã¯ã„ | +|       `NoCloud/` | dataDirectory | ライブラリ nosync | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| ã¯ã„ | +|       `Cloud/` | syncedDataDirectory | - | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | ã¯ã„ | +|       `Caches/` | cacheDirectory | キャッシュ | r/w | ã¯ã„ * | ã¯ã„**\* | ã„ã„㈠| ã¯ã„ | +|    `tmp/` | tempDirectory | - | r/w | ã„ã„ãˆ** | ã¯ã„**\* | ã„ã„㈠| ã¯ã„ | + +\ * アプリå†èµ·å‹•ã‚„ã‚¢ãƒƒãƒ—ã‚°ãƒ¬ãƒ¼ãƒ‰ã€æ°¸ç¶šåŒ–ファイルã—ã¾ã™ãŒã€OS を希望ã™ã‚‹ãŸã³ã«ã€ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’削除ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚アプリã¯å‰Šé™¤å¯èƒ½æ€§ãŒã‚りã¾ã™ä»»æ„ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å†ç¾ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã¯ãšã€‚ + +**ファイルã¯ã‚¢ãƒ—リå†èµ·å‹•ã®é–“ç¶šãã“ã¨ãŒã‚りã¾ã™ãŒã€ã“ã®å‹•作ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„。 ファイルã¯ã€æ›´æ–°ã‚’ç¶­æŒã™ã‚‹ã¯ä¿è¨¼ã•れã¾ã›ã‚“。 アプリãŒè©²å½“ã™ã‚‹å ´åˆã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‹ã‚‰ãƒ•ァイルを削除ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€ã“れらã®ãƒ•ァイルãŒå‰Šé™¤ã•れるã¨ã (ã¾ãŸã¯å ´åˆã§ã‚‚)ã€OS ã¯ä¿è¨¼ã—ã¾ã›ã‚“。 + +**\ * OS ã¯ãれã€å¿…è¦ã ã¨æ„Ÿã˜ã¦ã„ã‚‹ã¨ãã«ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容を消去å¯èƒ½æ€§ãŒã‚りã¾ã™ã€ã“れã«ä¾å­˜ã—ãªã„よã†ã«ã—ã¾ã™ã€‚ ã“ã®é©åˆ‡ãªãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«ã€ã‚¢ãƒ—リケーションをオフã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +### 人造人間ファイル ・ システム ・ レイアウト + +| デãƒã‚¤ã‚¹ ・ パス | `cordova.file.*` | `AndroidExtraFileSystems` | r/w ã§ã™ã‹ï¼Ÿ | 永続的ãªã§ã™ã‹ï¼Ÿ | OS を消去ã—ã¾ã™ | プライベート | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:--------:|:--------:|:---------:|:------:| +| `file:///android_asset/` | ディレクトリ | | r | N/A | N/A | ã¯ã„ | +| `/データ/データ/< app id >/` | applicationStorageDirectory | - | r/w | N/A | N/A | ã¯ã„ | +|    `cache` | cacheDirectory | キャッシュ | r/w | ã¯ã„ | ã¯ã„\ * | ã¯ã„ | +|    `files` | dataDirectory | ファイル | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | +|       `Documents` | | ドキュメント | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | +| `< sd カード >/` | externalRootDirectory | sd カード | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| +|       `cache` | externalCacheDirectry | 外部キャッシュ | r/w | ã¯ã„ | ã„ã„ãˆ** | ã„ã„㈠| +|       `files` | externalDataDirectory | 外部ファイル | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| + +\ * OS ã¯å®šæœŸçš„ã«ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’消去å¯èƒ½æ€§ãŒã‚りã¾ã™ãŒã€ã“ã®å‹•作ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„。 アプリケーションã®å¿…è¦ã«å¿œã˜ã¦ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容をオフã«ã—ã¾ã™ã€‚ ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ‰‹å‹•ã§ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’削除ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容ãŒå‰Šé™¤ã•れã¾ã™ã€‚ + +** OS ã¯ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’自動的ã«ã¯ã‚¯ãƒªã‚¢ã•れã¾ã›ã‚“内容を自分ã§ç®¡ç†ã™ã‚‹è²¬ä»»ãŒã‚りã¾ã™ã€‚ ãƒ¦ãƒ¼ã‚¶ã¯æ‰‹å‹•ã§ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’消去ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容ãŒå‰Šé™¤ã•れã¾ã™ã€‚ + +**注**: 外部記憶装置をマウントã§ããªã„å ´åˆã¯ã€`cordova.file.external*` プロパティを `null`. + +### ブラックベリー 10 ファイル ・ システム ・ レイアウト + +| デãƒã‚¤ã‚¹ ・ パス | `cordova.file.*` | r/w ã§ã™ã‹ï¼Ÿ | 永続的ãªã§ã™ã‹ï¼Ÿ | OS を消去ã—ã¾ã™ | プライベート | +|:----------------------------------------------------------- |:--------------------------- |:--------:|:--------:|:---------:|:------:| +| `file:///accounts/1000/appdata/< app id >/` | applicationStorageDirectory | r | N/A | N/A | ã¯ã„ | +|    `app/native` | ディレクトリ | r | N/A | N/A | ã¯ã„ | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | ã„ã„㈠| ã¯ã„ | ã¯ã„ | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| +| `file:///accounts/1000/shared` | sharedDirectory | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| + +*注*: ã™ã¹ã¦ã®ãƒ‘スã¯/accounts/1000-enterprise 基準ã«å¢ƒç•Œã‚’動作ã™ã‚‹ã‚ˆã†ã«ã‚¢ãƒ—リケーションを展開ã™ã‚‹ã¨ã。 + +## Android ã®ç™– + +### Android ã®æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã®å ´æ‰€ + +Android ã®ãƒ‡ãƒã‚¤ã‚¹ã«æ°¸ç¶šçš„ãªãƒ•ァイルを格ç´ã™ã‚‹è¤‡æ•°ã®æœ‰åйãªå ´æ‰€ãŒã‚りã¾ã™ã€‚ ã•ã¾ã–ã¾ãªå¯èƒ½æ€§ã«ã¤ã„ã¦åºƒç¯„ãªè­°è«–ã®ãŸã‚ã® [ã“ã®ãƒšãƒ¼ã‚¸](http://developer.android.com/guide/topics/data/data-storage.html) ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + +以å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã® SD カード (ã¾ãŸã¯åŒç­‰ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ パーティション) マウントã•れã¦ã„ãŸã¨ä¸»å¼µã—ãŸã‹ã©ã†ã‹ã«åŸºã¥ã„ã¦ã€èµ·å‹•時ã«ä¸€æ™‚ã¨æ°¸ç¶šçš„ãªãƒ•ァイルã®å ´æ‰€ã‚’é¸ã¶ã§ã—ょã†ã€‚ SD カードãŒãƒžã‚¦ãƒ³ãƒˆã•れã¦ã„ã‚‹å ´åˆã€ã¾ãŸã¯å¤§è¦æ¨¡ãªå†…部ストレージ パーティションãŒåˆ©ç”¨å¯èƒ½ãªå ´åˆ (よã†ãƒã‚¯ã‚µã‚¹ デãƒã‚¤ã‚¹ä¸Šï¼‰ ã—ã€æ°¸ç¶šçš„ãªãƒ•ァイルã¯ã€ãã®é ˜åŸŸã®ãƒ«ãƒ¼ãƒˆã«æ ¼ç´ã•れã¾ã™ã€‚ ã“れã¯ã™ã¹ã¦ã® Cordova アプリ見るã“ã¨ãŒã§ãる利用å¯èƒ½ãªãƒ•ァイルã®ã™ã¹ã¦ã®ã‚«ãƒ¼ãƒ‰ã«æ„味ã—ã¾ã—ãŸã€‚ + +SD カードãŒãªã„å ´åˆã€ä»¥å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒãƒ‡ãƒ¼ã‚¿ã‚’æ ¼ç´ã™ã‚‹ `/data/data/`ã€ãŠäº’ã„ã‹ã‚‰ã‚¢ãƒ—リを分離ã™ã‚‹ãŒã€ã¾ã åŽŸå› å¯èƒ½æ€§ãŒã‚りã¾ã™ãƒ¦ãƒ¼ã‚¶ãƒ¼é–“ã§å…±æœ‰ã™ã‚‹ãƒ‡ãƒ¼ã‚¿ã€‚ + +内部ファイルã®ä¿å­˜å ´æ‰€ã‚„アプリケーション㮠`config.xml` ファイルã«å„ªå…ˆé †ä½ã‚’æŒã¤ä»¥å‰ã®ãƒ­ã‚¸ãƒƒã‚¯ã‚’使用ã—ã¦ãƒ•ァイルをä¿å­˜ã™ã‚‹ã‹ã©ã†ã‹ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ä»Šã€‚ ã“れを行ã†ã«ã€`config.xml` ã«æ¬¡ã® 2 行ã®ã„ãšã‚Œã‹ã‚’追加ã—ã¾ã™ã€‚ + + + + + + +ã“ã®è¡ŒãŒãªã‘れã°ãƒ•ァイル プラグインã¯ãƒ‡ãƒ•ォルトã¨ã—㦠`Compatibility` を使用ã—ã¾ã™ã€‚優先タグãŒå­˜åœ¨ã—ã€ã“れらã®å€¤ã® 1 ã¤ã§ã¯ãªã„å ´åˆã€ã‚¢ãƒ—リケーションã¯èµ·å‹•ã—ã¾ã›ã‚“。 + +アプリケーションã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ä»¥å‰å‡ºè·ã•れã¦ã„ã‚‹å ´åˆã€å¤ã„ (å‰ 1.0) を使用ã—ã¦ã€ã“ã®ãƒ—ラグインã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã—ã€æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ä¿å­˜ã•れã¦ã„るファイルã«ã¯ `Compatibility` を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 自分ã®ã‚¢ãƒ—リケーションをアップグレードã™ã‚‹æ—¢å­˜ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’彼らã®è£…ç½®ã«ã‚ˆã£ã¦ã€ä»¥å‰ã«ä¿å­˜ã•れãŸãƒ•ァイルã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã“ã¨ãŒã‚ã‚‹ã“ã¨ã‚’æ„味ã™ã‚‹ã€Œå†…部ã€ã«å ´æ‰€ã‚’スイッãƒãƒ³ã‚°ã—ã¾ã™ã€‚ + +アプリケーションã¯ã€æ–°ã—ã„ã€ã¾ãŸã¯æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ ¼ç´ã•れ以å‰ã¯æ±ºã—ã¦å ´åˆã€`内部` è¨­å®šä¸€èˆ¬çš„ã«æŽ¨å¥¨ã•れã¾ã™ã€‚ + +### /Android_asset ã®ä½Žé€Ÿã®å†å¸°çš„ãªæ“作 + +アセット ディレクトリã®ä¸€è¦§è¡¨ç¤ºã¯ã€äººé€ äººé–“本当ã«é…ã„ã§ã™ã€‚ ãれをスピードアップã™ã‚‹ã“ã¨ãŒã§ãã¾ã™`src/android/build-extras.gradle`ã‚’ android プロジェクトã®ãƒ«ãƒ¼ãƒˆã«è¿½åŠ ã™ã‚‹ã“ã¨ã«ã‚ˆã£ã¦ã€æœ€å¤§ (ã¾ãŸ cordova-android@4.0.0 ãŒå¿…è¦ã§ã™ã¾ãŸã¯ãれ以上)。 + +## iOS ã®ç™– + + * `cordova.file.applicationStorageDirectory`読ã¿å–り専用;ルート ディレクトリ内ã®ãƒ•ァイルをä¿å­˜ã—よã†ã¯å¤±æ•—ã—ã¾ã™ã€‚ ä»–ã® 1 ã¤ã‚’使用ã—㦠`cordova.file.*` iOS ã®ãŸã‚定義ã•れã¦ã„るプロパティ (ã®ã¿ `applicationDirectory` 㨠`applicationStorageDirectory` ã¯èª­ã¿å–り専用)。 + * `FileReader.readAsText(blob, encoding)` + * `encoding`パラメーターã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã—ã€utf-8 エンコーディングãŒå¸¸ã«æœ‰åйã§ã™ã€‚ + +### iOS ã®æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã®å ´æ‰€ + +IOS デãƒã‚¤ã‚¹ã«æ°¸ç¶šçš„ãªãƒ•ァイルを格ç´ã™ã‚‹ 2 ã¤ã®æœ‰åйãªå ´æ‰€ãŒã‚ã‚‹: ドキュメントã¨ãƒ©ã‚¤ãƒ–ラリã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã€‚ プラグインã®ä»¥å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯ã€å”¯ä¸€ã®ã“れã¾ã§ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æ°¸ç¶šçš„ãªãƒ•ァイルを格ç´ã•れã¾ã™ã€‚ ã“れã¯ã€ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®ç›®çš„ã¯ã€è¼¸å‡ºã®ãŸã‚ã®å®Œå…¨ãªãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’作æˆã™ã‚‹ã®ã§ã¯ãªããªã‹ã£ãŸãŒã—ã°ã—ã°æ„図ã•れã¦ã„ãŸã‚Šã€ç‰¹ã«å¤šæ•°ã®å°ã•ã„ファイルを処ç†ã™ã‚‹ã‚¢ãƒ—リケーションã®å ´åˆã€iTunes ã«è¡¨ç¤ºã•れã¦ã„ã‚‹ã™ã¹ã¦ã®ã‚¢ãƒ—リケーションã®ãƒ•ァイルを作るã®å‰¯ä½œç”¨ãŒã‚ã£ãŸã€‚ + +ドキュメントã¾ãŸã¯ã‚¢ãƒ—リケーション㮠`config.xml` ファイルã«å„ªå…ˆé †ä½ã®ãƒ©ã‚¤ãƒ–ラリ ディレクトリã«ãƒ•ァイルをä¿å­˜ã™ã‚‹ã‹ã©ã†ã‹ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ä»Šã€‚ ã“れを行ã†ã«ã€`config.xml` ã«æ¬¡ã® 2 行ã®ã„ãšã‚Œã‹ã‚’追加ã—ã¾ã™ã€‚ + + + + + + +ã“ã®è¡ŒãŒãªã‘れã°ãƒ•ァイル プラグインã¯ãƒ‡ãƒ•ォルトã¨ã—㦠`Compatibility` を使用ã—ã¾ã™ã€‚優先タグãŒå­˜åœ¨ã—ã€ã“れらã®å€¤ã® 1 ã¤ã§ã¯ãªã„å ´åˆã€ã‚¢ãƒ—リケーションã¯èµ·å‹•ã—ã¾ã›ã‚“。 + +アプリケーションã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ä»¥å‰å‡ºè·ã•れã¦ã„ã‚‹å ´åˆã€å¤ã„ (å‰ 1.0) を使用ã—ã¦ã€ã“ã®ãƒ—ラグインã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã—ã€æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ä¿å­˜ã•れã¦ã„るファイルã«ã¯ `Compatibility` を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 自分ã®ã‚¢ãƒ—リケーションをアップグレードã™ã‚‹æ—¢å­˜ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’以å‰ã«ä¿å­˜ã•れãŸãƒ•ァイルã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã ã‚ã†ã“ã¨ã‚’æ„味ã™ã‚‹ `Library` ã«å ´æ‰€ã‚’スイッãƒãƒ³ã‚°ã—ã¾ã™ã€‚ + +アプリケーションã¯ã€æ–°ã—ã„ã€ã¾ãŸã¯æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ ¼ç´ã•れ以å‰ã¯æ±ºã—ã¦å ´åˆã€`Library` è¨­å®šä¸€èˆ¬çš„ã«æŽ¨å¥¨ã•れã¾ã™ã€‚ + +## Firefox OS ç™– + +ファイル システム API Firefox OS ã§ãƒã‚¤ãƒ†ã‚£ãƒ– サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã¨ã€indexedDB ã®ä¸Šã«ã‚·ãƒ ã¨ã—ã¦å®Ÿè£…ã•れã¦ã„ã¾ã™ã€‚ + + * 空ã§ãªã„ディレクトリを削除ã™ã‚‹ã¨ãã«å¤±æ•—ã—ã¾ã›ã‚“ + * ディレクトリã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“ + * 方法 `copyTo` 㨠`moveTo` ディレクトリをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“ + +次ã®ãƒ‡ãƒ¼ã‚¿ パスãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™: * `applicationDirectory` - `xhr` を使用ã—ã¦ã€ã‚¢ãƒ—リケーションã¨å…±ã«ãƒ‘ッケージ化ã•れるローカル ファイルをå–å¾—ã—ã¾ã™ã€‚ * `dataDirectory` - 永続的ãªã‚¢ãƒ—リケーション固有ã®ãƒ‡ãƒ¼ã‚¿ ファイル。 * `cacheDirectory` - アプリケーションã®å†èµ·å‹•後も維æŒã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã‚­ãƒ£ãƒƒã‚·ãƒ¥ ファイル (アプリã¯ã“ã“ã«ãƒ•ァイルを削除ã™ã‚‹ OS ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„)。 + +## ブラウザーã®ç™– + +### 共通ã®ç™–ã¨ç™ºè¨€ + + * å„ブラウザーã¯ã‚µãƒ³ãƒ‰ ボックス化ã•れãŸãƒ•ァイルシステムを使用ã—ã¾ã™ã€‚IE 㨠Firefox IndexedDB をベースã¨ã—ã¦ä½¿ç”¨ã—ã¾ã™ã€‚ã™ã¹ã¦ã®ãƒ–ラウザーã¯ã€ãƒ‘スã«ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®åŒºåˆ‡ã‚Šè¨˜å·ã¨ã—ã¦ã‚¹ãƒ©ãƒƒã‚·ãƒ¥ã‚’使用ã—ã¾ã™ã€‚ + * ディレクトリ エントリã¯é€£ç¶šã—ã¦ä½œæˆã•れãªã‘れã°ãªã‚Šã¾ã›ã‚“。 ãŸã¨ãˆã°ã€ã‚³ãƒ¼ãƒ« `fs.root.getDirectory ('dir1 dir2'ã€{create:true}ã€successCallbackã€è§£ã‚Š)` dir1 ãŒå­˜åœ¨ã—ãªã‹ã£ãŸå ´åˆã¯å¤±æ•—ã—ã¾ã™ã€‚ + * プラグインã¯ã€æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã®æœ€åˆã®èµ·å‹•時ã«ä½¿ç”¨ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’è¦æ±‚ã—ã¾ã™ã€‚ + * プラグインã¯ã€`cdvfile://localhost` (ローカル リソース) をサãƒãƒ¼ãƒˆã—ã¦ã„ã‚‹ã ã‘ã§ã™ã€‚ã™ãªã‚ã¡å¤–部リソースã¯ã€`cdvfile` を介ã—ã¦ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“. + * プラグイン㫠[制é™ã€ã®ãƒ•ァイル システム API ã® 8.3 命åè¦å‰‡](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions) ã«å¾“ã£ã¦ã„ã¾ã›ã‚“。. + * Blob ãŠã‚ˆã³ãƒ•ァイル ' `close` 関数ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + * `FileSaver` 㨠`BlobBuilder` ã“ã®ãƒ—ラグインã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„スタブをæŒã£ã¦ã„ã¾ã›ã‚“。 + * プラグインã¯ã€`requestAllFileSystems` をサãƒãƒ¼ãƒˆã—ã¾ã›ã‚“。ã“ã®é–¢æ•°ã¯ã€ä»•様ã§è¡Œæ–¹ä¸æ˜Žã«ã‚‚。 + * ディレクトリ内ã®ã‚¨ãƒ³ãƒˆãƒªã‚’使用ã™ã‚‹ã¨å‰Šé™¤ã•れã¾ã›ã‚“ `create: true` 既存ディレクトリã®ãƒ•ラグ。 + * コンス トラクターã§ä½œæˆã•れãŸãƒ•ァイルã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。代ã‚り㫠entry.file メソッドを使用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + * å„ブラウザー㯠blob URL å‚ç…§ã®ç‹¬è‡ªã®ãƒ•ォームを使用ã—ã¾ã™ã€‚ + * `readAsDataURL` 関数ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ãŒã‚¯ãƒ­ãƒ ãƒ¡ãƒƒã‚­ã§ mediatype エントリåã®æ‹¡å¼µå­ã«ã‚ˆã£ã¦ç•°ãªã‚Šã¾ã™ã€IE ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã®ç¨®é¡žã¯ã€å¸¸ã«ç©º (`text-plain` ã«å¾“ã£ã¦ã€ä»•様ã¨åŒã˜ã§ã‚る)ã€Firefox ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã®ç¨®é¡žã¯å¸¸ã« `アプリケーションã¾ãŸã¯ã‚ªã‚¯ãƒ†ãƒƒãƒˆ-ストリーム`。 ãŸã¨ãˆã°ã€ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒå ´åˆ `abcdefg` ã— Firefox ã‚’è¿”ã—ã¾ã™ `データ: アプリケーション/オクテット ストリームã€base64ã€YWJjZGVmZw = =`ã€ã™ãªã‚ã¡ã‚’è¿”ã—ã¾ã™ `データ:; base64ã€YWJjZGVmZw = =`ã€ã‚¯ãƒ­ãƒ ã‚’è¿”ã—ã¾ã™ `データ: < エントリåã®æ‹¡å¼µå­ã«ã‚ˆã£ã¦ mediatype >; base64ã€YWJjZGVmZw = =`. + * `toInternalURL` フォーム `file:///persistent/path/to/entry` (Firefoxã€IE) ã®ãƒ‘スを返ã—ã¾ã™ã€‚ クロム㮠`cdvfile://localhost/persistent/file` フォームã®ãƒ‘スを返ã—ã¾ã™. + +### クロムã®ç™– + + * デãƒã‚¤ã‚¹ã®æº–å‚™ãŒã§ãã¦ã„るイベント後クローム ファイルシステムã¯ã™ãã«æº–å‚™ãŒã§ãã¦ã„ã¾ã™ã€‚回é¿ç­–ã¨ã—ã¦ã¯ `filePluginIsReady` イベントã«ã‚µãƒ–スクライブã§ãã¾ã™ã€‚例: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +`window.isFilePluginReadyRaised` 関数を使用ã—ã¦ã€ã‚¤ãƒ™ãƒ³ãƒˆãŒæ—¢ã«ç™ºç”Ÿã—ãŸã‹ã©ã†ã‹ã‚’確èªã§ãã¾ã™ã€‚ -Chrome ã« window.requestFileSystem ä¸€æ™‚ã¨æ°¸ç¶šçš„ãªãƒ•ァイル ・ システムã®ã‚¯ã‚©ãƒ¼ã‚¿ã®åˆ¶é™ã¯ã‚りã¾ã›ã‚“。 -ã‚¯ãƒ­ãƒ å†…ã®æ°¸ç¶šã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’増加ã™ã‚‹ `window.initPersistentFileSystem` メソッドを呼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ã€‚ 永続的ãªè¨˜æ†¶åŸŸã®ã‚¯ã‚©ãƒ¼ã‚¿ã¯ã€æ—¢å®šã§ã¯ 5 MB ã§ã™ã€‚ クロムãŒå¿…è¦ã§ã™ `--許å¯-ファイル-アクセス--ファイルã‹ã‚‰` `file:///` プロトコル経由ã§ã‚µãƒãƒ¼ãƒˆ API ã«å¼•数を実行ã—ã¾ã™ã€‚ -`ファイル` オブジェクト フラグを使用ã™ã‚‹å ´åˆãªã„変更ã•れã¾ã™ `{create:true}` 既存㮠`エントリ` ã‚’å–å¾—ã™ã‚‹ã¨ã。 -イベント `cancelable` プロパティを設定ã™ã‚‹ã‚¯ãƒ­ãƒ ã®å ´åˆã¯ true。 ã“れ㯠[仕様](http://dev.w3.org/2009/dap/file-system/file-writer.html) ã«åã—ã¦ã€‚ -クロムメッキ㧠`ç¶²` 関数を返ã—ã¾ã™ `ファイルシステム:`-アプリケーションã®ãƒ›ã‚¹ãƒˆã«ã‚ˆã£ã¦ãƒ‘スã®ãƒ—レフィックスã—ã¾ã™ã€‚ ãŸã¨ãˆã°ã€`filesystem:file:///persistent/somefile.txt`ã€`filesystem:http://localhost:8080/persistent/somefile.txt`。 -`toURL` ã®é–¢æ•°ã®çµæžœã«ã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª ã‚¨ãƒ³ãƒˆãƒªå ´åˆæœ«å°¾ã«ã‚¹ãƒ©ãƒƒã‚·ãƒ¥ãŒå«ã¾ã‚Œã¦ã„ã¾ã›ã‚“。 クロムã¯ã€ã‚¹ãƒ©ãƒƒã‚·ãƒ¥å¾Œå¡µ url ã‚’æŒã¤ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãŒæ­£ã—ã解決ã•れるも。 -`resolveLocalFileSystemURL` メソッドã¯ã€å—ä¿¡ `url` ㌠`ファイルシステム` ã®ãƒ—レフィックスãŒå¿…è¦ã§ã™ã€‚ ãŸã¨ãˆã°ã€`resolveLocalFileSystemURL` ã® `url` パラメーター フォーム `filesystem:file:///persistent/somefile.txt` ã§äººé€ äººé–“フォーム `file:///persistent/somefile.txt` ã¨ã¯å¯¾ç…§çš„ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ -廃止ã•れ㟠`toNativeURL` 関数ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã€ã‚¹ã‚¿ãƒ–ã¯ã‚りã¾ã›ã‚“。 -`setMetadata` 関数ã¯ã€ä»•様ã«è¨˜è¼‰ã•れã¦ã„ãªã„ã‚りサãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠SYNTAX_ERR(code: 8) ã®éžå®Ÿåœ¨ã—ãªã„ファイルシステムã®ä¾é ¼ã‚’。 -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠PATH_EXISTS_ERR(code: 12)ã€æŽ’ä»–çš„ãªãƒ•ァイルã¾ãŸã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆã—よã†ã¨ã™ã‚‹ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚ -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠NO_MODIFICATION_ALLOWED_ERR(code: 6) ルート ・ ファイル ・ システム㧠removeRecursively を呼ã³å‡ºã™ã‚’ã—よã†ã¨ã—ã¦ã€‚ -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠NOT_FOUND_ERR(code: 1) [moveto] ディレクトリãŒå­˜åœ¨ã—ãªã„ã‚’ã—よã†ã¨ã—ã¦ã€‚ + +### IndexedDB ベース インプレ癖 (Firefox 㨠IE) + + * `.` `ã§ã™ã€‚` ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + * IE 㯠`file:///` をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“-モード;ホスト モードã®ã¿ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹ (http://localhost:xxxx) ã§ã™ã€‚ + * Firefox ã®ãƒ•ァイルシステムã®ã‚µã‚¤ã‚ºã¯ç„¡åˆ¶é™ã§ã™ãŒå„ 50 MB ã®æ‹¡å¼µæ©Ÿèƒ½ãŒãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’è¦æ±‚ã—ã¾ã™ã€‚ IE10 ã¯æœ€å¤§ 10 mb ã®è¤‡åˆ AppCache 㨠IndexedDB を求ã‚ãšã€ã‚µã‚¤ãƒˆã”ã¨ã« 250 mb ã®æœ€å¤§å€¤ã¾ã§å¢—加を許å¯ã™ã‚‹ã‹ã©ã†ã‹ã‚’ãŸãšã­ã‚‰ã‚Œã¾ã™ãã®ãƒ¬ãƒ™ãƒ«ã«å½“れã°ãƒ•ァイルシステムã®å®Ÿè£…ã§ä½¿ç”¨ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ `RequestFileSystem` 関数㮠`size` パラメーターã¯ã€Firefox 㨠IE ã®ãƒ•ァイルシステムã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。 + * `readAsBinaryString` 関数ã®ä»•様ã«è¨˜è¼‰ã•れã¦ã„ãªã„ã€IE ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã¨ã€ã‚¹ã‚¿ãƒ–ã‚’æŒã£ã¦ã„ã¾ã›ã‚“。 + * `file.type` ã¯ã€å¸¸ã« null ã§ã™ã€‚ + * 削除ã•れ㟠DirectoryEntry インスタンスã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã®çµæžœã‚’使用ã—ã¦ã‚¨ãƒ³ãƒˆãƒªã‚’作æˆã—ãªã„ã§ãã ã•ã„。ãれ以外ã®å ´åˆã¯ã€'ãƒãƒ³ã‚®ãƒ³ã‚°ã®ã‚¨ãƒ³ãƒˆãƒª' ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + * ã¡ã‚‡ã†ã©æ›¸ã‹ã‚Œã¦ã„ãŸã€ãƒ•ァイルを読むã“ã¨ãŒã§ãã¾ã™å‰ã«ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®æ–°ã—ã„インスタンスをå–å¾—ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + * `setMetadata` 関数ã¯ã€ä»•様ã«è¨˜è¼‰ã•れã¦ã„ãªã„ `modificationTime` フィールド変更ã®ã¿ã‚’サãƒãƒ¼ãƒˆã—ã¾ã™ã€‚ + * `copyTo` 㨠`moveTo` 関数ディレクトリをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。 + * ディレクトリã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + * 両方㮠Entry.remove 㨠directoryEntry.removeRecursively ã¯ç©ºã§ãªã„ディレクトリを削除ã™ã‚‹ã¨ãを失敗ã—ãªã„ - 削除ã•れるディレクトリ コンテンツã¨å…±ã«ã‚’掃除ã—ã¦ã„る代ã‚りã«ã€‚ + * `abort` ã—ã€`truncate` 機能ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 + * 進行状æ³ã‚¤ãƒ™ãƒ³ãƒˆã¯èµ·å‹•ã—ã¾ã›ã‚“。ãŸã¨ãˆã°ã€ã“ã®ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ãŒãªã„実行ã•れã¾ã™ã€‚ + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## ノートをアップグレードã—ã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインã®ãƒ‡ãƒ™ãƒ­ãƒƒãƒ‘ーã€å…¬é–‹ã•れãŸä»•様ã«åˆã†ã‚ˆã†ã«ã€`èªè¨¼` 㨠`DirectoryEntry` ã®æ§‹é€ ãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ + +プラグインã®å‰ (pre 1.0.0) ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ•ァイル場所 `エントリ` オブジェクト㮠`fullPath` ãƒ—ãƒ­ãƒ‘ãƒ†ã‚£ã«æ ¼ç´ã•れã¾ã™ã€‚ã“れらã®ãƒ‘スã¯ã‚ˆã†ã«ãªã‚Šã¾ã™é€šå¸¸ + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +ã“れらã®ãƒ‘スã¯ã¾ãŸ `Entry` オブジェクト㮠`toURL()` メソッドã«ã‚ˆã£ã¦è¿”ã•れã¾ã™ã€‚ + +デベロッパーã€`fullPath` 属性㯠*HTML ファイルシステムã®ãƒ«ãƒ¼ãƒˆã«å¯¾ã™ã‚‹ç›¸å¯¾ãƒ‘ス* ã®ãƒ•ァイルã¸ã®ãƒ‘ス。 ã—ãŸãŒã£ã¦ã€ä¸Šè¨˜ã®ãƒ‘スã¯ä»Šä¸¡æ–¹ã® `fullPath` 㨠`FileEntry` オブジェクトã§è¡¨ã•れる + + /path/to/file + + +デãƒã‚¤ã‚¹çµ¶å¯¾ãƒ‘スã¨ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³å‹•ä½œä»¥å‰ `Entry` オブジェクト㮠`fullPath` プロパティを使用ã—ã¦ã“れらã®ãƒ‘スをå–å¾—ã—ãŸå ´åˆã¯ã€ä»£ã‚り㫠`entry.toURL()` を使用ã™ã‚‹ã‚³ãƒ¼ãƒ‰ã‚’æ›´æ–°ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +下ä½äº’æ›æ€§ã€`resolveLocalFileSystemURL()` メソッドã¯ã€ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ‘スをå—ã‘入れるã—ã€ã¯ã€`TEMPORARY` ã¾ãŸã¯ `PERSISTENT` ファイル ・ システム内ã§ãã®ãƒ•ァイルãŒå­˜åœ¨ã™ã‚‹é™ã‚Šã€ãれã«å¯¾å¿œã™ã‚‹ `Entry` オブジェクトを返ã—ã¾ã™ã€‚ + +ã“れã¯ç‰¹ã«ä»¥å‰ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ‘スを使用ã—ã¦ãƒ•ァイル転é€ã®ãƒ—ラグインã§å•題ã¨ãªã£ã¦ã„ã‚‹ (ãã—ã¦ã¾ã ãれらをå—ã‘入れるã“ã¨ãŒã§ãã¾ã™ï¼‰ã€‚ ãれãŒãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã§å‹•作ã™ã‚‹ãƒ—ラグインを得るå•題を解決ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ `entry.toURL()` ã§ `entry.fullPath` ã‚’ç½®ãæ›ãˆã‚‹ã®ã§ã€ãƒ•ァイルシステム㮠Url ã§æ­£å¸¸ã«å‹•作ã«ã‚¢ãƒƒãƒ—デートã•れã¾ã—ãŸã€‚ + +V1.1.0 ã® `toURL()` ã®æˆ»ã‚Šå€¤ã«å¤‰æ›´ã•れã¾ã—㟠(\[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394) ã‚’å‚ç…§) を絶対 'file://' ã§å§‹ã¾ã‚‹ URL ã‚’è¿”ã—ã¾ã™ã€‚ å¯èƒ½ãªé™ã‚Šã€‚ 確ä¿ã™ã‚‹ãŸã‚ã«ã€' cdvfile:'-`toInternalURL()` を今ã™ã使用ã§ãã¾ã™ URL。 ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¯ã€ãƒ•ォームã®ãƒ•ァイルシステム㮠Url ã‚’è¿”ã—ã¾ã™ä»Š + + cdvfile://localhost/persistent/path/to/file + + +ã“れã¯ãƒ•ァイルを一æ„ã«è­˜åˆ¥ã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã§ãã¾ã™ã€‚ + +## エラー ã‚³ãƒ¼ãƒ‰ã¨æ„味ã®ãƒªã‚¹ãƒˆ + +エラーãŒã‚¹ãƒ­ãƒ¼ã•れるã¨ã€æ¬¡ã®ã‚³ãƒ¼ãƒ‰ã®ã„ãšã‚Œã‹ãŒä½¿ç”¨ã•れã¾ã™ã€‚ + +| コード | 定数 | +| ---:|:----------------------------- | +| 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` | + +## (çœç•¥å¯èƒ½) プラグインを構æˆã—ã¾ã™ã€‚ + +利用å¯èƒ½ãªãƒ•ァイルシステムã®ã‚»ãƒƒãƒˆã¯æ§‹æˆã•れãŸãƒ—ラットフォームをã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚IOS 㨠Android ã®ä¸¡æ–¹ã‚’èªè­˜ã—ã¾ã™ã€‚ ã‚¿ã‚° `config.xml` をインストールã™ã‚‹ãƒ•ァイルシステムã®åå‰ã—ã¾ã™ã€‚既定ã§ã¯ã€ã™ã¹ã¦ã®ãƒ•ァイル システムã®ãƒ«ãƒ¼ãƒˆãŒæœ‰åйã«ãªã‚Šã¾ã™ã€‚ + + + + + +### アンドロイド + + * `files`: アプリケーションã®å†…部ファイルã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ディレクトリ + * `files-external`: アプリケーションã®å¤–部ã®ãƒ•ァイルã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ディレクトリ + * `sdcard`:ã€ã‚°ãƒ­ãƒ¼ãƒãƒ«å¤–部ストレージ ディレクトリをファイル (ã“れ㯠SD カードã®ãƒ«ãƒ¼ãƒˆãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹å ´åˆ)。 ã“れを使用ã™ã‚‹ã«ã¯ã€`android.permission.WRITE_EXTERNAL_STORAGE` 権é™ãŒå¿…è¦ã§ã™ã€‚ + * `cache`: アプリケーションã®å†…部キャッシュ ディレクトリ + * `cache-external`: 外部キャッシュã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª + * `root`: デãƒã‚¤ã‚¹å…¨ä½“ã®ãƒ•ァイルシステム + +アンドロイドを「ファイルã€ãƒ•ァイルシステム内ã®"ドキュメント/"サブディレクトリを表ã™"ドキュメント"ã¨ã„ã†åå‰ã®ç‰¹æ®Šãªãƒ•ァイルシステムもサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚ + +### iOS + + * `library`: ライブラリã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª + * `documents`: ドキュメントã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª + * `cache`: キャッシュã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª + * `bundle`: アプリケーションãƒãƒ³ãƒ‰ãƒ«;アプリ自体 (読ã¿å–りã®ã¿) ディスク上ã®å ´æ‰€ + * `root`: デãƒã‚¤ã‚¹å…¨ä½“ã®ãƒ•ァイルシステム + +既定ã§ã¯ã€ãƒ©ã‚¤ãƒ–ラリã¨ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ ディレクトリを iCloud ã«åŒæœŸã§ãã¾ã™ã€‚ 2 ã¤ã®è¿½åŠ ã®ãƒ•ァイルシステムã€`library-nosync` ãŠã‚ˆã³ `documents-nosync` を表ã™ã€ç‰¹åˆ¥ãªéžåŒæœŸãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªå†…ã‚’è¦æ±‚ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€`/Library` ã¾ãŸã¯ `Documents/` ファイルシステム。 \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/ja/index.md b/plugins/cordova-plugin-file/doc/ja/index.md new file mode 100644 index 0000000..57fb7d2 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ja/index.md @@ -0,0 +1,338 @@ + + +# cordova-plugin-file + +ã“ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã¸ã®èª­ã¿å–り/書ãè¾¼ã¿ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹ãƒ•ァイル API を実装ã—ã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインをå«ã‚€ã€ã„ãã¤ã‹ã®ä»•様ã«åŸºã¥ã„ã¦ã„ã¾ã™ï¼šã€HTML5 File API ã® + +(今ã¯äº¡ã) ディレクトリã¨ã‚·ã‚¹ãƒ†ãƒ ã¯ã€æœ€æ–°ã®æ‹¡å¼µæ©Ÿèƒ½: プラグインã®ã‚³ãƒ¼ãƒ‰ã®ã»ã¨ã‚“ã©ã¯ã¨ãã«ã€ä»¥å‰ã®ä»•æ§˜ã«æ›¸ã‹ã‚Œã¦ã„ãŸãŒç¾åœ¨ã¯ï¼š + +FileWriter 仕様も実装ã—ã¦ã„ã¾ã™: + +使用法をå‚ç…§ã—ã¦ãã ã•ã„ HTML5 岩 ' 優秀ãª[ファイルシステム記事][1]。 + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +ä»–ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ã‚ªãƒ—ã‚·ãƒ§ãƒ³ã®æ¦‚è¦ã«ã¤ã„ã¦ã¯ã€ã‚³ãƒ«ãƒ‰ãƒã®[ストレージ ・ ガイド][2]ã‚’å‚ç…§ã—ã¦ãã ã•ã„。. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `cordova.file` オブジェクトを定義ã—ã¾ã™ã€‚ + +グローãƒãƒ« スコープã§ã¯ã‚ã‚‹ãŒãれãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã§ã™ã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## インストール + + cordova plugin add cordova-plugin-file + + +## サãƒãƒ¼ãƒˆã•れã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* Firefox ã® OS +* iOS +* Windows Phone 7 㨠8 * +* Windows 8 * +* ブラウザー + +* *`FileReader.readAsArrayBuffer` ã‚‚ `FileWriter.write(blob)` ã‚‚ã“れらã®ãƒ—ラットフォームã¯ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“*。 + +## ファイルをä¿å­˜ã™ã‚‹å ´æ‰€ + +V1.2.0ã€ç¾åœ¨é‡è¦ãªãƒ•ァイル システム ディレクトリã¸ã® Url ã‚’æä¾›ã—ã¦ã„ã¾ã™ã€‚ å„ URL ã¯ãƒ•ォーム *file:///path/to/spot/* ã§ã€`window.resolveLocalFileSystemURL()` を使用ã™ã‚‹ `DirectoryEntry` ã«å¤‰æ›ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚. + +* `cordova.file.applicationDirectory`-読ã¿å–り専用ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ã€ã‚¢ãƒ—リケーションãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã™ã€‚(*iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + +* `cordova.file.applicationStorageDirectory`-アプリケーションã®ã‚µãƒ³ãƒ‰ ボックス; ã®ãƒ«ãƒ¼ãƒˆ ディレクトリiOS ã§ã“ã®å ´æ‰€ãŒèª­ã¿å–り専用 (特定ã®ã‚µãƒ–ディレクトリ㌠[ã®ã‚ˆã†ãª `/Documents` ] ã¯ã€èª­ã¿å–り/書ãè¾¼ã¿)。 内ã«å«ã¾ã‚Œã‚‹ã™ã¹ã¦ã®ãƒ‡ãƒ¼ã‚¿ã¯ã€ã‚¢ãƒ—リケーションã«ãƒ—ライベートã§ã™ã€‚ ( *iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + +* `cordova.file.dataDirectory`内部メモリを使用ã—ã¦ã€ã‚¢ãƒ—リケーションã®ã‚µãƒ³ãƒ‰ ãƒœãƒƒã‚¯ã‚¹å†…ã§æ°¸ç¶šãªãƒ—ライベート データ ストレージ (外部メモリを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚‹å ´åˆä½¿ç”¨ã—㦠Android 上㧠`.externalDataDirectory` )。 IOS ã¯ã€ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ iCloud ã¨åŒæœŸã•れã¾ã›ã‚“ (使用ã™ã‚‹ `.syncedDataDirectory` )。 (*iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + +* `cordova.file.cacheDirectory`-キャッシュã•れãŸãƒ‡ãƒ¼ã‚¿ ファイルやアプリã«ç°¡å˜ã«å†ä½œæˆã§ãã‚‹ä»»æ„ã®ãƒ•ァイルã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã€‚ ストレージ デãƒã‚¤ã‚¹ãŒä¸è¶³ã—ãŸã¨ãã«ã€OS ãŒã“れらã®ãƒ•ァイルを削除å¯èƒ½æ€§ãŒã‚りã¾ã™ã€ãれã«ã‚‚ã‹ã‹ã‚らãšã€ã‚¢ãƒ—リã¯ã“ã“ã«ãƒ•ァイルを削除ã™ã‚‹ OS ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„。 (*iOS*ã€*アンドロイド*ã€*ブラックベリー 10*) + +* `cordova.file.externalApplicationStorageDirectory`外部ストレージã®ã‚¢ãƒ—リケーション領域。(*アンドロイド*) + +* `cordova.file.externalDataDirectory`-外部ストレージ上ã®ã‚¢ãƒ—リ固有ã®ãƒ‡ãƒ¼ã‚¿ ファイルをé…ç½®ã™ã‚‹å ´æ‰€ã€‚(*アンドロイド*) + +* `cordova.file.externalCacheDirectory`外部ストレージã«ã‚¢ãƒ—リケーション キャッシュ。(*アンドロイド*) + +* `cordova.file.externalRootDirectory`-外部ストレージ (SD カード) ルート。(*アンドロイド*ã€*ブラックベリー 10*) + +* `cordova.file.tempDirectory`-OS をクリアã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ temp ディレクトリãŒã€‚ ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª; オフ㫠OS ã«ä¾å­˜ã—ã¾ã›ã‚“。アプリãŒå¸¸ã«è©²å½“ã™ã‚‹ãƒ•ァイルを削除ã—ã¾ã™ã€‚ (*iOS*) + +* `cordova.file.syncedDataDirectory`-(例ãˆã° iCloud) ã«åŒæœŸã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã‚¢ãƒ—リケーション固有ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ä¿æŒã—ã¾ã™ã€‚(*iOS*) + +* `cordova.file.documentsDirectory`-ファイルã€ã‚¢ãƒ—リケーションã«ãƒ—ライベートã¯ä»–ã®ã‚¢ãƒ—リケーション (Office ファイルãªã©ï¼‰ ã‚’æ„味ã§ã™ã€‚(*iOS*) + +* `cordova.file.sharedDirectory`ã™ã¹ã¦ã®ã‚¢ãƒ—リケーション (*ブラックベリー 10*ã«ã‚°ãƒ­ãƒ¼ãƒãƒ«ã«ä½¿ç”¨ã§ãるファイル) + +## ファイル ・ システム ・ レイアウト + +技術的ã«å®Ÿè£…ã®è©³ç´°ã€éžå¸¸ã«ã©ã®ã‚ˆã†ã« `cordova.file.*` プロパティã¯ã€å®Ÿéš›ã®ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ç‰©ç†ãƒ‘スã«ãƒžãƒƒãƒ—を知ã£ã¦ãŠãã¨ä¾¿åˆ©ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + +### iOS ファイル システムã®ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆ + +| デãƒã‚¤ã‚¹ ・ パス | `cordova.file.*` | `iosExtraFileSystems` | r/w ã§ã™ã‹ï¼Ÿ | 永続的ãªã§ã™ã‹ï¼Ÿ | OS を消去ã—ã¾ã™ | åŒæœŸ | プライベート | +|:------------------------------------ |:--------------------------- |:--------------------- |:--------:|:--------:|:----------:|:------:|:------:| +| `/var/モãƒã‚¤ãƒ«/アプリケーション/< UUID >/` | applicationStorageDirectory | - | r | N/A | N/A | N/A | ã¯ã„ | +|    `appname.app/` | ディレクトリ | ãƒãƒ³ãƒ‰ãƒ« | r | N/A | N/A | N/A | ã¯ã„ | +|       `www/` | - | - | r | N/A | N/A | N/A | ã¯ã„ | +|    `Documents/` | documentsDirectory | ドキュメント | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | ã¯ã„ | +|       `NoCloud/` | - | ドキュメント nosync | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| ã¯ã„ | +|    `Library` | - | ライブラリ | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ã§ã™ã‹ï¼Ÿ | ã¯ã„ | +|       `NoCloud/` | dataDirectory | ライブラリ nosync | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| ã¯ã„ | +|       `Cloud/` | syncedDataDirectory | - | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | ã¯ã„ | +|       `Caches/` | cacheDirectory | キャッシュ | r/w | ã¯ã„ * | ã¯ã„ * * *| | ã„ã„㈠| ã¯ã„ | +|    `tmp/` | tempDirectory | - | r/w | ã„ã„㈠* * | ã¯ã„ * * *| | ã„ã„㈠| ã¯ã„ | + +* アプリをå†èµ·å‹•ã—ã€ã‚¢ãƒƒãƒ—グレードã¨ã®é–“ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ä¿æŒãŒã€OS を希望ã™ã‚‹ãŸã³ã«ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’削除ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚アプリを削除å¯èƒ½æ€§ãŒã‚りã¾ã™ä»»æ„ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å†ä½œæˆã™ã‚‹ã“ã¨ãŒã§ãã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +* * ファイル アプリケーションå†èµ·å‹•を渡ã£ã¦ç¶šãã“ã¨ãŒã‚りã¾ã™ãŒã€ã“ã®å‹•作ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„。 ファイルã¯ã€æ›´æ–°ã‚’ç¶­æŒã™ã‚‹ã¯ä¿è¨¼ã•れã¾ã›ã‚“。 アプリãŒè©²å½“ã™ã‚‹å ´åˆã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‹ã‚‰ãƒ•ァイルを削除ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€ã“れらã®ãƒ•ァイルãŒå‰Šé™¤ã•れるã¨ã (ã¾ãŸã¯å ´åˆã§ã‚‚)ã€OS ã¯ä¿è¨¼ã—ã¾ã›ã‚“。 + +* * *| OS ã¯ãれã€å¿…è¦ã ã¨æ„Ÿã˜ã¦ã„ã‚‹ã¨ãã«ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容を消去å¯èƒ½æ€§ãŒã‚りã¾ã™ãŒã€ã“れã«ä¾å­˜ã—ã¾ã›ã‚“。 ã“ã®é©åˆ‡ãªãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«ã€ã‚¢ãƒ—リケーションをオフã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +### 人造人間ファイル ・ システム ・ レイアウト + +| デãƒã‚¤ã‚¹ ・ パス | `cordova.file.*` | `AndroidExtraFileSystems` | r/w ã§ã™ã‹ï¼Ÿ | 永続的ãªã§ã™ã‹ï¼Ÿ | OS を消去ã—ã¾ã™ | プライベート | +|:--------------------------------- |:----------------------------------- |:------------------------- |:--------:|:--------:|:---------:|:------:| +| `file:///android_asset/` | ディレクトリ | | r | N/A | N/A | ã¯ã„ | +| `/データ/データ/< app id >/` | applicationStorageDirectory | - | r/w | N/A | N/A | ã¯ã„ | +|    `cache` | cacheDirectory | キャッシュ | r/w | ã¯ã„ | ã¯ã„ * | ã¯ã„ | +|    `files` | dataDirectory | ファイル | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | +|       `Documents` | | ドキュメント | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | +| `< sd カード >/` | externalRootDirectory | sd カード | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| +|       `cache` | externalCacheDirectry | 外部キャッシュ | r/w | ã¯ã„ | ã„ã„㈠* * | ã„ã„㈠| +|       `files` | externalDataDirectory | 外部ファイル | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| + +* OS ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’å®šæœŸçš„ã«æ¶ˆåŽ»å¯èƒ½æ€§ãŒã‚りã¾ã™ãŒã€ã“ã®å‹•作ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„。 アプリケーションã®å¿…è¦ã«å¿œã˜ã¦ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容をオフã«ã—ã¾ã™ã€‚ ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ‰‹å‹•ã§ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’削除ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容ãŒå‰Šé™¤ã•れã¾ã™ã€‚ + +* * OS ã¯ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯è‡ªå‹•çš„ã«ã‚¯ãƒªã‚¢ã•れã¾ã›ã‚“自分ã§ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を管ç†ã™ã‚‹ãŸã‚ã«è²¬ä»»ãŒã‚りã¾ã™ã€‚ ãƒ¦ãƒ¼ã‚¶ã¯æ‰‹å‹•ã§ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’消去ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®å†…容ãŒå‰Šé™¤ã•れã¾ã™ã€‚ + +**注**: 外部記憶装置をマウントã§ããªã„å ´åˆã¯ã€`cordova.file.external*` プロパティを `null`. + +### ブラックベリー 10 ファイル ・ システム ・ レイアウト + +| デãƒã‚¤ã‚¹ ・ パス | `cordova.file.*` | r/w ã§ã™ã‹ï¼Ÿ | 永続的ãªã§ã™ã‹ï¼Ÿ | OS を消去ã—ã¾ã™ | プライベート | +|:------------------------------------------------- |:--------------------------- |:--------:|:--------:|:---------:|:------:| +| `file:///accounts/1000/appdata/< app id >/` | applicationStorageDirectory | r | N/A | N/A | ã¯ã„ | +|    `app/native` | ディレクトリ | r | N/A | N/A | ã¯ã„ | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | ã„ã„㈠| ã¯ã„ | ã¯ã„ | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | ã¯ã„ | ã„ã„㈠| ã¯ã„ | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| +| `file:///accounts/1000/shared` | sharedDirectory | r/w | ã¯ã„ | ã„ã„㈠| ã„ã„㈠| + +*注*: ã™ã¹ã¦ã®ãƒ‘スã¯/accounts/1000-enterprise 基準ã«å¢ƒç•Œã‚’動作ã™ã‚‹ã‚ˆã†ã«ã‚¢ãƒ—リケーションを展開ã™ã‚‹ã¨ã。 + +## Android ã®ç™– + +### Android ã®æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã®å ´æ‰€ + +Android ã®ãƒ‡ãƒã‚¤ã‚¹ã«æ°¸ç¶šçš„ãªãƒ•ァイルを格ç´ã™ã‚‹è¤‡æ•°ã®æœ‰åйãªå ´æ‰€ãŒã‚りã¾ã™ã€‚ ã•ã¾ã–ã¾ãªå¯èƒ½æ€§ã«ã¤ã„ã¦åºƒç¯„ãªè­°è«–ã®ãŸã‚ã® [ã“ã®ãƒšãƒ¼ã‚¸][3] ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +以å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ—ラグインã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã® SD カード (ã¾ãŸã¯åŒç­‰ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ パーティション) マウントã•れã¦ã„ãŸã¨ä¸»å¼µã—ãŸã‹ã©ã†ã‹ã«åŸºã¥ã„ã¦ã€èµ·å‹•時ã«ä¸€æ™‚ã¨æ°¸ç¶šçš„ãªãƒ•ァイルã®å ´æ‰€ã‚’é¸ã¶ã§ã—ょã†ã€‚ SD カードãŒãƒžã‚¦ãƒ³ãƒˆã•れã¦ã„ã‚‹å ´åˆã€ã¾ãŸã¯å¤§è¦æ¨¡ãªå†…部ストレージ パーティションãŒåˆ©ç”¨å¯èƒ½ãªå ´åˆ (よã†ãƒã‚¯ã‚µã‚¹ デãƒã‚¤ã‚¹ä¸Šï¼‰ ã—ã€æ°¸ç¶šçš„ãªãƒ•ァイルã¯ã€ãã®é ˜åŸŸã®ãƒ«ãƒ¼ãƒˆã«æ ¼ç´ã•れã¾ã™ã€‚ ã“れã¯ã™ã¹ã¦ã® Cordova アプリ見るã“ã¨ãŒã§ãる利用å¯èƒ½ãªãƒ•ァイルã®ã™ã¹ã¦ã®ã‚«ãƒ¼ãƒ‰ã«æ„味ã—ã¾ã—ãŸã€‚ + +SD カードãŒãªã„å ´åˆã€ä»¥å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒãƒ‡ãƒ¼ã‚¿ã‚’æ ¼ç´ã™ã‚‹ `/data/data/`ã€ãŠäº’ã„ã‹ã‚‰ã‚¢ãƒ—リを分離ã™ã‚‹ãŒã€ã¾ã åŽŸå› å¯èƒ½æ€§ãŒã‚りã¾ã™ãƒ¦ãƒ¼ã‚¶ãƒ¼é–“ã§å…±æœ‰ã™ã‚‹ãƒ‡ãƒ¼ã‚¿ã€‚ + +内部ファイルã®ä¿å­˜å ´æ‰€ã‚„アプリケーション㮠`config.xml` ファイルã«å„ªå…ˆé †ä½ã‚’æŒã¤ä»¥å‰ã®ãƒ­ã‚¸ãƒƒã‚¯ã‚’使用ã—ã¦ãƒ•ァイルをä¿å­˜ã™ã‚‹ã‹ã©ã†ã‹ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ä»Šã€‚ ã“れを行ã†ã«ã€`config.xml` ã«æ¬¡ã® 2 行ã®ã„ãšã‚Œã‹ã‚’追加ã—ã¾ã™ã€‚ + + + + + + +ã“ã®è¡ŒãŒãªã‘れã°ãƒ•ァイル プラグインã¯ãƒ‡ãƒ•ォルトã¨ã—㦠`Compatibility` を使用ã—ã¾ã™ã€‚優先タグãŒå­˜åœ¨ã—ã€ã“れらã®å€¤ã® 1 ã¤ã§ã¯ãªã„å ´åˆã€ã‚¢ãƒ—リケーションã¯èµ·å‹•ã—ã¾ã›ã‚“。 + +アプリケーションã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ä»¥å‰å‡ºè·ã•れã¦ã„ã‚‹å ´åˆã€å¤ã„ (å‰ 1.0) を使用ã—ã¦ã€ã“ã®ãƒ—ラグインã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã—ã€æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ä¿å­˜ã•れã¦ã„るファイルã«ã¯ `Compatibility` を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 自分ã®ã‚¢ãƒ—リケーションをアップグレードã™ã‚‹æ—¢å­˜ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’彼らã®è£…ç½®ã«ã‚ˆã£ã¦ã€ä»¥å‰ã«ä¿å­˜ã•れãŸãƒ•ァイルã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã“ã¨ãŒã‚ã‚‹ã“ã¨ã‚’æ„味ã™ã‚‹ã€Œå†…部ã€ã«å ´æ‰€ã‚’スイッãƒãƒ³ã‚°ã—ã¾ã™ã€‚ + +アプリケーションã¯ã€æ–°ã—ã„ã€ã¾ãŸã¯æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ ¼ç´ã•れ以å‰ã¯æ±ºã—ã¦å ´åˆã€`内部` è¨­å®šä¸€èˆ¬çš„ã«æŽ¨å¥¨ã•れã¾ã™ã€‚ + +## iOS ã®ç™– + +* `cordova.file.applicationStorageDirectory`読ã¿å–り専用;ルート ディレクトリ内ã®ãƒ•ァイルをä¿å­˜ã—よã†ã¯å¤±æ•—ã—ã¾ã™ã€‚ ä»–ã® 1 ã¤ã‚’使用ã—㦠`cordova.file.*` iOS ã®ãŸã‚定義ã•れã¦ã„るプロパティ (ã®ã¿ `applicationDirectory` 㨠`applicationStorageDirectory` ã¯èª­ã¿å–り専用)。 +* `FileReader.readAsText(blob, encoding)` + * `encoding`パラメーターã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã—ã€utf-8 エンコーディングãŒå¸¸ã«æœ‰åйã§ã™ã€‚ + +### iOS ã®æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã®å ´æ‰€ + +IOS デãƒã‚¤ã‚¹ã«æ°¸ç¶šçš„ãªãƒ•ァイルを格ç´ã™ã‚‹ 2 ã¤ã®æœ‰åйãªå ´æ‰€ãŒã‚ã‚‹: ドキュメントã¨ãƒ©ã‚¤ãƒ–ラリã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã€‚ プラグインã®ä»¥å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯ã€å”¯ä¸€ã®ã“れã¾ã§ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æ°¸ç¶šçš„ãªãƒ•ァイルを格ç´ã•れã¾ã™ã€‚ ã“れã¯ã€ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®ç›®çš„ã¯ã€è¼¸å‡ºã®ãŸã‚ã®å®Œå…¨ãªãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’作æˆã™ã‚‹ã®ã§ã¯ãªããªã‹ã£ãŸãŒã—ã°ã—ã°æ„図ã•れã¦ã„ãŸã‚Šã€ç‰¹ã«å¤šæ•°ã®å°ã•ã„ファイルを処ç†ã™ã‚‹ã‚¢ãƒ—リケーションã®å ´åˆã€iTunes ã«è¡¨ç¤ºã•れã¦ã„ã‚‹ã™ã¹ã¦ã®ã‚¢ãƒ—リケーションã®ãƒ•ァイルを作るã®å‰¯ä½œç”¨ãŒã‚ã£ãŸã€‚ + +ドキュメントã¾ãŸã¯ã‚¢ãƒ—リケーション㮠`config.xml` ファイルã«å„ªå…ˆé †ä½ã®ãƒ©ã‚¤ãƒ–ラリ ディレクトリã«ãƒ•ァイルをä¿å­˜ã™ã‚‹ã‹ã©ã†ã‹ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ä»Šã€‚ ã“れを行ã†ã«ã€`config.xml` ã«æ¬¡ã® 2 行ã®ã„ãšã‚Œã‹ã‚’追加ã—ã¾ã™ã€‚ + + + + + + +ã“ã®è¡ŒãŒãªã‘れã°ãƒ•ァイル プラグインã¯ãƒ‡ãƒ•ォルトã¨ã—㦠`Compatibility` を使用ã—ã¾ã™ã€‚優先タグãŒå­˜åœ¨ã—ã€ã“れらã®å€¤ã® 1 ã¤ã§ã¯ãªã„å ´åˆã€ã‚¢ãƒ—リケーションã¯èµ·å‹•ã—ã¾ã›ã‚“。 + +アプリケーションã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ä»¥å‰å‡ºè·ã•れã¦ã„ã‚‹å ´åˆã€å¤ã„ (å‰ 1.0) を使用ã—ã¦ã€ã“ã®ãƒ—ラグインã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã—ã€æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ä¿å­˜ã•れã¦ã„るファイルã«ã¯ `Compatibility` を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 自分ã®ã‚¢ãƒ—リケーションをアップグレードã™ã‚‹æ—¢å­˜ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’以å‰ã«ä¿å­˜ã•れãŸãƒ•ァイルã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã ã‚ã†ã“ã¨ã‚’æ„味ã™ã‚‹ `Library` ã«å ´æ‰€ã‚’スイッãƒãƒ³ã‚°ã—ã¾ã™ã€‚ + +アプリケーションã¯ã€æ–°ã—ã„ã€ã¾ãŸã¯æ°¸ç¶šçš„ãªãƒ•ァイルシステムã«ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ ¼ç´ã•れ以å‰ã¯æ±ºã—ã¦å ´åˆã€`Library` è¨­å®šä¸€èˆ¬çš„ã«æŽ¨å¥¨ã•れã¾ã™ã€‚ + +## Firefox OS ç™– + +ファイル システム API Firefox OS ã§ãƒã‚¤ãƒ†ã‚£ãƒ– サãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã¨ã€indexedDB ã®ä¸Šã«ã‚·ãƒ ã¨ã—ã¦å®Ÿè£…ã•れã¦ã„ã¾ã™ã€‚ + +* 空ã§ãªã„ディレクトリを削除ã™ã‚‹ã¨ãã«å¤±æ•—ã—ã¾ã›ã‚“ +* ディレクトリã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“ +* 方法 `copyTo` 㨠`moveTo` ディレクトリをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“ + +次ã®ãƒ‡ãƒ¼ã‚¿ パスãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™: * `applicationDirectory` - `xhr` を使用ã—ã¦ã€ã‚¢ãƒ—リケーションã¨å…±ã«ãƒ‘ッケージ化ã•れるローカル ファイルをå–å¾—ã—ã¾ã™ã€‚ * `dataDirectory` - 永続的ãªã‚¢ãƒ—リケーション固有ã®ãƒ‡ãƒ¼ã‚¿ ファイル。 * `cacheDirectory` - アプリケーションã®å†èµ·å‹•後も維æŒã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã‚­ãƒ£ãƒƒã‚·ãƒ¥ ファイル (アプリã¯ã“ã“ã«ãƒ•ァイルを削除ã™ã‚‹ OS ã«ä¾å­˜ã—ãªã„ã§ãã ã•ã„)。 + +## ブラウザーã®ç™– + +### 共通ã®ç™–ã¨ç™ºè¨€ + +* å„ブラウザーã¯ã‚µãƒ³ãƒ‰ ボックス化ã•れãŸãƒ•ァイルシステムを使用ã—ã¾ã™ã€‚IE 㨠Firefox IndexedDB をベースã¨ã—ã¦ä½¿ç”¨ã—ã¾ã™ã€‚ã™ã¹ã¦ã®ãƒ–ラウザーã¯ã€ãƒ‘スã«ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®åŒºåˆ‡ã‚Šè¨˜å·ã¨ã—ã¦ã‚¹ãƒ©ãƒƒã‚·ãƒ¥ã‚’使用ã—ã¾ã™ã€‚ +* ディレクトリ エントリã¯é€£ç¶šã—ã¦ä½œæˆã•れãªã‘れã°ãªã‚Šã¾ã›ã‚“。 ãŸã¨ãˆã°ã€ã‚³ãƒ¼ãƒ« `fs.root.getDirectory ('dir1 dir2'ã€{create:true}ã€successCallbackã€è§£ã‚Š)` dir1 ãŒå­˜åœ¨ã—ãªã‹ã£ãŸå ´åˆã¯å¤±æ•—ã—ã¾ã™ã€‚ +* プラグインã¯ã€æ°¸ç¶šçš„ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã®æœ€åˆã®èµ·å‹•時ã«ä½¿ç”¨ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’è¦æ±‚ã—ã¾ã™ã€‚ +* プラグインã¯ã€`cdvfile://localhost` (ローカル リソース) をサãƒãƒ¼ãƒˆã—ã¦ã„ã‚‹ã ã‘ã§ã™ã€‚ã™ãªã‚ã¡å¤–部リソースã¯ã€`cdvfile` を介ã—ã¦ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“. +* プラグイン㫠[制é™ã€ã®ãƒ•ァイル システム API ã® 8.3 命åè¦å‰‡][4] ã«å¾“ã£ã¦ã„ã¾ã›ã‚“。. +* Blob ãŠã‚ˆã³ãƒ•ァイル ' `close` 関数ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 +* `FileSaver` 㨠`BlobBuilder` ã“ã®ãƒ—ラグインã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„スタブをæŒã£ã¦ã„ã¾ã›ã‚“。 +* プラグインã¯ã€`requestAllFileSystems` をサãƒãƒ¼ãƒˆã—ã¾ã›ã‚“。ã“ã®é–¢æ•°ã¯ã€ä»•様ã§è¡Œæ–¹ä¸æ˜Žã«ã‚‚。 +* ディレクトリ内ã®ã‚¨ãƒ³ãƒˆãƒªã‚’使用ã™ã‚‹ã¨å‰Šé™¤ã•れã¾ã›ã‚“ `create: true` 既存ディレクトリã®ãƒ•ラグ。 +* コンス トラクターã§ä½œæˆã•れãŸãƒ•ァイルã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。代ã‚り㫠entry.file メソッドを使用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ +* å„ブラウザー㯠blob URL å‚ç…§ã®ç‹¬è‡ªã®ãƒ•ォームを使用ã—ã¾ã™ã€‚ +* `readAsDataURL` 関数ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ãŒã‚¯ãƒ­ãƒ ãƒ¡ãƒƒã‚­ã§ mediatype エントリåã®æ‹¡å¼µå­ã«ã‚ˆã£ã¦ç•°ãªã‚Šã¾ã™ã€IE ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã®ç¨®é¡žã¯ã€å¸¸ã«ç©º (`text-plain` ã«å¾“ã£ã¦ã€ä»•様ã¨åŒã˜ã§ã‚る)ã€Firefox ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã®ç¨®é¡žã¯å¸¸ã« `アプリケーションã¾ãŸã¯ã‚ªã‚¯ãƒ†ãƒƒãƒˆ-ストリーム`。 ãŸã¨ãˆã°ã€ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒå ´åˆ `abcdefg` ã— Firefox ã‚’è¿”ã—ã¾ã™ `データ: アプリケーション/オクテット ストリームã€base64ã€YWJjZGVmZw = =`ã€ã™ãªã‚ã¡ã‚’è¿”ã—ã¾ã™ `データ:; base64ã€YWJjZGVmZw = =`ã€ã‚¯ãƒ­ãƒ ã‚’è¿”ã—ã¾ã™ `データ: < エントリåã®æ‹¡å¼µå­ã«ã‚ˆã£ã¦ mediatype >; base64ã€YWJjZGVmZw = =`. +* `toInternalURL` フォーム `file:///persistent/path/to/entry` (Firefoxã€IE) ã®ãƒ‘スを返ã—ã¾ã™ã€‚ クロム㮠`cdvfile://localhost/persistent/file` フォームã®ãƒ‘スを返ã—ã¾ã™. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### クロムã®ç™– + +* デãƒã‚¤ã‚¹ã®æº–å‚™ãŒã§ãã¦ã„るイベント後クローム ファイルシステムã¯ã™ãã«æº–å‚™ãŒã§ãã¦ã„ã¾ã™ã€‚回é¿ç­–ã¨ã—ã¦ã¯ `filePluginIsReady` イベントã«ã‚µãƒ–スクライブã§ãã¾ã™ã€‚例: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +`window.isFilePluginReadyRaised` 関数を使用ã—ã¦ã€ã‚¤ãƒ™ãƒ³ãƒˆãŒæ—¢ã«ç™ºç”Ÿã—ãŸã‹ã©ã†ã‹ã‚’確èªã§ãã¾ã™ã€‚ -Chrome ã« window.requestFileSystem ä¸€æ™‚ã¨æ°¸ç¶šçš„ãªãƒ•ァイル ・ システムã®ã‚¯ã‚©ãƒ¼ã‚¿ã®åˆ¶é™ã¯ã‚りã¾ã›ã‚“。 -ã‚¯ãƒ­ãƒ å†…ã®æ°¸ç¶šã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’増加ã™ã‚‹ `window.initPersistentFileSystem` メソッドを呼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ã€‚ 永続的ãªè¨˜æ†¶åŸŸã®ã‚¯ã‚©ãƒ¼ã‚¿ã¯ã€æ—¢å®šã§ã¯ 5 MB ã§ã™ã€‚ クロムãŒå¿…è¦ã§ã™ `--許å¯-ファイル-アクセス--ファイルã‹ã‚‰` `file:///` プロトコル経由ã§ã‚µãƒãƒ¼ãƒˆ API ã«å¼•数を実行ã—ã¾ã™ã€‚ -`ファイル` オブジェクト フラグを使用ã™ã‚‹å ´åˆãªã„変更ã•れã¾ã™ `{create:true}` 既存㮠`エントリ` ã‚’å–å¾—ã™ã‚‹ã¨ã。 -イベント `cancelable` プロパティを設定ã™ã‚‹ã‚¯ãƒ­ãƒ ã®å ´åˆã¯ true。 ã“れ㯠[仕様][5] ã«åã—ã¦ã€‚ -クロムメッキ㧠`ç¶²` 関数を返ã—ã¾ã™ `ファイルシステム:`-アプリケーションã®ãƒ›ã‚¹ãƒˆã«ã‚ˆã£ã¦ãƒ‘スã®ãƒ—レフィックスã—ã¾ã™ã€‚ ãŸã¨ãˆã°ã€`filesystem:file:///persistent/somefile.txt`ã€`filesystem:http://localhost:8080/persistent/somefile.txt`。 -`toURL` ã®é–¢æ•°ã®çµæžœã«ã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª ã‚¨ãƒ³ãƒˆãƒªå ´åˆæœ«å°¾ã«ã‚¹ãƒ©ãƒƒã‚·ãƒ¥ãŒå«ã¾ã‚Œã¦ã„ã¾ã›ã‚“。 クロムã¯ã€ã‚¹ãƒ©ãƒƒã‚·ãƒ¥å¾Œå¡µ url ã‚’æŒã¤ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãŒæ­£ã—ã解決ã•れるも。 -`resolveLocalFileSystemURL` メソッドã¯ã€å—ä¿¡ `url` ㌠`ファイルシステム` ã®ãƒ—レフィックスãŒå¿…è¦ã§ã™ã€‚ ãŸã¨ãˆã°ã€`resolveLocalFileSystemURL` ã® `url` パラメーター フォーム `filesystem:file:///persistent/somefile.txt` ã§äººé€ äººé–“フォーム `file:///persistent/somefile.txt` ã¨ã¯å¯¾ç…§çš„ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ -廃止ã•れ㟠`toNativeURL` 関数ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ã€ã‚¹ã‚¿ãƒ–ã¯ã‚りã¾ã›ã‚“。 -`setMetadata` 関数ã¯ã€ä»•様ã«è¨˜è¼‰ã•れã¦ã„ãªã„ã‚りサãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠SYNTAX_ERR(code: 8) ã®éžå®Ÿåœ¨ã—ãªã„ファイルシステムã®ä¾é ¼ã‚’。 -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠PATH_EXISTS_ERR(code: 12)ã€æŽ’ä»–çš„ãªãƒ•ァイルã¾ãŸã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆã—よã†ã¨ã™ã‚‹ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚ -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠NO_MODIFICATION_ALLOWED_ERR(code: 6) ルート ・ ファイル ・ システム㧠removeRecursively を呼ã³å‡ºã™ã‚’ã—よã†ã¨ã—ã¦ã€‚ -INVALID_MODIFICATION_ERR (コード: 9) ã®ä»£ã‚りã«ã‚¹ãƒ­ãƒ¼ã•れ㟠NOT_FOUND_ERR(code: 1) [moveto] ディレクトリãŒå­˜åœ¨ã—ãªã„ã‚’ã—よã†ã¨ã—ã¦ã€‚ + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### IndexedDB ベース インプレ癖 (Firefox 㨠IE) + +* `.` `ã§ã™ã€‚` ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 +* IE 㯠`file:///` をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“-モード;ホスト モードã®ã¿ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹ (http://localhost:xxxx) ã§ã™ã€‚ +* Firefox ã®ãƒ•ァイルシステムã®ã‚µã‚¤ã‚ºã¯ç„¡åˆ¶é™ã§ã™ãŒå„ 50 MB ã®æ‹¡å¼µæ©Ÿèƒ½ãŒãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’è¦æ±‚ã—ã¾ã™ã€‚ IE10 ã¯æœ€å¤§ 10 mb ã®è¤‡åˆ AppCache 㨠IndexedDB を求ã‚ãšã€ã‚µã‚¤ãƒˆã”ã¨ã« 250 mb ã®æœ€å¤§å€¤ã¾ã§å¢—加を許å¯ã™ã‚‹ã‹ã©ã†ã‹ã‚’ãŸãšã­ã‚‰ã‚Œã¾ã™ãã®ãƒ¬ãƒ™ãƒ«ã«å½“れã°ãƒ•ァイルシステムã®å®Ÿè£…ã§ä½¿ç”¨ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ `RequestFileSystem` 関数㮠`size` パラメーターã¯ã€Firefox 㨠IE ã®ãƒ•ァイルシステムã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。 +* `readAsBinaryString` 関数ã®ä»•様ã«è¨˜è¼‰ã•れã¦ã„ãªã„ã€IE ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„ã¨ã€ã‚¹ã‚¿ãƒ–ã‚’æŒã£ã¦ã„ã¾ã›ã‚“。 +* `file.type` ã¯ã€å¸¸ã« null ã§ã™ã€‚ +* 削除ã•れ㟠DirectoryEntry インスタンスã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã®çµæžœã‚’使用ã—ã¦ã‚¨ãƒ³ãƒˆãƒªã‚’作æˆã—ãªã„ã§ãã ã•ã„。ãれ以外ã®å ´åˆã¯ã€'ãƒãƒ³ã‚®ãƒ³ã‚°ã®ã‚¨ãƒ³ãƒˆãƒª' ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ +* ã¡ã‚‡ã†ã©æ›¸ã‹ã‚Œã¦ã„ãŸã€ãƒ•ァイルを読むã“ã¨ãŒã§ãã¾ã™å‰ã«ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®æ–°ã—ã„インスタンスをå–å¾—ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ +* `setMetadata` 関数ã¯ã€ä»•様ã«è¨˜è¼‰ã•れã¦ã„ãªã„ `modificationTime` フィールド変更ã®ã¿ã‚’サãƒãƒ¼ãƒˆã—ã¾ã™ã€‚ +* `copyTo` 㨠`moveTo` 関数ディレクトリをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。 +* ディレクトリã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 +* 両方㮠Entry.remove 㨠directoryEntry.removeRecursively ã¯ç©ºã§ãªã„ディレクトリを削除ã™ã‚‹ã¨ãを失敗ã—ãªã„ - 削除ã•れるディレクトリ コンテンツã¨å…±ã«ã‚’掃除ã—ã¦ã„る代ã‚りã«ã€‚ +* `abort` ã—ã€`truncate` 機能ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 +* 進行状æ³ã‚¤ãƒ™ãƒ³ãƒˆã¯èµ·å‹•ã—ã¾ã›ã‚“。ãŸã¨ãˆã°ã€ã“ã®ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ãŒãªã„実行ã•れã¾ã™ã€‚ + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## ノートをアップグレードã—ã¾ã™ã€‚ + +ã“ã®ãƒ—ラグインã®ãƒ‡ãƒ™ãƒ­ãƒƒãƒ‘ーã€å…¬é–‹ã•れãŸä»•様ã«åˆã†ã‚ˆã†ã«ã€`èªè¨¼` 㨠`DirectoryEntry` ã®æ§‹é€ ãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ + +プラグインã®å‰ (pre 1.0.0) ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ•ァイル場所 `エントリ` オブジェクト㮠`fullPath` ãƒ—ãƒ­ãƒ‘ãƒ†ã‚£ã«æ ¼ç´ã•れã¾ã™ã€‚ã“れらã®ãƒ‘スã¯ã‚ˆã†ã«ãªã‚Šã¾ã™é€šå¸¸ + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +ã“れらã®ãƒ‘スã¯ã¾ãŸ `Entry` オブジェクト㮠`toURL()` メソッドã«ã‚ˆã£ã¦è¿”ã•れã¾ã™ã€‚ + +デベロッパーã€`fullPath` 属性㯠*HTML ファイルシステムã®ãƒ«ãƒ¼ãƒˆã«å¯¾ã™ã‚‹ç›¸å¯¾ãƒ‘ス* ã®ãƒ•ァイルã¸ã®ãƒ‘ス。 ã—ãŸãŒã£ã¦ã€ä¸Šè¨˜ã®ãƒ‘スã¯ä»Šä¸¡æ–¹ã® `fullPath` 㨠`FileEntry` オブジェクトã§è¡¨ã•れる + + /path/to/file + + +デãƒã‚¤ã‚¹çµ¶å¯¾ãƒ‘スã¨ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³å‹•ä½œä»¥å‰ `Entry` オブジェクト㮠`fullPath` プロパティを使用ã—ã¦ã“れらã®ãƒ‘スをå–å¾—ã—ãŸå ´åˆã¯ã€ä»£ã‚り㫠`entry.toURL()` を使用ã™ã‚‹ã‚³ãƒ¼ãƒ‰ã‚’æ›´æ–°ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + +下ä½äº’æ›æ€§ã€`resolveLocalFileSystemURL()` メソッドã¯ã€ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ‘スをå—ã‘入れるã—ã€ã¯ã€`TEMPORARY` ã¾ãŸã¯ `PERSISTENT` ファイル ・ システム内ã§ãã®ãƒ•ァイルãŒå­˜åœ¨ã™ã‚‹é™ã‚Šã€ãれã«å¯¾å¿œã™ã‚‹ `Entry` オブジェクトを返ã—ã¾ã™ã€‚ + +ã“れã¯ç‰¹ã«ä»¥å‰ãƒ‡ãƒã‚¤ã‚¹çµ¶å¯¾ãƒ‘スを使用ã—ã¦ãƒ•ァイル転é€ã®ãƒ—ラグインã§å•題ã¨ãªã£ã¦ã„ã‚‹ (ãã—ã¦ã¾ã ãれらをå—ã‘入れるã“ã¨ãŒã§ãã¾ã™ï¼‰ã€‚ ãれãŒãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ãƒ•ァイルã§å‹•作ã™ã‚‹ãƒ—ラグインを得るå•題を解決ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ `entry.toURL()` ã§ `entry.fullPath` ã‚’ç½®ãæ›ãˆã‚‹ã®ã§ã€ãƒ•ァイルシステム㮠Url ã§æ­£å¸¸ã«å‹•作ã«ã‚¢ãƒƒãƒ—デートã•れã¾ã—ãŸã€‚ + +V1.1.0 ã® `toURL()` ã®æˆ»ã‚Šå€¤ã«å¤‰æ›´ã•れã¾ã—㟠(\[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394) ã‚’å‚ç…§) を絶対 'file://' ã§å§‹ã¾ã‚‹ URL ã‚’è¿”ã—ã¾ã™ã€‚ å¯èƒ½ãªé™ã‚Šã€‚ 確ä¿ã™ã‚‹ãŸã‚ã«ã€' cdvfile:'-`toInternalURL()` を今ã™ã使用ã§ãã¾ã™ URL。 ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¯ã€ãƒ•ォームã®ãƒ•ァイルシステム㮠Url ã‚’è¿”ã—ã¾ã™ä»Š + + cdvfile://localhost/persistent/path/to/file + + +ã“れã¯ãƒ•ァイルを一æ„ã«è­˜åˆ¥ã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã§ãã¾ã™ã€‚ + +## エラー ã‚³ãƒ¼ãƒ‰ã¨æ„味ã®ãƒªã‚¹ãƒˆ + +エラーãŒã‚¹ãƒ­ãƒ¼ã•れるã¨ã€æ¬¡ã®ã‚³ãƒ¼ãƒ‰ã®ã„ãšã‚Œã‹ãŒä½¿ç”¨ã•れã¾ã™ã€‚ + +| コード | 定数 | +| ---:|:----------------------------- | +| 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` | + +## (çœç•¥å¯èƒ½) プラグインを構æˆã—ã¾ã™ã€‚ + +利用å¯èƒ½ãªãƒ•ァイルシステムã®ã‚»ãƒƒãƒˆã¯æ§‹æˆã•れãŸãƒ—ラットフォームをã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚IOS 㨠Android ã®ä¸¡æ–¹ã‚’èªè­˜ã—ã¾ã™ã€‚ ã‚¿ã‚° `config.xml` をインストールã™ã‚‹ãƒ•ァイルシステムã®åå‰ã—ã¾ã™ã€‚既定ã§ã¯ã€ã™ã¹ã¦ã®ãƒ•ァイル システムã®ãƒ«ãƒ¼ãƒˆãŒæœ‰åйã«ãªã‚Šã¾ã™ã€‚ + + + + + +### アンドロイド + +* `files`: アプリケーションã®å†…部ファイルã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ディレクトリ +* `files-external`: アプリケーションã®å¤–部ã®ãƒ•ァイルã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ ディレクトリ +* `sdcard`:ã€ã‚°ãƒ­ãƒ¼ãƒãƒ«å¤–部ストレージ ディレクトリをファイル (ã“れ㯠SD カードã®ãƒ«ãƒ¼ãƒˆãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹å ´åˆ)。 ã“れを使用ã™ã‚‹ã«ã¯ã€`android.permission.WRITE_EXTERNAL_STORAGE` 権é™ãŒå¿…è¦ã§ã™ã€‚ +* `cache`: アプリケーションã®å†…部キャッシュ ディレクトリ +* `cache-external`: 外部キャッシュã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª +* `root`: デãƒã‚¤ã‚¹å…¨ä½“ã®ãƒ•ァイルシステム + +アンドロイドを「ファイルã€ãƒ•ァイルシステム内ã®"ドキュメント/"サブディレクトリを表ã™"ドキュメント"ã¨ã„ã†åå‰ã®ç‰¹æ®Šãªãƒ•ァイルシステムもサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚ + +### iOS + +* `library`: ライブラリã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª +* `documents`: ドキュメントã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª +* `cache`: キャッシュã®ã‚¢ãƒ—リケーションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª +* `bundle`: アプリケーションãƒãƒ³ãƒ‰ãƒ«;アプリ自体 (読ã¿å–りã®ã¿) ディスク上ã®å ´æ‰€ +* `root`: デãƒã‚¤ã‚¹å…¨ä½“ã®ãƒ•ァイルシステム + +既定ã§ã¯ã€ãƒ©ã‚¤ãƒ–ラリã¨ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ ディレクトリを iCloud ã«åŒæœŸã§ãã¾ã™ã€‚ 2 ã¤ã®è¿½åŠ ã®ãƒ•ァイルシステムã€`library-nosync` ãŠã‚ˆã³ `documents-nosync` を表ã™ã€ç‰¹åˆ¥ãªéžåŒæœŸãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªå†…ã‚’è¦æ±‚ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€`/Library` ã¾ãŸã¯ `Documents/` ファイルシステム。 diff --git a/plugins/cordova-plugin-file/doc/ja/plugins.md b/plugins/cordova-plugin-file/doc/ja/plugins.md new file mode 100644 index 0000000..c54aa78 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ja/plugins.md @@ -0,0 +1,101 @@ + + +# プラグイン開発者ã®ãŸã‚ã®ãƒ¡ãƒ¢ + +ã“れらã®ãƒŽãƒ¼ãƒˆã¯ä¸»ã« Android 㨠iOS 開発者インタ フェース ファイルã®ãƒ—ラグインを使用ã—ã¦ãƒ•ァイル システムã§ãƒ—ラグインを書ããŸã„人å‘ã‘。 + +## コルドãƒã®ãƒ•ァイル システム㮠Url ã§ã®ä½œæ¥­ + +ãƒãƒ¼ã‚¸ãƒ§ãƒ³ 1.0.0 ã§ã¯ã€ä»¥æ¥ã€ã“ã®ãƒ—ラグインをå«ã‚€ Url を使用ã™ã‚‹ `cdvfile` JavaScript ã« raw デãƒã‚¤ã‚¹ ファイル システムã®ãƒ‘スを公開ã™ã‚‹ã®ã§ã¯ãªãã€æ©‹ã®ä¸Šã®ã™ã¹ã¦ã®é€šä¿¡æ–¹å¼ã—ã¾ã™ã€‚ + +JavaScript å´ã§ã¯ã€ã“れã¯ãƒ•ァイル㨠DirectoryEntry オブジェクト㫠HTML ファイル システムã®ãƒ«ãƒ¼ãƒˆã‚’基準ã¨ã—ã¦ã€fullPath 属性ãŒã‚ã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ã€‚ ã‚ãªãŸã®ãƒ—ラグイン㮠JavaScript API ãŒãƒ•ァイルã¾ãŸã¯ DirectoryEntry オブジェクトをå—ã‘入れる場åˆã‚’呼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ `.toURL()` 橋を渡ã£ã¦ãれをãƒã‚¤ãƒ†ã‚£ãƒ– ã‚³ãƒ¼ãƒ‰ã«æ¸¡ã™å‰ã«ãã®ã‚ªãƒ–ジェクトã®ã€‚ + +### Cdvfile ã«å¤‰æ›ã™ã‚‹://fileystem ã®ãƒ‘ス㫠Url + +ファイルシステムã¸ã®æ›¸ãè¾¼ã¿ã™ã‚‹å¿…è¦ãŒã‚るプラグインã¯ã€å®Ÿéš›ã®ãƒ•ァイルシステムã®å ´æ‰€ã«å—ä¿¡ã—ãŸãƒ•ァイル システム URL ã«å¤‰æ›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ãƒã‚¤ãƒ†ã‚£ãƒ– プラットフォームã«ã‚ˆã£ã¦ã€ã“れを行ã†ãŸã‚ã®è¤‡æ•°ã®æ–¹æ³•ãŒã‚りã¾ã™ã€‚ + +ãれを覚ãˆã¦ã„ã‚‹ã“ã¨ãŒé‡è¦ã§ã™ã™ã¹ã¦ `cdvfile://` ã® Url ãŒãƒ‡ãƒã‚¤ã‚¹ä¸Šã®å®Ÿéš›ã®ãƒ•ァイルをマッピングå¯èƒ½ãªã€‚ ã„ãã¤ã‹ã® Url ã¯ã€ãƒ•ァイルã§ã¯è¡¨ã•れãªã„ã¾ãŸã¯ãƒªãƒ¢ãƒ¼ãƒˆ リソースをå‚ç…§ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã‚‚デãƒã‚¤ã‚¹ä¸Šã®è³‡ç”£ã‚’å‚ç…§ã§ãã¾ã™ã€‚ ã“れらã®å¯èƒ½æ€§ã®ãŸã‚ã®ãƒ—ラグインã¯ã€æˆ»ã‚‹ã¨ãã«ãƒ‘ス㫠Url を変æ›ã—よã†ã¨ã—ã¦ã€å½¼ã‚‰ã¯æ„味ã®ã‚ã‚‹çµæžœã‚’å¾—ã‚‹ã‹ã©ã†ã‹å¸¸ã«ãƒ†ã‚¹ãƒˆå¿…è¦ãŒã‚りã¾ã™ã€‚ + +#### アンドロイド + +アンドロイド, ã«å¤‰æ›ã™ã‚‹æœ€ã‚‚ç°¡å˜ãªæ–¹æ³•ã§ã€ `cdvfile://` を使用ã™ã‚‹ãƒ•ァイルシステムã®ãƒ‘ス㫠URL 㯠`org.apache.cordova.CordovaResourceApi` 。 `CordovaResourceApi`扱ã†ã“ã¨ãŒã§ãã‚‹ã„ãã¤ã‹ã®æ–¹æ³•ã¯ã€ `cdvfile://` ã® Url: + + webView プラグイン クラス CordovaResourceApi resourceApi ã®ãƒ¡ãƒ³ãƒãƒ¼ã§ã‚ã‚‹ = webView.getResourceApi()。 + + デãƒã‚¤ã‚¹ã§ã“ã®ãƒ•ァイルを表㙠file:///URL ã‚’å–å¾—//ファイル Uri fileURL ã«ãƒžãƒƒãƒ—ã§ããªã„å ´åˆã€åŒã˜ URL ã¯å¤‰æ›´ã•れã¾ã›ã‚“ = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +ã¾ãŸã€ãƒ•ァイルã®ãƒ—ラグインを直接使用ã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ã€‚ + + インãƒãƒ¼ãƒˆ org.apache.cordova.file.FileUtils; + インãƒãƒ¼ãƒˆ org.apache.cordova.file.FileSystem; + インãƒãƒ¼ãƒˆ java.net.MalformedURLException; + + プラグイン マãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã‹ã‚‰ãƒ•ァイルã®ãƒ—ラグインを入手ã—ã¦ã‚³ãƒžãƒ³ãƒ‰ filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + ãれを試ã¿ã‚‹ãŸã‚ã«ãƒ‘スをå–å¾— URL を指定ã™ã‚‹ã¨ã€{文字列パス = filePlugin.filesystemPathForURL(cdvfileURL);} キャッム(MalformedURLException e) {/ファイルシステム㮠url ãŒèªè­˜ã•れã¾ã›ã‚“ã§ã—ãŸ/} + + +パスã‹ã‚‰å¤‰æ›ã™ã‚‹ã€ `cdvfile://` URL: + + インãƒãƒ¼ãƒˆ org.apache.cordova.file.LocalFilesystemURL; + + デãƒã‚¤ã‚¹ ・ パス㮠LocalFilesystemURL オブジェクトをå–å¾—//cdvfile URL ã¨ã—ã¦è¡¨ç¾ã§ããªã„å ´åˆã¯ null。 + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path); + URL ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã®æ–‡å­—列 cdvfileURL ã®æ–‡å­—列表ç¾ã‚’å–å¾—ã™ã‚‹ = url.toString(); + + +ã‚ãªãŸã®ãƒ—ラグインã¯ã€ãƒ•ァイルを作æˆã—をファイル オブジェクトを返ã™å ´åˆã€ãƒ•ァイルã®ãƒ—ラグインを使用ã—ã¾ã™ã€‚ + + JavaScript ã‚’è¿”ã™ã¨ãã«é©ã—㟠JSON 構造を返ã™//ã“ã®ãƒ•ァイル㯠cdvfile URL ã¨ã—ã¦è¡¨ç¾ã§ããªã„å ´åˆã¯ null。 + JSONObject エントリ = filePlugin.getEntryForFile(file); + + +#### iOS + +IOS ã®ã‚³ãƒ«ãƒ‰ãƒã¯åŒã˜ã‚’使用ã—ãªã„ `CordovaResourceApi` アンドロイドã¨ã—ã¦ã®æ¦‚念。IOS ã§ã¯ã€Url ã¨ãƒ•ァイルシステムã®ãƒ‘スã®é–“を変æ›ã™ã‚‹ãƒ•ァイル プラグインを使用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + + URL ã®æ–‡å­—列 CDVFilesystemURL * url ã‹ã‚‰ CDVFilesystem URL オブジェクトをå–å¾— [CDVFilesystemURL fileSystemURLWithString:cdvfileURL]; + ファイル NSString * パスã«ãƒžãƒƒãƒ—ã§ããªã„å ´åˆã¯ nil ã¾ãŸã¯ URL オブジェクトã®ãƒ‘スをå–å¾— [filePlugin filesystemPathForURL:url]; + + + デãƒã‚¤ã‚¹ ・ パス㮠CDVFilesystem ã® URL オブジェクトをå–å¾—ã¾ãŸã¯//cdvfile URL ã¨ã—ã¦è¡¨ç¾ã§ããªã„å ´åˆã¯ nil ã§ã™ã€‚ + CDVFilesystemURL ã® url = [filePlugin fileSystemURLforLocalPath:path]; + URL オブジェクト NSString * cdvfileURL ã®æ–‡å­—列表ç¾ã‚’å–å¾—ã™ã‚‹ = [url absoluteString]; + + +ã‚ãªãŸã®ãƒ—ラグインã¯ã€ãƒ•ァイルを作æˆã—をファイル オブジェクトを返ã™å ´åˆã€ãƒ•ァイルã®ãƒ—ラグインを使用ã—ã¾ã™ã€‚ + + デãƒã‚¤ã‚¹ ・ パス㮠CDVFilesystem ã® URL オブジェクトをå–å¾—ã¾ãŸã¯//cdvfile URL ã¨ã—ã¦è¡¨ç¾ã§ããªã„å ´åˆã¯ nil ã§ã™ã€‚ + CDVFilesystemURL ã® url = [filePlugin fileSystemURLforLocalPath:path]; + JavaScript NSDictionary * ã‚¨ãƒ³ãƒˆãƒªã«æˆ»ã‚‹æ§‹é€ ã‚’å¾—ã‚‹ = [filePlugin makeEntryForLocalURL:url] + + +#### Java スクリプトã®è¨­å®š + +Java スクリプトã®è¨­å®šã‚’å–å¾—ã™ã‚‹ã«ã€ `cdvfile://` ファイルã¾ãŸã¯ DirectoryEntry オブジェクトã‹ã‚‰ã® URL を呼ã³å‡ºã—ã¦ã€ `.toURL()` ãれを。 + + var cdvfileURL = entry.toURL(); + + +プラグイン応答ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ã«è¿”ã•れ㟠FileEntry 構造体ã®å®Ÿéš›ã®ã‚¨ãƒ³ãƒˆãƒª オブジェクトを変æ›ã™ã‚‹ã€ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ã®ã‚³ãƒ¼ãƒ‰ ファイルã®ãƒ—ラグインをインãƒãƒ¼ãƒˆã€æ–°ã—ã„オブジェクトを作æˆã—ã¾ã™ã€‚ + + é©åˆ‡ãªã‚¨ãƒ³ãƒˆãƒª オブジェクト var エントリを作æˆã—ã¾ã™ã€‚ + å ´åˆ (entryStruct.isDirectory) {エントリ = æ–°ã—ã„ DirectoryEntry (entryStruct.nameã€entryStruct.fullPathã€æ–°ã—ã„ FileSystem(entryStruct.filesystemName));} ä»– {エントリ = æ–°ã—ã„ファイル (entryStruct.nameã€entryStruct.fullPathã€æ–°ã—ã„ FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/ko/README.md b/plugins/cordova-plugin-file/doc/ko/README.md new file mode 100644 index 0000000..a3f3e94 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ko/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +ì´ í”ŒëŸ¬ê·¸ì¸ì€ ìž¥ì¹˜ì— ìžˆëŠ” 파ì¼ì— 대 한 ì½ê¸°/쓰기 액세스를 허용 하는 íŒŒì¼ API를 구현 합니다. + +ì´ í”ŒëŸ¬ê·¸ì¸ì„ í¬í•¨ 한 몇 가지 ì‚¬ì–‘ì— ë”°ë¼: HTML5 íŒŒì¼ API는 + +(ì§€ê¸ˆì€ ì—†ì–´ì§„) 디렉터리와 시스템 확장 최신: í”ŒëŸ¬ê·¸ì¸ ì½”ë“œì˜ ëŒ€ë¶€ë¶„ì€ ë•Œ ì´ì „ 사양 작성 ë˜ì—ˆìŠµë‹ˆë‹¤ 있지만 현재는: + +ê·¸ê²ƒì€ ë˜í•œ FileWriter 사양 구현: + +ì‚¬ìš©ì„ ì°¸ì¡° 하십시오 HTML5 바위 ' 우수한 [íŒŒì¼ ì‹œìŠ¤í…œ 문서.](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +다른 저장소 ì˜µì…˜ì— ëŒ€ 한 개요, 코르ë„ë°”ì˜ [저장소 ê°€ì´ë“œ](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html) 를 참조합니다. + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `cordova.file` 개체를 ì •ì˜í•©ë‹ˆë‹¤. + +ì „ì—­ ë²”ìœ„ì— ìžˆì§€ë§Œ ê·¸ê²ƒì€ ë¶ˆê°€ëŠ¥ê¹Œì§€ `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## 설치 + + cordova plugin add cordova-plugin-file + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 드 로ì´ë“œ + * 블랙베리 10 + * Firefox ìš´ì˜ ì²´ì œ + * iOS + * Windows Phone 7ê³¼ 8 * + * 윈ë„ìš° 8 * + * Windows* + * 브ë¼ìš°ì € + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## 파ì¼ì„ 저장할 위치를 + +V1.2.0, 현재 중요 한 íŒŒì¼ ì‹œìŠ¤í…œ ë””ë ‰í„°ë¦¬ì— Urlë„ ì œê³µ ë©ë‹ˆë‹¤. ê° URL 형태 *file:///path/to/spot/* ì´ë©° `DirectoryEntry` `window.resolveLocalFileSystemURL()`를 사용 하 ì—¬ 변환할 수 있습니다.. + + * `cordova.file.applicationDirectory`-ì½ê¸° ì „ìš© 디렉터리는 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì„¤ì¹˜ 합니다. (*iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + + * `cordova.file.applicationStorageDirectory`ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìƒŒë“œë°•ìŠ¤;ì˜ ë£¨íŠ¸ 디렉터리 iOSì—ì´ ìœ„ì¹˜ì—는 ì½ê¸° ì „ìš© (특정 하위 디렉토리만 [ê°™ì€ `/Documents` ]ì€ ì½ê¸° / 쓰기). í¬í•¨ ëœ ëª¨ë“  ë°ì´í„°ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì— ì „ìš©. ( *iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + + * `cordova.file.dataDirectory`-ë‚´ë¶€ 메모리를 사용 하 ì—¬ ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìƒŒë“œë°•ìŠ¤ ë‚´ì—서 ì˜êµ¬ ë° ê°œì¸ ë°ì´í„° 스토리지 (안 드 로ì´ë“œ, 외부 메모리를 사용 해야 하는 경우 사용 하 ì—¬ `.externalDataDirectory` ). IOSì—ì´ ë””ë ‰í„°ë¦¬ iCloud와 ë™ê¸°í™” ë˜ì§€ ë˜ (를 사용 하 ì—¬ `.syncedDataDirectory` ). (*iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + + * `cordova.file.cacheDirectory`-디렉터리 ìºì‹œ ë°ì´í„° íŒŒì¼ ë˜ëŠ” 모든 파ì¼ì„ ë‹¹ì‹ ì˜ app를 다시 쉽게 만들 수 있습니다. ìš´ì˜ ì²´ì œ 장치 저장소 부족 하면 ì´ëŸ¬í•œ 파ì¼ì„ 삭제할 수 있습니다, 그리고 그럼ì—ë„ ë¶ˆêµ¬ 하 ê³ , 애플 리 ì¼€ì´ ì…˜ ì—¬ê¸°ì— íŒŒì¼ì„ ì‚­ì œ 하려면 ìš´ì˜ ì²´ì œì— ì˜ì¡´ 하지 ë§ì•„야 합니다. (*iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + + * `cordova.file.externalApplicationStorageDirectory`-ì‘ìš© 프로그램 외부 저장 공간입니다. (*안 드 로ì´ë“œ*) + + * `cordova.file.externalDataDirectory`-외부 ì €ìž¥ì†Œì— ì‘ìš© 프로그램 특정 ë°ì´í„° 파ì¼ì„ 넣어 ì–´ë””. (*안 드 로ì´ë“œ*) + + * `cordova.file.externalCacheDirectory`외부 ì €ìž¥ì†Œì— ì‘ìš© 프로그램 ìºì‹œìž…니다. (*안 드 로ì´ë“œ*) + + * `cordova.file.externalRootDirectory`-외부 저장 (SD 카드) 루트입니다. (*안 드 로ì´ë“œ*, *블랙베리 10*) + + * `cordova.file.tempDirectory`-ìš´ì˜ ì²´ì œì—서 지울 수 있습니다 임시 디렉터리 것입니다. ì´ ë””ë ‰í„°ë¦¬;를 ìš´ì˜ ì²´ì œì— ì˜ì¡´ 하지 마십시오 ê·€í•˜ì˜ ì‘ìš© 프로그램 í•­ìƒ í•´ë‹¹ 하는 경우 파ì¼ì„ 제거 해야 합니다. (*iOS*) + + * `cordova.file.syncedDataDirectory`-(ICloud)를 예를 들어 ë™ê¸°í™” 해야 하는 ì‘ìš© 프로그램 관련 파ì¼ì„ 보유 하 ê³  있습니다. (*iOS*) + + * `cordova.file.documentsDirectory`-íŒŒì¼ ì• í”Œ 리 ì¼€ì´ ì…˜, 하지만 ê·¸ ê°œì¸ì€ 다른 ì‘ìš© 프로그램 (예: Office 파ì¼)ì— ì˜ë¯¸ìž…니다. (*iOS*) + + * `cordova.file.sharedDirectory`-모든 ì‘ìš© 프로그램 (*블랙베리 10* ì— ì „ì„¸ê³„ì ìœ¼ë¡œ 사용 가능한 파ì¼) + +## íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +하지만 구현 세부 ì‚¬í•­ì„ ê¸°ìˆ ì ìœ¼ë¡œ `cordova.file.*` ì†ì„± 실제 ìž¥ì¹˜ì— ì‹¤ì œ ê²½ë¡œì— ë§¤í•‘í•˜ëŠ” ë°©ë²•ì„ ì•„ëŠ” ê²ƒì´ ë§¤ìš° 유용할 수 있습니다. + +### iOS íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +| 장치 경로 | `cordova.file.*` | `iosExtraFileSystems` | r/w? | ì˜êµ¬? | OS ì§€ì›ë‹ˆë‹¤ | ë™ê¸°í™” | ê°œì¸ | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:----:|:--------:|:-------------:|:---:|:--:| +| `/ var/모바ì¼/ì‘ìš© 프로그램/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | 예 | +|    `appname.app/` | applicationDirectory | 번들 | r | N/A | N/A | N/A | 예 | +|       `www/` | - | - | r | N/A | N/A | N/A | 예 | +|    `Documents/` | documentsDirectory | 문서 | r/w | 예 | ì—†ìŒ | 예 | 예 | +|       `NoCloud/` | - | 문서 nosync | r/w | 예 | ì—†ìŒ | ì—†ìŒ | 예 | +|    `Library` | - | ë¼ì´ë¸ŒëŸ¬ë¦¬ | r/w | 예 | ì—†ìŒ | 그래? | 예 | +|       `NoCloud/` | dataDirectory | ë¼ì´ë¸ŒëŸ¬ë¦¬ nosync | r/w | 예 | ì—†ìŒ | ì—†ìŒ | 예 | +|       `Cloud/` | syncedDataDirectory | - | r/w | 예 | ì—†ìŒ | 예 | 예 | +|       `Caches/` | cacheDirectory | ìºì‹œ | r/w | 예 * | Yes**\* | ì—†ìŒ | 예 | +|    `tmp/` | tempDirectory | - | r/w | No** | Yes**\* | ì—†ìŒ | 예 | + +\ * íŒŒì¼ ì‘ìš© 프로그램 다시 시작 ë° ì—…ê·¸ë ˆì´ë“œ, 유지 하지만 때마다 OS ìš•ë§ì´ 디렉터리를 지울 수 있습니다. 앱 ì‚­ì œ ë  ìˆ˜ 있습니다 모든 콘í…츠를 다시 만들 수 있어야 합니다. + +** íŒŒì¼ ì‘ìš© 프로그램 다시 시작ì—서 ì§€ì† ë  ìˆ˜ 있습니다 í•˜ì§€ë§Œì´ ë™ìž‘ì— ì˜ì¡´ 하지 마십시오. íŒŒì¼ ì—¬ëŸ¬ ì—…ë°ì´íŠ¸ë¥¼ 보장 하지 않습니다. 때 해당 앱ì´ì´ 디렉터리ì—서 파ì¼ì„ 제거 해야, ì´ëŸ¬í•œ 파ì¼ì„ 제거할 때 (ë˜ëŠ” 경우ì—ë„) ìš´ì˜ ì²´ì œ ë³´ì¦ í•˜ì§€ 않습니다으로. + +**\ * OS ê·¸ê²ƒì´ í•„ìš”ë¥¼ ëŠë‚€ë‹¤ 언제 ë“  ì§€ì´ ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì„ ì·¨ì†Œ 수 있습니다 하지만 ì´ê²ƒì— ì˜ì¡´ 하지 마십시오. ì´ ë””ë ‰í„°ë¦¬ë¥¼ ì‘ìš© í”„ë¡œê·¸ëž¨ì— ëŒ€ 한 ì ì ˆ 한 ì„ íƒì„ 취소 해야 합니다. + +### 안 드 로ì´ë“œ íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +| 장치 경로 | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | ì˜êµ¬? | OS ì§€ì›ë‹ˆë‹¤ | ê°œì¸ | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:----:|:---:|:--------:|:--:| +| `file:///android_asset/` | applicationDirectory | | r | N/A | N/A | 예 | +| `/data/ë°ì´í„°/< app id > /` | applicationStorageDirectory | - | r/w | N/A | N/A | 예 | +|    `cache` | cacheDirectory | ìºì‹œ | r/w | 예 | 예\* | 예 | +|    `files` | dataDirectory | íŒŒì¼ | r/w | 예 | ì—†ìŒ | 예 | +|       `Documents` | | 문서 | r/w | 예 | ì—†ìŒ | 예 | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | 예 | ì—†ìŒ | ì—†ìŒ | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | 예 | ì—†ìŒ | ì—†ìŒ | +|       `cache` | externalCacheDirectry | 외부 ìºì‹œ | r/w | 예 | ì—†ìŒ** | ì—†ìŒ | +|       `files` | externalDataDirectory | íŒŒì¼ ì™¸ë¶€ | r/w | 예 | ì—†ìŒ | ì—†ìŒ | + +\ * ìš´ì˜ ì²´ì œ 정기ì ìœ¼ë¡œì´ 디렉터리 ì‚­ì œ 수 ìžˆì§€ë§Œì´ ë™ìž‘ì— ì˜ì¡´ 하지 마십시오. ì´ ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì„ ì·¨ì†Œ 합니다. ì‚¬ìš©ìž ìˆ˜ë™ìœ¼ë¡œ ìºì‹œ 제거 해야,ì´ ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì€ ì œê±° ë©ë‹ˆë‹¤. + +** ìš´ì˜ ì²´ì œ ìžë™ìœ¼ë¡œ;ì´ ë””ë ‰í„°ë¦¬ë¥¼ ì‚­ì œ 하지 않습니다 ë‚´ìš©ì„ ì§ì ‘ ê´€ë¦¬ì— ëŒ€ 한 ì±…ìž„ì´ ìžˆìŠµë‹ˆë‹¤. ì‚¬ìš©ìž ìˆ˜ë™ìœ¼ë¡œ ìºì‹œ 제거 합니다, ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì€ ì œê±° ë©ë‹ˆë‹¤. + +**참고**: 외부 저장소를 탑재할 수 없는 경우 `cordova.file.external*` ì†ì„±ì€ `null`. + +### 블랙베리 10 íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +| 장치 경로 | `cordova.file.*` | r/w? | ì˜êµ¬? | OS ì§€ì›ë‹ˆë‹¤ | ê°œì¸ | +|:----------------------------------------------------------- |:--------------------------- |:----:|:---:|:-------:|:--:| +| `file:///accounts/1000/appdata/ < app id > /` | applicationStorageDirectory | r | N/A | N/A | 예 | +|    `app/native` | applicationDirectory | r | N/A | N/A | 예 | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | ì—†ìŒ | 예 | 예 | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | 예 | ì—†ìŒ | 예 | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | 예 | ì—†ìŒ | ì—†ìŒ | +| `file:///accounts/1000/shared` | sharedDirectory | r/w | 예 | ì—†ìŒ | ì—†ìŒ | + +*참고*: 모든 경로 /accounts/1000-enterprise를 기준으로 ì‘ìš© 프로그램 경계를 ìž‘ë™ í•˜ë„ë¡ ë°°í¬ ë  ë•Œ. + +## 안 드 로ì´ë“œ ë‹¨ì  + +### 안 드 로ì´ë“œ ì˜êµ¬ 저장 위치 + +안 드 로ì´ë“œ ìž¥ì¹˜ì— ì˜êµ¬ 파ì¼ì„ 저장할 여러 유효한 위치가 있다. 다양 한 ê°€ëŠ¥ì„±ì˜ ê´‘ë²”ìœ„ 한 í† ë¡ ì— ëŒ€ 한 [ì´ íŽ˜ì´ì§€](http://developer.android.com/guide/topics/data/data-storage.html)를 참조 하십시오. + +플러그ì¸ì˜ ì´ì „ ë²„ì „ì„ ì‹œìž‘í•  때, 장치는 SD 카드 (ë˜ëŠ” 해당 스토리지 파티션) 탑재 했다 주장 하는 ì—¬ë¶€ì— ë”°ë¼ ìž„ì‹œ ë° ì˜êµ¬ 파ì¼ì˜ 위치를 ì„ íƒ í•©ë‹ˆë‹¤. SD 카드 마운트, ë˜ëŠ” í° ë‚´ë¶€ 스토리지 íŒŒí‹°ì…˜ì— ì‚¬ìš©í•  수 있었습니다 (ê°™ì€ ë„¥ì„œìŠ¤ 장치ì—) ê·¸ í›„ì— ì˜êµ¬ íŒŒì¼ ê³µê°„ì˜ ë£¨íŠ¸ì— ì €ìž¥ ë©ë‹ˆë‹¤. ì´ ëª¨ë“  코르 ë„ìš° ë°” 애플 리 ì¼€ì´ ì…˜ ì¹´ë“œì— ëª¨ë‘ ì‚¬ìš©í•  수 있는 파ì¼ì„ ë³¼ 수 있는 ì˜ë¯¸ 합니다. + +SD 카드는 사용할 수 있는 경우 ì´ì „ 버전ì—서 ë°ì´í„° 저장 `/data/data/`는 서로 다른 애플 리 ì¼€ì´ ì…˜ì„ ë¶„ë¦¬ 하지만 여전히 ì›ì¸ ë°ì´í„°ë¥¼ ì‚¬ìš©ìž ê°„ì— ê³µìœ í•  수 있습니다. + +ê·¸ê²ƒì€ ì§€ê¸ˆ ë‚´ë¶€ íŒŒì¼ ì €ìž¥ 위치 ë˜ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ `config.xml` 파ì¼ì— 기본 설정으로 ì´ì „ 논리를 사용 하 ì—¬ 파ì¼ì„ 저장할 것ì¸ì§€ë¥¼ ì„ íƒí•  수 있습니다. ì´ë ‡ê²Œ 하려면 `config.xml`ì—ì´ ë‘ ì¤„ 중 하나를 추가: + + + + + + +ì´ ì¤„ì´ ì—†ìœ¼ë©´ íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì€ 기본ì ìœ¼ë¡œ `Compatibility`ì„ ì‚¬ìš© 합니다. 기본 태그,ì´ ì´ëŸ¬í•œ ê°’ 중 하나가 아닌 ê²½ìš°ì— ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì‹œìž‘ ë˜ì§€ 않습니다. + +ì´ì „ (사전 1.0)ì„ ì‚¬ìš© 하는 경우 ì‘ìš© 프로그램 사용ìžì— 게 발송 ë˜ì—ˆë‹¤ ì´ì „,ì´ í”ŒëŸ¬ê·¸ì¸ì˜ 버전 ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— ì €ìž¥ ëœ íŒŒì¼ì€ 그리고 `Compatibility` 환경 ì„¤ì •ì„ ì„¤ì • 해야 합니다. "ë‚´ë¶€"ì˜ ìœ„ì¹˜ 전환 ê·¸ë“¤ì˜ ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì—…ê·¸ë ˆì´ë“œ 기존 사용ìžì˜ ê·¸ë“¤ì˜ ìž¥ì¹˜ì— ë”°ë¼ ê·¸ë“¤ì˜ ì´ì „ì— ì €ìž¥ ëœ íŒŒì¼ì— 액세스할 수 수 있다는 뜻입니다. + +경우 ì‘ìš© í”„ë¡œê·¸ëž¨ì€ ìƒˆë¡œìš´, ë˜ëŠ” ì´ì „ ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— íŒŒì¼ì„ 저장, `Internal` ì„¤ì •ì€ ì¼ë°˜ì ìœ¼ë¡œ 권장 ë©ë‹ˆë‹¤. + +### /Android_assetì— ëŒ€ 한 ëŠë¦° 재귀 작업 + +ìžì‚° 디렉터리를 나열 하는 ê²ƒì€ ì•ˆ 드 로ì´ë“œì— ì •ë§ ëŠë¦¬ë‹¤ìž…니다. ì†ë„ ë†’ì¼ ìˆ˜ 있습니다 하지만, 안 드 로ì´ë“œ 프로ì íŠ¸ì˜ ë£¨íŠ¸ì— `src/android/build-extras.gradle` 를 추가 하 ì—¬ 최대 (cordova-android@4.0.0 í•„ìš” ì´ìƒ). + +## iOS ë‹¨ì  + + * `cordova.file.applicationStorageDirectory`ì½ê¸° ì „ìš©; 루트 디렉터리 ë‚´ì—서 파ì¼ì„ 저장 í•˜ë ¤ê³ ì— ì‹¤íŒ¨ 합니다. 다른 중 하나를 사용 하 ì—¬ `cordova.file.*` iOSì— ëŒ€í•´ ì •ì˜ ëœ ì†ì„± (ë§Œ `applicationDirectory` 와 `applicationStorageDirectory` 는 ì½ê¸° ì „ìš©). + * `FileReader.readAsText(blob, encoding)` + * `encoding`매개 변수는 ì§€ì› ë˜ì§€ 않습니다, ë° íš¨ê³¼ì— í•­ìƒ u t F-8 ì¸ì½”ë”©ì„ í•©ë‹ˆë‹¤. + +### iOS ì˜êµ¬ 저장소 위치 + +IOS 디바ì´ìŠ¤ì— ì˜êµ¬ 파ì¼ì„ 저장할 ë‘ ê°œì˜ ìœ íš¨í•œ 위치가 있다: 문서 디렉터리 ë° ë¼ì´ë¸ŒëŸ¬ë¦¬ 디렉터리. 플러그ì¸ì˜ ì´ì „ ë²„ì „ì€ ì˜¤ì§ ë¬¸ì„œ ë””ë ‰í† ë¦¬ì— ì˜êµ¬ 파ì¼ì„ 저장. ì´ ë¶€ìž‘ìš© 보다는 아니었다 수시로 특히 ë§Žì€ ìž‘ì€ íŒŒì¼ì„ 처리 하는 ì‘ìš© í”„ë¡œê·¸ëž¨ì— ëŒ€ 한 ì˜ë„, iTunesì— í‘œì‹œ 모든 ì‘ìš© 프로그램 파ì¼ì„ 만드는 ë””ë ‰í„°ë¦¬ì˜ ìš©ë„ ë‚´ë³´ë‚´ê¸°ì— ëŒ€ 한 완전 한 문서를 ìƒì‚° 했다. + +ê·¸ê²ƒì€ ì§€ê¸ˆ 문서 ë˜ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ `config.xml` 파ì¼ì— 기본 설정으로 ë¼ì´ë¸ŒëŸ¬ë¦¬ ë””ë ‰í† ë¦¬ì— íŒŒì¼ì„ 저장할 것ì¸ì§€ë¥¼ ì„ íƒí•  수 있습니다. ì´ë ‡ê²Œ 하려면 `config.xml`ì—ì´ ë‘ ì¤„ 중 하나를 추가: + + + + + + +ì´ ì¤„ì´ ì—†ìœ¼ë©´ íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì€ 기본ì ìœ¼ë¡œ `Compatibility`ì„ ì‚¬ìš© 합니다. 기본 태그,ì´ ì´ëŸ¬í•œ ê°’ 중 하나가 아닌 ê²½ìš°ì— ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì‹œìž‘ ë˜ì§€ 않습니다. + +ì´ì „ (사전 1.0)ì„ ì‚¬ìš© 하는 경우 ì‘ìš© 프로그램 사용ìžì— 게 발송 ë˜ì—ˆë‹¤ ì´ì „,ì´ í”ŒëŸ¬ê·¸ì¸ì˜ 버전 ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— ì €ìž¥ ëœ íŒŒì¼ì€ 그리고 `Compatibility` 환경 ì„¤ì •ì„ ì„¤ì • 해야 합니다. `Library`ì— ìœ„ì¹˜ë¥¼ 스위칭 기존 사용ìžì— 게 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì—…ê·¸ë ˆì´ ë“œì˜ ê·¸ë“¤ì˜ ì´ì „ì— ì €ìž¥ ëœ íŒŒì¼ì— 액세스할 수 ê²ƒì„ ì˜ë¯¸í•  것입니다. + +경우 ì‘ìš© í”„ë¡œê·¸ëž¨ì€ ìƒˆë¡œìš´, ë˜ëŠ” ì´ì „ ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— íŒŒì¼ì„ 저장, `Library` ì„¤ì •ì€ ì¼ë°˜ì ìœ¼ë¡œ 권장 ë©ë‹ˆë‹¤. + +## 파ì´ì–´ í­ìФ OS ë‹¨ì  + +íŒŒì¼ ì‹œìŠ¤í…œ API Firefox ìš´ì˜ ì²´ì œì—서 기본ì ìœ¼ë¡œ ì§€ì› í•˜ì§€ ë° indexedDB ìœ„ì— ì‹¬ìœ¼ë¡œ 구현 ë©ë‹ˆë‹¤. + + * 비어 있지 ì•Šì€ ë””ë ‰í„°ë¦¬ë¥¼ 제거할 때 실패 하지 않습니다. + * ë””ë ‰í„°ë¦¬ì— ëŒ€ 한 메타 ë°ì´í„°ë¥¼ ì§€ì› í•˜ì§€ 않습니다. + * 메서드 `copyTo` ë° `moveTo` 디렉터리를 ì§€ì› í•˜ì§€ 않습니다 + +ë‹¤ìŒ ë°ì´í„° 경로 ì§€ì› ë©ë‹ˆë‹¤: * `applicationDirectory`-`xhr`를 사용 하 ì—¬ 로컬 파ì¼ì„ ì‘ìš© 프로그램 패키지를 가져옵니다. * `dataDirectory`-ì˜êµ¬ ì‘ìš© 프로그램 특정 ë°ì´í„° 파ì¼ì— 대 한. * `cacheDirectory`-ì‘ìš© 프로그램 다시 시작 해야 하는 ìºì‹œ ëœ íŒŒì¼ (애플 리 ì¼€ì´ ì…˜ì€ ì—¬ê¸°ì— íŒŒì¼ì„ ì‚­ì œ 하려면 ìš´ì˜ ì²´ì œì— ì˜ì¡´ 하지 ë§ì•„야). + +## 브ë¼ìš°ì € 만지면 + +### ì¼ë°˜ì ì¸ ë‹¨ì  ë° ì„¤ëª… + + * ê° ë¸Œë¼ìš°ì €ëŠ” 샌드박스 ìžì²´ íŒŒì¼ ì‹œìŠ¤í…œì„ ì‚¬ìš©í•©ë‹ˆë‹¤. IE와 파ì´ì–´ í­ìФ 기반으로 IndexedDB를 사용합니다. 모든 브ë¼ìš°ì €ëŠ” 경로ì—서 디렉터리 구분 기호로 슬래시를 사용합니다. + * 디렉터리 í•­ëª©ì„ ì—°ì†ì ìœ¼ë¡œ 만들 수 있다. 예를 들어 ì „í™” `fs.root.getDirectory ('dir1/dir2 ', {create:true}, successCallback, errorCallback)` d i r 1 존재 하지 ì•Šì€ ê²½ìš° 실패 합니다. + * í”ŒëŸ¬ê·¸ì¸ ì‘ìš© 프로그램 ì²˜ìŒ ì‹œìž‘í•  ì˜êµ¬ 저장소를 사용 하 ì—¬ ì‚¬ìš©ìž ê¶Œí•œì„ ìš”ì²­ 합니다. + * í”ŒëŸ¬ê·¸ì¸ ì§€ì› `cdvfile://localhost` (로컬 리소스)ë§Œ. 즉, 외부 리소스는 `cdvfile`를 통해 ì§€ì› ë˜ì§€ 않습니다.. + * í”ŒëŸ¬ê·¸ì¸ ["íŒŒì¼ ì‹œìŠ¤í…œ API 8.3 명명 제한"ì„](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions) 수행 하지 않습니다.. + * Blob ë° íŒŒì¼ ' `close` 함수는 ì§€ì› ë˜ì§€ 않습니다. + * `FileSaver` ë° `BlobBuilder`ëŠ”ì´ í”ŒëŸ¬ê·¸ ì ‘ì†ì‹ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다 그리고 명세서를 필요가 없습니다. + * í”ŒëŸ¬ê·¸ì¸ `requestAllFileSystems`를 ì§€ì› í•˜ì§€ 않습니다. ì´ í•¨ìˆ˜ëŠ” ë˜í•œ ì‚¬ì–‘ì— ë¹ ì§„. + * 사용 하는 경우 디렉터리ì—서 항목 제거 ë˜ì§€ 것입니다 `create: true` 기존 ë””ë ‰í„°ë¦¬ì— ëŒ€ 한 플래그. + * ìƒì„±ìžë¥¼ 통해 ìƒì„± ëœ íŒŒì¼ì€ ì§€ì› ë˜ì§€ 않습니다. Entry.file 메서드를 대신 사용 해야 합니다. + * ê° ë¸Œë¼ìš°ì € blob URL ì°¸ì¡°ì— ëŒ€ 한 ê·¸ê²ƒì˜ ìžì‹ ì˜ 형태를 사용합니다. + * `readAsDataURL` ê¸°ëŠ¥ì„ ì§€ì› í•˜ì§€ë§Œ í¬ë¡¬ì—서 mediatype 항목 ì´ë¦„ í™•ìž¥ëª…ì— ë”°ë¼ ë‹¬ë¼ ì§‘ë‹ˆë‹¤, 그리고 mediatype IEì—는 í•­ìƒ ë¹ˆ (`í…스트 ì¼ë°˜` ì‚¬ì–‘ì— ë”°ë¼ ë™ì¼), 파ì´ì–´ í­ìФì—서 mediatypeì€ í•­ìƒ `ì‘ìš© 프로그램/8 진수 스트림`. 예를 들어, 콘í…츠는 `abcdefg` ë‹¤ìŒ íŒŒì´ì–´ í­ìФ 반환 `ë°ì´í„°: ì‘ìš© 프로그램 / 8 진수 스트림; base64, YWJjZGVmZw = =`, 즉 반환 `ë°ì´í„°:; base64, YWJjZGVmZw = =`, 반환 í¬ë¡¬ `ë°ì´í„°: < 항목 ì´ë¦„ì˜ í™•ìž¥ì— ë”°ë¼ mediatype >; base64, YWJjZGVmZw = =`. + * `toInternalURL` ì–‘ì‹ `file:///persistent/path/to/entry` (파ì´ì–´ í­ìФ, ì¸í„°ë„· ìµìŠ¤í”Œë¡œëŸ¬)ì—서 경로 반환합니다. í¬ë¡¬ ì–‘ì‹ `cdvfile://localhost/persistent/file`ì— ê²½ë¡œ 반환합니다.. + +### í¬ë¡¬ 특수 + + * í¬ë¡¬ íŒŒì¼ ì‹œìŠ¤í…œ 장치 준비 ì´ë²¤íЏ 후 즉시 준비 ë˜ì§€ 않습니다. 문제를 í•´ê²° 하려면 `filePluginIsReady` ì´ë²¤íŠ¸ë¥¼ 구ë…í•  수 있습니다. 예를 들어: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +`Window.isFilePluginReadyRaised` 함수를 사용 하 ì—¬ ì´ë²¤íŠ¸ê°€ ì´ë¯¸ ë°œìƒ ì—¬ë¶€ë¥¼ 확ì¸í•  수 있습니다. -window.requestFileSystem 임시 ë° ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œ 할당량 í¬ë¡¬ì— 제한 ë˜ì§€ 않습니다. -í¬ë¡¬ì—서 ì˜êµ¬ 저장소를 ì¦ê°€ 하려면 `window.initPersistentFileSystem` 메서드를 호출 해야 합니다. ì˜êµ¬ 저장소 í• ë‹¹ëŸ‰ì€ ê¸°ë³¸ì ìœ¼ë¡œ 5 메가바ì´íŠ¸ìž…ë‹ˆë‹¤. -í¬ë¡¬ í•„ìš” `-허용-파ì¼-액세스-ì—서-파ì¼` `file:///` í”„ë¡œí† ì½œì„ í†µí•´ ì§€ì› API ì¸ìˆ˜ë¥¼ 실행 합니다. -플래그를 사용 하면 `파ì¼` 개체 하지 변경할 수 `{create:true}` 때 기존 `항목`. -행사 `cancelable` ì†ì„±ì´ë¡œ 설정 ëœ í¬ë¡¬ì—서. ì´ [사양](http://dev.w3.org/2009/dap/file-system/file-writer.html) ëŒ€ì¡°ì  ì´ë‹¤. -í¬ë¡¬ì—서 `toURL` 함수 반환 합니다 `íŒŒì¼ ì‹œìŠ¤í…œ:`-ì‘ìš© 프로그램 í˜¸ìŠ¤íŠ¸ì— ë”°ë¼ ê²½ë¡œ 앞ì—. 예를 들어, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -`toURL` 함수 ê²°ê³¼ 디렉터리 í•­ëª©ì˜ ê²½ìš°ì— í›„í–‰ 슬래시를 í¬í•¨ 하지 않습니다. í¬ë¡¬ 하지만 제대로 붙여 슬래시 urlì´ í¬í•¨ ëœ ë””ë ‰í„°ë¦¬ 해결합니다. -`resolveLocalFileSystemURL` 메서드 ì¸ë°”운드 `url`ì„ `íŒŒì¼ ì‹œìŠ¤í…œ` ì ‘ë‘사가 í•„ìš” 합니다. 예를 들어, `url` 매개 변수 `resolveLocalFileSystemURL`ì— ëŒ€ 한 안 드 로ì´ë“œì—서 ì–‘ì‹ `file:///persistent/somefile.txt` 반대로 ì–‘ì‹ `filesystem:file:///persistent/somefile.txt`ì— ìžˆì–´ì•¼ 합니다. -사용 ë˜ì§€ 않는 `toNativeURL` 함수는 ì§€ì› ë˜ì§€ 않습니다 ë° stubì—는 없습니다. -`setMetadata` 함수는 ê·œê²©ì— ëª…ì‹œ ë˜ì§€ ì•Šì€ ë° ì§€ì› ë˜ì§€ 않습니다. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ SYNTAX_ERR(code: 8) 비 existant íŒŒì¼ ì‹œìŠ¤í…œì˜ ìš”ì²­ì—. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ PATH_EXISTS_ERR(code: 12) ë…ì ì ìœ¼ë¡œ íŒŒì¼ ë˜ëŠ” 디렉터리를 만들 ë ¤,는 ì´ë¯¸ 존재 합니다. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ NO_MODIFICATION_ALLOWED_ERR(code: 6) 루트 íŒŒì¼ ì‹œìŠ¤í…œì— removeRecursivelyì„ í˜¸ì¶œ 하려고 합니다. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ NOT_FOUND_ERR(code: 1) moveTo 디렉터리 존재 하지 않는 ê²ƒì„ ì‹œë„ì—. + +### IndexedDB 기반 êµ¬í˜„ì´ íŠ¹ìˆ˜ (파ì´ì–´ í­ìŠ¤ì™€ IE) + + * `.` `.`는 ì§€ì› ë˜ì§€ 않습니다. + * IE `file:///`를 ì§€ì› í•˜ì§€ 않습니다-모드; 호스트 모드 ì§€ì›ëœ (http://localhost:xxxx)입니다. + * 파ì´ì–´ í­ìФ íŒŒì¼ ì‹œìŠ¤í…œ í¬ê¸° 제한 ì´ì§€ë§Œ ê° 50MB 확장 ì‚¬ìš©ìž ê¶Œí•œì„ ìš”ì²­ 합니다. IE10 최대 10 mb ê²°í•© AppCache ë° IndexedDB 묻는 사ì´íЏ 당 250 mbì˜ ìµœëŒ€ 최대 ì¦ê°€ ë  ìˆ˜ 있ë„ë¡ í•˜ë ¤ëŠ” 경우 해당 ìˆ˜ì¤€ì— ì¶©ëŒ í•œ 번 메시지를 표시 하지 않고 íŒŒì¼ ì‹œìŠ¤í…œì˜ êµ¬í˜„ì— ì‚¬ìš©ì„ í—ˆìš© 한다. 그래서 `size` 매개 변수 `requestFileSystem` í•¨ìˆ˜ì— ëŒ€ 한 파ì´ì–´ í­ìŠ¤ì™€ IEì—서 íŒŒì¼ ì‹œìŠ¤í…œ ì˜í–¥ì„ 주지 않습니다. + * `readAsBinaryString` 함수 ì‚¬ì–‘ì— ëª…ì‹œ ë˜ì§€ ì•Šì€ IEì—서 ì§€ì› ë˜ì§€ 않으며 stubì—는 없습니다. + * `file.type`ì€ í•­ìƒ null입니다. + * 하지 항목 ì‚­ì œ ëœ DirectoryEntry ì¸ìŠ¤í„´ìŠ¤ì˜ ì½œë°± ê²°ê³¼ 사용 하 ì—¬ 만들어야 합니다. 그렇지 않으면, 'êµìˆ˜í˜• 항목'ì„ ì–»ì„ ê²ƒ ì´ë‹¤. + * 그냥 작성 ëœ íŒŒì¼ì„ ì½ì„ 수 ìžˆëŠ”ì´ íŒŒì¼ì˜ 새 ì¸ìŠ¤í„´ìŠ¤ë¥¼ 얻으려면 해야 합니다. + * `setMetadata` 함수는 ì‚¬ì–‘ì— ëª…ì‹œ ë˜ì§€ ì•Šì€ ì§€ì› `modificationTime` 필드 변경ì—ë§Œ 해당 합니다. + * `copyTo` ë° `moveTo` 함수는 디렉터리를 ì§€ì› í•˜ì§€ 않습니다. + * 디렉터리 메타 ë°ì´í„°ëŠ” ì§€ì› ë˜ì§€ 않습니다. + * 둘 다 Entry.remove와 directoryEntry.removeRecursively 비어 있지 ì•Šì€ ë””ë ‰í„°ë¦¬ë¥¼ 제거할 때 실패 하지 않습니다-디렉터리 제거 ë˜ëŠ” 대신 ë‚´ìš©ì„ í•¨ê»˜ 청소. + * `abort` ë° `truncate` 함수 ì§€ì› ë˜ì§€ 않습니다. + * ì§„í–‰ ì´ë²¤íŠ¸ê°€ ë°œìƒ í•˜ì§€ 합니다. 예를 들어,ì´ ì²˜ë¦¬ê¸° 하지 실행 ë©ë‹ˆë‹¤. + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## 업그레ì´ë“œ 노트 + +ì´ í”ŒëŸ¬ê·¸ì¸ì˜ v1.0.0ì— ê²Œì‹œ ëœ ì‚¬ì–‘ì— ë§žì¶° ë” ë§Žì€ ê²ƒ `FileEntry` ë° `DirectoryEntry` 구조 변경 ë˜ì—ˆìŠµë‹ˆë‹¤. + +플러그ì¸ì˜ ì´ì „ (pre-1.0.0) 버전 장치 절대 íŒŒì¼ ìœ„ì¹˜ `Entry` ê°œì²´ì˜ `fullPath` ì†ì„±ì— 저장 ë©ë‹ˆë‹¤. ì´ëŸ¬í•œ 경로 ì¼ë°˜ì ìœ¼ë¡œ 같습니다. + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +ì´ëŸ¬í•œ 경로 `항목` ê°œì²´ì˜ `toURL()` 메서드ì—서 반환 했다. + +V1.0.0, `fullPath` ì†ì„±ì€ *HTML íŒŒì¼ ì‹œìŠ¤í…œì˜ ë£¨íŠ¸ì— ìƒëŒ€ì ì¸* 파ì¼ì˜ 경로를. 그래서, ìœ„ì˜ ê²½ë¡œ 지금 둘 ë‹¤ì˜ `fullPath`와 `FileEntry` ê°œì²´ì— ì˜í•´ 표현 ë  ê²ƒ ì´ë‹¤ + + /path/to/file + + +ì‘ìš© 프로그램 ìž‘ë™ ìž¥ì¹˜ 절대 경로, ì´ì „ `항목` ê°œì²´ì˜ `fullPath` ì†ì„±ì„ 통해 ê·¸ 경로 검색 하는 경우ì—, ë‹¹ì‹ ì€ ëŒ€ì‹  `entry.toURL()`를 사용 하 ì—¬ 코드를 ì—…ë°ì´íЏ 해야 합니다. + +대 한 ë’¤ 호환성, `resolveLocalFileSystemURL()` 메서드는 장치-절대-경로 ìˆ˜ë½ í•˜ ê³  ê·¸ íŒŒì¼ ì¤‘ `TEMPORARY` ë˜ëŠ” `PERSISTENT` íŒŒì¼ ì‹œìŠ¤í…œ ë‚´ì—서 존재 하는 경우, 해당 `Entry` 개체를 반환 합니다. + +ì´ íŠ¹ížˆ ì´ì „ 장치 절대 경로 사용 하는 íŒŒì¼ ì „ì†¡ 플러그ì¸ì— 문제가 있다 (그리고 ì•„ì§ë„ ê·¸ë“¤ì„ ë°›ì•„ë“¤ì¼ ìˆ˜.) ê·¸ê²ƒì€ `entry.toURL()`와 `entry.fullPath`를 대체 í™•ì¸ ìž¥ì¹˜ì— íŒŒì¼ì„ 사용 하는 플러그ì¸ì„ ì§€ ê³  그래서 íŒŒì¼ ì‹œìŠ¤í…œ Url와 함께 제대로 ìž‘ë™ í•˜ë ¤ë©´ ì—…ë°ì´íЏ ë˜ì—ˆìŠµë‹ˆë‹¤. + +V1.1.0ì— `toURL()`ì˜ ë°˜í™˜ ê°’ (\[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394) 참조)로 바뀌었다 'file://' 절대 URLì„ ë°˜í™˜. 가능 하다 ë©´. 보장 하는 ' cdvfile:'-URL `toInternalURL()`를 지금 사용할 수 있습니다. ì´ ë©”ì„œë“œ ì´ì œ ì–‘ì‹ì˜ íŒŒì¼ Urlì„ ë°˜í™˜ 합니다. + + cdvfile://localhost/persistent/path/to/file + + +ì–´ë–¤ 파ì¼ì„ 고유 하 게 ì‹ë³„ 하려면 사용할 수 있습니다. + +## 오류 코드 ë° ì˜ë¯¸ì˜ ëª©ë¡ + +오류가 throw ë©ë‹ˆë‹¤ 때 ë‹¤ìŒ ì½”ë“œ 중 하나가 사용 ë©ë‹ˆë‹¤. + +| 코드 | ìƒìˆ˜ | +| --:|:----------------------------- | +| 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` | + +## (ì„ íƒ ì‚¬í•­) í”ŒëŸ¬ê·¸ì¸ êµ¬ì„± + +사용 가능한 íŒŒì¼ ì‹œìŠ¤í…œì˜ ì§‘í•© í”Œëž«í¼ ë‹¹ êµ¬ì„±ëœ ë  ìˆ˜ 있습니다. IOS와 안 드 로ì´ë“œë¥¼ ì¸ì‹ 한 `config.xml` 설치 ë  íŒŒì¼ ì‹œìŠ¤í…œ ì´ë¦„ì— íƒœê·¸. 기본ì ìœ¼ë¡œ 모든 íŒŒì¼ ì‹œìŠ¤í…œ 루트 사용할 수 있습니다. + + + + + +### 안 드 로ì´ë“œ + + * `files`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë‚´ë¶€ íŒŒì¼ ì €ìž¥ 디렉토리 + * `files-external`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ì™¸ë¶€ íŒŒì¼ ì €ìž¥ 디렉토리 + * `sdcard`: 글로벌 외부 íŒŒì¼ ì €ìž¥ 디렉토리 (ì´ê²ƒì€ SD ì¹´ë“œì˜ ë£¨íŠ¸ 설치 ëœ ê²½ìš°). ì´ê²ƒì„ 사용 하려면 `android.permission.WRITE_EXTERNAL_STORAGE` ê¶Œí•œì´ ìžˆì–´ì•¼ 합니다. + * `cache`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë‚´ë¶€ ìºì‹œ 디렉터리 + * `cache-external`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ì™¸ë¶€ ìºì‹œ 디렉터리 + * `root`: ì „ì²´ 장치 íŒŒì¼ ì‹œìŠ¤í…œ + +안 드 로ì´ë“œëŠ” ë˜í•œ "파ì¼" íŒŒì¼ ì‹œìŠ¤í…œ ë‚´ì—서 "/ 문서 /" 하위 디렉토리를 나타내는 "문서" ë¼ëŠ” 특별 한 파ì¼ì„ ì§€ì› í•©ë‹ˆë‹¤. + +### iOS + + * `library`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë¼ì´ë¸ŒëŸ¬ë¦¬ 디렉터리 + * `documents`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë¬¸ì„œ 디렉토리 + * `cache`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìºì‹œ 디렉터리 + * `bundle`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë²ˆë“¤; (ì½ê¸° ì „ìš©) 디스í¬ì— ì‘ìš© 프로그램 ìžì²´ì˜ 위치 + * `root`: ì „ì²´ 장치 íŒŒì¼ ì‹œìŠ¤í…œ + +기본ì ìœ¼ë¡œ ë¼ì´ë¸ŒëŸ¬ë¦¬ ë° ë¬¸ì„œ 디렉토리 iCloudì— ë™ê¸°í™” í•  수 있습니다. ë˜í•œ 2 ê°œì˜ ì¶”ê°€ì ì¸ íŒŒì¼ ì‹œìŠ¤í…œ, `library-nosync` ë° `documents-nosync`, ë‚´ 특별 한 ë™ê¸°í™” ë˜ì§€ ì•Šì€ ë””ë ‰í„°ë¦¬ë¥¼ 대표 하는 요청할 수 있습니다는 `/Library` ë˜ëŠ” `/Documents` íŒŒì¼ ì‹œìŠ¤í…œ. \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/ko/index.md b/plugins/cordova-plugin-file/doc/ko/index.md new file mode 100644 index 0000000..08340d8 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ko/index.md @@ -0,0 +1,338 @@ + + +# cordova-plugin-file + +ì´ í”ŒëŸ¬ê·¸ì¸ì€ ìž¥ì¹˜ì— ìžˆëŠ” 파ì¼ì— 대 한 ì½ê¸°/쓰기 액세스를 허용 하는 íŒŒì¼ API를 구현 합니다. + +ì´ í”ŒëŸ¬ê·¸ì¸ì„ í¬í•¨ 한 몇 가지 ì‚¬ì–‘ì— ë”°ë¼: HTML5 íŒŒì¼ API는 + +(ì§€ê¸ˆì€ ì—†ì–´ì§„) 디렉터리와 시스템 확장 최신: í”ŒëŸ¬ê·¸ì¸ ì½”ë“œì˜ ëŒ€ë¶€ë¶„ì€ ë•Œ ì´ì „ 사양 작성 ë˜ì—ˆìŠµë‹ˆë‹¤ 있지만 현재는: + +ê·¸ê²ƒì€ ë˜í•œ FileWriter 사양 구현: + +ì‚¬ìš©ì„ ì°¸ì¡° 하십시오 HTML5 바위 ' 우수한 [íŒŒì¼ ì‹œìŠ¤í…œ 문서.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +다른 저장소 ì˜µì…˜ì— ëŒ€ 한 개요, 코르ë„ë°”ì˜ [저장소 ê°€ì´ë“œ][2] 를 참조합니다. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `cordova.file` 개체를 ì •ì˜í•©ë‹ˆë‹¤. + +ì „ì—­ ë²”ìœ„ì— ìžˆì§€ë§Œ ê·¸ê²ƒì€ ë¶ˆê°€ëŠ¥ê¹Œì§€ `deviceready` ì´ë²¤íЏ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## 설치 + + cordova plugin add cordova-plugin-file + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 드 로ì´ë“œ +* 블랙베리 10 +* Firefox ìš´ì˜ ì²´ì œ +* iOS +* Windows Phone 7ê³¼ 8 * +* 윈ë„ìš° 8 * +* 브ë¼ìš°ì € + +* *`FileReader.readAsArrayBuffer`ë„ `FileWriter.write(blob)`ì´ í”Œëž«í¼ì„ ì§€ì› í•˜ì§€ 않습니다.* + +## 파ì¼ì„ 저장할 위치를 + +V1.2.0, 현재 중요 한 íŒŒì¼ ì‹œìŠ¤í…œ ë””ë ‰í„°ë¦¬ì— Urlë„ ì œê³µ ë©ë‹ˆë‹¤. ê° URL 형태 *file:///path/to/spot/* ì´ë©° `DirectoryEntry` `window.resolveLocalFileSystemURL()`를 사용 하 ì—¬ 변환할 수 있습니다.. + +* `cordova.file.applicationDirectory`-ì½ê¸° ì „ìš© 디렉터리는 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì„¤ì¹˜ 합니다. (*iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + +* `cordova.file.applicationStorageDirectory`ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìƒŒë“œë°•ìŠ¤;ì˜ ë£¨íŠ¸ 디렉터리 iOSì—ì´ ìœ„ì¹˜ì—는 ì½ê¸° ì „ìš© (특정 하위 디렉토리만 [ê°™ì€ `/Documents` ]ì€ ì½ê¸° / 쓰기). í¬í•¨ ëœ ëª¨ë“  ë°ì´í„°ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì— ì „ìš©. ( *iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + +* `cordova.file.dataDirectory`-ë‚´ë¶€ 메모리를 사용 하 ì—¬ ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìƒŒë“œë°•ìŠ¤ ë‚´ì—서 ì˜êµ¬ ë° ê°œì¸ ë°ì´í„° 스토리지 (안 드 로ì´ë“œ, 외부 메모리를 사용 해야 하는 경우 사용 하 ì—¬ `.externalDataDirectory` ). IOSì—ì´ ë””ë ‰í„°ë¦¬ iCloud와 ë™ê¸°í™” ë˜ì§€ ë˜ (를 사용 하 ì—¬ `.syncedDataDirectory` ). (*iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + +* `cordova.file.cacheDirectory`-디렉터리 ìºì‹œ ë°ì´í„° íŒŒì¼ ë˜ëŠ” 모든 파ì¼ì„ ë‹¹ì‹ ì˜ app를 다시 쉽게 만들 수 있습니다. ìš´ì˜ ì²´ì œ 장치 저장소 부족 하면 ì´ëŸ¬í•œ 파ì¼ì„ 삭제할 수 있습니다, 그리고 그럼ì—ë„ ë¶ˆêµ¬ 하 ê³ , 애플 리 ì¼€ì´ ì…˜ ì—¬ê¸°ì— íŒŒì¼ì„ ì‚­ì œ 하려면 ìš´ì˜ ì²´ì œì— ì˜ì¡´ 하지 ë§ì•„야 합니다. (*iOS*, *안 드 로ì´ë“œ*, *블랙베리 10*) + +* `cordova.file.externalApplicationStorageDirectory`-ì‘ìš© 프로그램 외부 저장 공간입니다. (*안 드 로ì´ë“œ*) + +* `cordova.file.externalDataDirectory`-외부 ì €ìž¥ì†Œì— ì‘ìš© 프로그램 특정 ë°ì´í„° 파ì¼ì„ 넣어 ì–´ë””. (*안 드 로ì´ë“œ*) + +* `cordova.file.externalCacheDirectory`외부 ì €ìž¥ì†Œì— ì‘ìš© 프로그램 ìºì‹œìž…니다. (*안 드 로ì´ë“œ*) + +* `cordova.file.externalRootDirectory`-외부 저장 (SD 카드) 루트입니다. (*안 드 로ì´ë“œ*, *블랙베리 10*) + +* `cordova.file.tempDirectory`-ìš´ì˜ ì²´ì œì—서 지울 수 있습니다 임시 디렉터리 것입니다. ì´ ë””ë ‰í„°ë¦¬;를 ìš´ì˜ ì²´ì œì— ì˜ì¡´ 하지 마십시오 ê·€í•˜ì˜ ì‘ìš© 프로그램 í•­ìƒ í•´ë‹¹ 하는 경우 파ì¼ì„ 제거 해야 합니다. (*iOS*) + +* `cordova.file.syncedDataDirectory`-(ICloud)를 예를 들어 ë™ê¸°í™” 해야 하는 ì‘ìš© 프로그램 관련 파ì¼ì„ 보유 하 ê³  있습니다. (*iOS*) + +* `cordova.file.documentsDirectory`-íŒŒì¼ ì• í”Œ 리 ì¼€ì´ ì…˜, 하지만 ê·¸ ê°œì¸ì€ 다른 ì‘ìš© 프로그램 (예: Office 파ì¼)ì— ì˜ë¯¸ìž…니다. (*iOS*) + +* `cordova.file.sharedDirectory`-모든 ì‘ìš© 프로그램 (*블랙베리 10* ì— ì „ì„¸ê³„ì ìœ¼ë¡œ 사용 가능한 파ì¼) + +## íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +하지만 구현 세부 ì‚¬í•­ì„ ê¸°ìˆ ì ìœ¼ë¡œ `cordova.file.*` ì†ì„± 실제 ìž¥ì¹˜ì— ì‹¤ì œ ê²½ë¡œì— ë§¤í•‘í•˜ëŠ” ë°©ë²•ì„ ì•„ëŠ” ê²ƒì´ ë§¤ìš° 유용할 수 있습니다. + +### iOS íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +| 장치 경로 | `cordova.file.*` | `iosExtraFileSystems` | r/w? | ì˜êµ¬? | OS ì§€ì›ë‹ˆë‹¤ | ë™ê¸°í™” | ê°œì¸ | +|:------------------------------------ |:--------------------------- |:--------------------- |:----:|:------:|:---------:|:---:|:--:| +| `/ var/모바ì¼/ì‘ìš© 프로그램/< UUID > /` | applicationStorageDirectory | - | r | N/A | N/A | N/A | 예 | +|    `appname.app/` | applicationDirectory | 번들 | r | N/A | N/A | N/A | 예 | +|       `www/` | - | - | r | N/A | N/A | N/A | 예 | +|    `Documents/` | documentsDirectory | 문서 | r/w | 예 | ì—†ìŒ | 예 | 예 | +|       `NoCloud/` | - | 문서 nosync | r/w | 예 | ì—†ìŒ | ì—†ìŒ | 예 | +|    `Library` | - | ë¼ì´ë¸ŒëŸ¬ë¦¬ | r/w | 예 | ì—†ìŒ | 그래? | 예 | +|       `NoCloud/` | dataDirectory | ë¼ì´ë¸ŒëŸ¬ë¦¬ nosync | r/w | 예 | ì—†ìŒ | ì—†ìŒ | 예 | +|       `Cloud/` | syncedDataDirectory | - | r/w | 예 | ì—†ìŒ | 예 | 예 | +|       `Caches/` | cacheDirectory | ìºì‹œ | r/w | 예 * | 예 * * *| | ì—†ìŒ | 예 | +|    `tmp/` | tempDirectory | - | r/w | 아니 * * | 예 * * *| | ì—†ìŒ | 예 | + +* íŒŒì¼ ì‘ìš© 프로그램 다시 시작 ë° ì—…ê·¸ë ˆì´ë“œ, 유지 하지만 OS ìš•ë§ ì–¸ì œ ë“  ì§€ì´ ë””ë ‰í„°ë¦¬ë¥¼ 지울 수 있습니다. 앱 ì‚­ì œ ë  ìˆ˜ 있습니다 모든 콘í…츠를 다시 만들 수 있어야 합니다. + +* * íŒŒì¼ ì‘ìš© 프로그램 다시 시작ì—서 ì§€ì† ë  ìˆ˜ 있습니다 í•˜ì§€ë§Œì´ ë™ìž‘ì— ì˜ì¡´ 하지 마십시오. íŒŒì¼ ì—¬ëŸ¬ ì—…ë°ì´íŠ¸ë¥¼ 보장 하지 않습니다. 때 해당 앱ì´ì´ 디렉터리ì—서 파ì¼ì„ 제거 해야, ì´ëŸ¬í•œ 파ì¼ì„ 제거할 때 (ë˜ëŠ” 경우ì—ë„) ìš´ì˜ ì²´ì œ ë³´ì¦ í•˜ì§€ 않습니다으로. + +* * *| OS ê·¸ê²ƒì´ í•„ìš”ë¥¼ ëŠë‚€ë‹¤ 언제 ë“  ì§€ì´ ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì„ ì·¨ì†Œ ë  ìˆ˜ 있습니다 하지만 ì´ê²ƒì— ì˜ì¡´ 하지 마십시오. ì´ ë””ë ‰í„°ë¦¬ë¥¼ ì‘ìš© í”„ë¡œê·¸ëž¨ì— ëŒ€ 한 ì ì ˆ 한 ì„ íƒì„ 취소 해야 합니다. + +### 안 드 로ì´ë“œ íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +| 장치 경로 | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | ì˜êµ¬? | OS ì§€ì›ë‹ˆë‹¤ | ê°œì¸ | +|:--------------------------------- |:----------------------------------- |:------------------------- |:----:|:---:|:-------:|:--:| +| `file:///android_asset/` | applicationDirectory | | r | N/A | N/A | 예 | +| `/data/ë°ì´í„°/< app id > /` | applicationStorageDirectory | - | r/w | N/A | N/A | 예 | +|    `cache` | cacheDirectory | ìºì‹œ | r/w | 예 | 예 * | 예 | +|    `files` | dataDirectory | íŒŒì¼ | r/w | 예 | ì—†ìŒ | 예 | +|       `Documents` | | 문서 | r/w | 예 | ì—†ìŒ | 예 | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | 예 | ì—†ìŒ | ì—†ìŒ | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | 예 | ì—†ìŒ | ì—†ìŒ | +|       `cache` | externalCacheDirectry | 외부 ìºì‹œ | r/w | 예 | 아니 * * | ì—†ìŒ | +|       `files` | externalDataDirectory | íŒŒì¼ ì™¸ë¶€ | r/w | 예 | ì—†ìŒ | ì—†ìŒ | + +* OS 수 있습니다 정기ì ìœ¼ë¡œì´ ë””ë ‰í„°ë¦¬ì— ìžˆì§€ë§Œì´ ë™ìž‘ì— ì˜ì¡´ 하지 마십시오. ì´ ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì„ ì·¨ì†Œ 합니다. ì‚¬ìš©ìž ìˆ˜ë™ìœ¼ë¡œ ìºì‹œ 제거 해야,ì´ ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì€ ì œê±° ë©ë‹ˆë‹¤. + +* * OS 지워지지 ì•ŠìŠµë‹ˆë‹¤ì´ ë””ë ‰í„°ë¦¬ ìžë™ìœ¼ë¡œ; 콘í…츠를 관리 하기 위한 ì±…ìž„ì´ ìžˆìŠµë‹ˆë‹¤. ì‚¬ìš©ìž ìˆ˜ë™ìœ¼ë¡œ ìºì‹œ 제거 합니다, ë””ë ‰í„°ë¦¬ì˜ ë‚´ìš©ì€ ì œê±° ë©ë‹ˆë‹¤. + +**참고**: 외부 저장소를 탑재할 수 없는 경우 `cordova.file.external*` ì†ì„±ì€ `null`. + +### 블랙베리 10 íŒŒì¼ ì‹œìŠ¤í…œ ë ˆì´ì•„웃 + +| 장치 경로 | `cordova.file.*` | r/w? | ì˜êµ¬? | OS ì§€ì›ë‹ˆë‹¤ | ê°œì¸ | +|:--------------------------------------------------- |:--------------------------- |:----:|:---:|:-------:|:--:| +| `file:///accounts/1000/appdata/ < app id > /` | applicationStorageDirectory | r | N/A | N/A | 예 | +|    `app/native` | applicationDirectory | r | N/A | N/A | 예 | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | ì—†ìŒ | 예 | 예 | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | 예 | ì—†ìŒ | 예 | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | 예 | ì—†ìŒ | ì—†ìŒ | +| `file:///accounts/1000/shared` | sharedDirectory | r/w | 예 | ì—†ìŒ | ì—†ìŒ | + +*참고*: 모든 경로 /accounts/1000-enterprise를 기준으로 ì‘ìš© 프로그램 경계를 ìž‘ë™ í•˜ë„ë¡ ë°°í¬ ë  ë•Œ. + +## 안 드 로ì´ë“œ ë‹¨ì  + +### 안 드 로ì´ë“œ ì˜êµ¬ 저장 위치 + +안 드 로ì´ë“œ ìž¥ì¹˜ì— ì˜êµ¬ 파ì¼ì„ 저장할 여러 유효한 위치가 있다. 다양 한 ê°€ëŠ¥ì„±ì˜ ê´‘ë²”ìœ„ 한 í† ë¡ ì— ëŒ€ 한 [ì´ íŽ˜ì´ì§€][3]를 참조 하십시오. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +플러그ì¸ì˜ ì´ì „ ë²„ì „ì„ ì‹œìž‘í•  때, 장치는 SD 카드 (ë˜ëŠ” 해당 스토리지 파티션) 탑재 했다 주장 하는 ì—¬ë¶€ì— ë”°ë¼ ìž„ì‹œ ë° ì˜êµ¬ 파ì¼ì˜ 위치를 ì„ íƒ í•©ë‹ˆë‹¤. SD 카드 마운트, ë˜ëŠ” í° ë‚´ë¶€ 스토리지 íŒŒí‹°ì…˜ì— ì‚¬ìš©í•  수 있었습니다 (ê°™ì€ ë„¥ì„œìŠ¤ 장치ì—) ê·¸ í›„ì— ì˜êµ¬ íŒŒì¼ ê³µê°„ì˜ ë£¨íŠ¸ì— ì €ìž¥ ë©ë‹ˆë‹¤. ì´ ëª¨ë“  코르 ë„ìš° ë°” 애플 리 ì¼€ì´ ì…˜ ì¹´ë“œì— ëª¨ë‘ ì‚¬ìš©í•  수 있는 파ì¼ì„ ë³¼ 수 있는 ì˜ë¯¸ 합니다. + +SD 카드는 사용할 수 있는 경우 ì´ì „ 버전ì—서 ë°ì´í„° 저장 `/data/data/`는 서로 다른 애플 리 ì¼€ì´ ì…˜ì„ ë¶„ë¦¬ 하지만 여전히 ì›ì¸ ë°ì´í„°ë¥¼ ì‚¬ìš©ìž ê°„ì— ê³µìœ í•  수 있습니다. + +ê·¸ê²ƒì€ ì§€ê¸ˆ ë‚´ë¶€ íŒŒì¼ ì €ìž¥ 위치 ë˜ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ `config.xml` 파ì¼ì— 기본 설정으로 ì´ì „ 논리를 사용 하 ì—¬ 파ì¼ì„ 저장할 것ì¸ì§€ë¥¼ ì„ íƒí•  수 있습니다. ì´ë ‡ê²Œ 하려면 `config.xml`ì—ì´ ë‘ ì¤„ 중 하나를 추가: + + + + + + +ì´ ì¤„ì´ ì—†ìœ¼ë©´ íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì€ 기본ì ìœ¼ë¡œ `Compatibility`ì„ ì‚¬ìš© 합니다. 기본 태그,ì´ ì´ëŸ¬í•œ ê°’ 중 하나가 아닌 ê²½ìš°ì— ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì‹œìž‘ ë˜ì§€ 않습니다. + +ì´ì „ (사전 1.0)ì„ ì‚¬ìš© 하는 경우 ì‘ìš© 프로그램 사용ìžì— 게 발송 ë˜ì—ˆë‹¤ ì´ì „,ì´ í”ŒëŸ¬ê·¸ì¸ì˜ 버전 ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— ì €ìž¥ ëœ íŒŒì¼ì€ 그리고 `Compatibility` 환경 ì„¤ì •ì„ ì„¤ì • 해야 합니다. "ë‚´ë¶€"ì˜ ìœ„ì¹˜ 전환 ê·¸ë“¤ì˜ ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì—…ê·¸ë ˆì´ë“œ 기존 사용ìžì˜ ê·¸ë“¤ì˜ ìž¥ì¹˜ì— ë”°ë¼ ê·¸ë“¤ì˜ ì´ì „ì— ì €ìž¥ ëœ íŒŒì¼ì— 액세스할 수 수 있다는 뜻입니다. + +경우 ì‘ìš© í”„ë¡œê·¸ëž¨ì€ ìƒˆë¡œìš´, ë˜ëŠ” ì´ì „ ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— íŒŒì¼ì„ 저장, `Internal` ì„¤ì •ì€ ì¼ë°˜ì ìœ¼ë¡œ 권장 ë©ë‹ˆë‹¤. + +## iOS ë‹¨ì  + +* `cordova.file.applicationStorageDirectory`ì½ê¸° ì „ìš©; 루트 디렉터리 ë‚´ì—서 파ì¼ì„ 저장 í•˜ë ¤ê³ ì— ì‹¤íŒ¨ 합니다. 다른 중 하나를 사용 하 ì—¬ `cordova.file.*` iOSì— ëŒ€í•´ ì •ì˜ ëœ ì†ì„± (ë§Œ `applicationDirectory` 와 `applicationStorageDirectory` 는 ì½ê¸° ì „ìš©). +* `FileReader.readAsText(blob, encoding)` + * `encoding`매개 변수는 ì§€ì› ë˜ì§€ 않습니다, ë° íš¨ê³¼ì— í•­ìƒ u t F-8 ì¸ì½”ë”©ì„ í•©ë‹ˆë‹¤. + +### iOS ì˜êµ¬ 저장소 위치 + +IOS 디바ì´ìŠ¤ì— ì˜êµ¬ 파ì¼ì„ 저장할 ë‘ ê°œì˜ ìœ íš¨í•œ 위치가 있다: 문서 디렉터리 ë° ë¼ì´ë¸ŒëŸ¬ë¦¬ 디렉터리. 플러그ì¸ì˜ ì´ì „ ë²„ì „ì€ ì˜¤ì§ ë¬¸ì„œ ë””ë ‰í† ë¦¬ì— ì˜êµ¬ 파ì¼ì„ 저장. ì´ ë¶€ìž‘ìš© 보다는 아니었다 수시로 특히 ë§Žì€ ìž‘ì€ íŒŒì¼ì„ 처리 하는 ì‘ìš© í”„ë¡œê·¸ëž¨ì— ëŒ€ 한 ì˜ë„, iTunesì— í‘œì‹œ 모든 ì‘ìš© 프로그램 파ì¼ì„ 만드는 ë””ë ‰í„°ë¦¬ì˜ ìš©ë„ ë‚´ë³´ë‚´ê¸°ì— ëŒ€ 한 완전 한 문서를 ìƒì‚° 했다. + +ê·¸ê²ƒì€ ì§€ê¸ˆ 문서 ë˜ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ `config.xml` 파ì¼ì— 기본 설정으로 ë¼ì´ë¸ŒëŸ¬ë¦¬ ë””ë ‰í† ë¦¬ì— íŒŒì¼ì„ 저장할 것ì¸ì§€ë¥¼ ì„ íƒí•  수 있습니다. ì´ë ‡ê²Œ 하려면 `config.xml`ì—ì´ ë‘ ì¤„ 중 하나를 추가: + + + + + + +ì´ ì¤„ì´ ì—†ìœ¼ë©´ íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì€ 기본ì ìœ¼ë¡œ `Compatibility`ì„ ì‚¬ìš© 합니다. 기본 태그,ì´ ì´ëŸ¬í•œ ê°’ 중 하나가 아닌 ê²½ìš°ì— ì‘ìš© í”„ë¡œê·¸ëž¨ì´ ì‹œìž‘ ë˜ì§€ 않습니다. + +ì´ì „ (사전 1.0)ì„ ì‚¬ìš© 하는 경우 ì‘ìš© 프로그램 사용ìžì— 게 발송 ë˜ì—ˆë‹¤ ì´ì „,ì´ í”ŒëŸ¬ê·¸ì¸ì˜ 버전 ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— ì €ìž¥ ëœ íŒŒì¼ì€ 그리고 `Compatibility` 환경 ì„¤ì •ì„ ì„¤ì • 해야 합니다. `Library`ì— ìœ„ì¹˜ë¥¼ 스위칭 기존 사용ìžì— 게 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì—…ê·¸ë ˆì´ ë“œì˜ ê·¸ë“¤ì˜ ì´ì „ì— ì €ìž¥ ëœ íŒŒì¼ì— 액세스할 수 ê²ƒì„ ì˜ë¯¸í•  것입니다. + +경우 ì‘ìš© í”„ë¡œê·¸ëž¨ì€ ìƒˆë¡œìš´, ë˜ëŠ” ì´ì „ ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œì— íŒŒì¼ì„ 저장, `Library` ì„¤ì •ì€ ì¼ë°˜ì ìœ¼ë¡œ 권장 ë©ë‹ˆë‹¤. + +## 파ì´ì–´ í­ìФ OS ë‹¨ì  + +íŒŒì¼ ì‹œìŠ¤í…œ API Firefox ìš´ì˜ ì²´ì œì—서 기본ì ìœ¼ë¡œ ì§€ì› í•˜ì§€ ë° indexedDB ìœ„ì— ì‹¬ìœ¼ë¡œ 구현 ë©ë‹ˆë‹¤. + +* 비어 있지 ì•Šì€ ë””ë ‰í„°ë¦¬ë¥¼ 제거할 때 실패 하지 않습니다. +* ë””ë ‰í„°ë¦¬ì— ëŒ€ 한 메타 ë°ì´í„°ë¥¼ ì§€ì› í•˜ì§€ 않습니다. +* 메서드 `copyTo` ë° `moveTo` 디렉터리를 ì§€ì› í•˜ì§€ 않습니다 + +ë‹¤ìŒ ë°ì´í„° 경로 ì§€ì› ë©ë‹ˆë‹¤: * `applicationDirectory`-`xhr`를 사용 하 ì—¬ 로컬 파ì¼ì„ ì‘ìš© 프로그램 패키지를 가져옵니다. * `dataDirectory`-ì˜êµ¬ ì‘ìš© 프로그램 특정 ë°ì´í„° 파ì¼ì— 대 한. * `cacheDirectory`-ì‘ìš© 프로그램 다시 시작 해야 하는 ìºì‹œ ëœ íŒŒì¼ (애플 리 ì¼€ì´ ì…˜ì€ ì—¬ê¸°ì— íŒŒì¼ì„ ì‚­ì œ 하려면 ìš´ì˜ ì²´ì œì— ì˜ì¡´ 하지 ë§ì•„야). + +## 브ë¼ìš°ì € 만지면 + +### ì¼ë°˜ì ì¸ ë‹¨ì  ë° ì„¤ëª… + +* ê° ë¸Œë¼ìš°ì €ëŠ” 샌드박스 ìžì²´ íŒŒì¼ ì‹œìŠ¤í…œì„ ì‚¬ìš©í•©ë‹ˆë‹¤. IE와 파ì´ì–´ í­ìФ 기반으로 IndexedDB를 사용합니다. 모든 브ë¼ìš°ì €ëŠ” 경로ì—서 디렉터리 구분 기호로 슬래시를 사용합니다. +* 디렉터리 í•­ëª©ì„ ì—°ì†ì ìœ¼ë¡œ 만들 수 있다. 예를 들어 ì „í™” `fs.root.getDirectory ('dir1/dir2 ', {create:true}, successCallback, errorCallback)` d i r 1 존재 하지 ì•Šì€ ê²½ìš° 실패 합니다. +* í”ŒëŸ¬ê·¸ì¸ ì‘ìš© 프로그램 ì²˜ìŒ ì‹œìž‘í•  ì˜êµ¬ 저장소를 사용 하 ì—¬ ì‚¬ìš©ìž ê¶Œí•œì„ ìš”ì²­ 합니다. +* í”ŒëŸ¬ê·¸ì¸ ì§€ì› `cdvfile://localhost` (로컬 리소스)ë§Œ. 즉, 외부 리소스는 `cdvfile`를 통해 ì§€ì› ë˜ì§€ 않습니다.. +* í”ŒëŸ¬ê·¸ì¸ ["íŒŒì¼ ì‹œìŠ¤í…œ API 8.3 명명 제한"ì„][4] 수행 하지 않습니다.. +* Blob ë° íŒŒì¼ ' `close` 함수는 ì§€ì› ë˜ì§€ 않습니다. +* `FileSaver` ë° `BlobBuilder`ëŠ”ì´ í”ŒëŸ¬ê·¸ ì ‘ì†ì‹ì— ì˜í•´ ì§€ì› ë˜ì§€ 않습니다 그리고 명세서를 필요가 없습니다. +* í”ŒëŸ¬ê·¸ì¸ `requestAllFileSystems`를 ì§€ì› í•˜ì§€ 않습니다. ì´ í•¨ìˆ˜ëŠ” ë˜í•œ ì‚¬ì–‘ì— ë¹ ì§„. +* 사용 하는 경우 디렉터리ì—서 항목 제거 ë˜ì§€ 것입니다 `create: true` 기존 ë””ë ‰í„°ë¦¬ì— ëŒ€ 한 플래그. +* ìƒì„±ìžë¥¼ 통해 ìƒì„± ëœ íŒŒì¼ì€ ì§€ì› ë˜ì§€ 않습니다. Entry.file 메서드를 대신 사용 해야 합니다. +* ê° ë¸Œë¼ìš°ì € blob URL ì°¸ì¡°ì— ëŒ€ 한 ê·¸ê²ƒì˜ ìžì‹ ì˜ 형태를 사용합니다. +* `readAsDataURL` ê¸°ëŠ¥ì„ ì§€ì› í•˜ì§€ë§Œ í¬ë¡¬ì—서 mediatype 항목 ì´ë¦„ í™•ìž¥ëª…ì— ë”°ë¼ ë‹¬ë¼ ì§‘ë‹ˆë‹¤, 그리고 mediatype IEì—는 í•­ìƒ ë¹ˆ (`í…스트 ì¼ë°˜` ì‚¬ì–‘ì— ë”°ë¼ ë™ì¼), 파ì´ì–´ í­ìФì—서 mediatypeì€ í•­ìƒ `ì‘ìš© 프로그램/8 진수 스트림`. 예를 들어, 콘í…츠는 `abcdefg` ë‹¤ìŒ íŒŒì´ì–´ í­ìФ 반환 `ë°ì´í„°: ì‘ìš© 프로그램 / 8 진수 스트림; base64, YWJjZGVmZw = =`, 즉 반환 `ë°ì´í„°:; base64, YWJjZGVmZw = =`, 반환 í¬ë¡¬ `ë°ì´í„°: < 항목 ì´ë¦„ì˜ í™•ìž¥ì— ë”°ë¼ mediatype >; base64, YWJjZGVmZw = =`. +* `toInternalURL` ì–‘ì‹ `file:///persistent/path/to/entry` (파ì´ì–´ í­ìФ, ì¸í„°ë„· ìµìŠ¤í”Œë¡œëŸ¬)ì—서 경로 반환합니다. í¬ë¡¬ ì–‘ì‹ `cdvfile://localhost/persistent/file`ì— ê²½ë¡œ 반환합니다.. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### í¬ë¡¬ 특수 + +* í¬ë¡¬ íŒŒì¼ ì‹œìŠ¤í…œ 장치 준비 ì´ë²¤íЏ 후 즉시 준비 ë˜ì§€ 않습니다. 문제를 í•´ê²° 하려면 `filePluginIsReady` ì´ë²¤íŠ¸ë¥¼ 구ë…í•  수 있습니다. 예를 들어: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +`Window.isFilePluginReadyRaised` 함수를 사용 하 ì—¬ ì´ë²¤íŠ¸ê°€ ì´ë¯¸ ë°œìƒ ì—¬ë¶€ë¥¼ 확ì¸í•  수 있습니다. -window.requestFileSystem 임시 ë° ì˜êµ¬ íŒŒì¼ ì‹œìŠ¤í…œ 할당량 í¬ë¡¬ì— 제한 ë˜ì§€ 않습니다. -í¬ë¡¬ì—서 ì˜êµ¬ 저장소를 ì¦ê°€ 하려면 `window.initPersistentFileSystem` 메서드를 호출 해야 합니다. ì˜êµ¬ 저장소 í• ë‹¹ëŸ‰ì€ ê¸°ë³¸ì ìœ¼ë¡œ 5 메가바ì´íŠ¸ìž…ë‹ˆë‹¤. -í¬ë¡¬ í•„ìš” `-허용-파ì¼-액세스-ì—서-파ì¼` `file:///` í”„ë¡œí† ì½œì„ í†µí•´ ì§€ì› API ì¸ìˆ˜ë¥¼ 실행 합니다. -플래그를 사용 하면 `파ì¼` 개체 하지 변경할 수 `{create:true}` 때 기존 `항목`. -행사 `cancelable` ì†ì„±ì´ë¡œ 설정 ëœ í¬ë¡¬ì—서. ì´ [사양][5] ëŒ€ì¡°ì  ì´ë‹¤. -í¬ë¡¬ì—서 `toURL` 함수 반환 합니다 `íŒŒì¼ ì‹œìŠ¤í…œ:`-ì‘ìš© 프로그램 í˜¸ìŠ¤íŠ¸ì— ë”°ë¼ ê²½ë¡œ 앞ì—. 예를 들어, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -`toURL` 함수 ê²°ê³¼ 디렉터리 í•­ëª©ì˜ ê²½ìš°ì— í›„í–‰ 슬래시를 í¬í•¨ 하지 않습니다. í¬ë¡¬ 하지만 제대로 붙여 슬래시 urlì´ í¬í•¨ ëœ ë””ë ‰í„°ë¦¬ 해결합니다. -`resolveLocalFileSystemURL` 메서드 ì¸ë°”운드 `url`ì„ `íŒŒì¼ ì‹œìŠ¤í…œ` ì ‘ë‘사가 í•„ìš” 합니다. 예를 들어, `url` 매개 변수 `resolveLocalFileSystemURL`ì— ëŒ€ 한 안 드 로ì´ë“œì—서 ì–‘ì‹ `file:///persistent/somefile.txt` 반대로 ì–‘ì‹ `filesystem:file:///persistent/somefile.txt`ì— ìžˆì–´ì•¼ 합니다. -사용 ë˜ì§€ 않는 `toNativeURL` 함수는 ì§€ì› ë˜ì§€ 않습니다 ë° stubì—는 없습니다. -`setMetadata` 함수는 ê·œê²©ì— ëª…ì‹œ ë˜ì§€ ì•Šì€ ë° ì§€ì› ë˜ì§€ 않습니다. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ SYNTAX_ERR(code: 8) 비 existant íŒŒì¼ ì‹œìŠ¤í…œì˜ ìš”ì²­ì—. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ PATH_EXISTS_ERR(code: 12) ë…ì ì ìœ¼ë¡œ íŒŒì¼ ë˜ëŠ” 디렉터리를 만들 ë ¤,는 ì´ë¯¸ 존재 합니다. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ NO_MODIFICATION_ALLOWED_ERR(code: 6) 루트 íŒŒì¼ ì‹œìŠ¤í…œì— removeRecursivelyì„ í˜¸ì¶œ 하려고 합니다. -INVALID_MODIFICATION_ERR (코드: 9) 대신 throw ë©ë‹ˆë‹¤ NOT_FOUND_ERR(code: 1) moveTo 디렉터리 존재 하지 않는 ê²ƒì„ ì‹œë„ì—. + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### IndexedDB 기반 êµ¬í˜„ì´ íŠ¹ìˆ˜ (파ì´ì–´ í­ìŠ¤ì™€ IE) + +* `.` `.`는 ì§€ì› ë˜ì§€ 않습니다. +* IE `file:///`를 ì§€ì› í•˜ì§€ 않습니다-모드; 호스트 모드 ì§€ì›ëœ (http://localhost:xxxx)입니다. +* 파ì´ì–´ í­ìФ íŒŒì¼ ì‹œìŠ¤í…œ í¬ê¸° 제한 ì´ì§€ë§Œ ê° 50MB 확장 ì‚¬ìš©ìž ê¶Œí•œì„ ìš”ì²­ 합니다. IE10 최대 10 mb ê²°í•© AppCache ë° IndexedDB 묻는 사ì´íЏ 당 250 mbì˜ ìµœëŒ€ 최대 ì¦ê°€ ë  ìˆ˜ 있ë„ë¡ í•˜ë ¤ëŠ” 경우 해당 ìˆ˜ì¤€ì— ì¶©ëŒ í•œ 번 메시지를 표시 하지 않고 íŒŒì¼ ì‹œìŠ¤í…œì˜ êµ¬í˜„ì— ì‚¬ìš©ì„ í—ˆìš© 한다. 그래서 `size` 매개 변수 `requestFileSystem` í•¨ìˆ˜ì— ëŒ€ 한 파ì´ì–´ í­ìŠ¤ì™€ IEì—서 íŒŒì¼ ì‹œìŠ¤í…œ ì˜í–¥ì„ 주지 않습니다. +* `readAsBinaryString` 함수 ì‚¬ì–‘ì— ëª…ì‹œ ë˜ì§€ ì•Šì€ IEì—서 ì§€ì› ë˜ì§€ 않으며 stubì—는 없습니다. +* `file.type`ì€ í•­ìƒ null입니다. +* 하지 항목 ì‚­ì œ ëœ DirectoryEntry ì¸ìŠ¤í„´ìŠ¤ì˜ ì½œë°± ê²°ê³¼ 사용 하 ì—¬ 만들어야 합니다. 그렇지 않으면, 'êµìˆ˜í˜• 항목'ì„ ì–»ì„ ê²ƒ ì´ë‹¤. +* 그냥 작성 ëœ íŒŒì¼ì„ ì½ì„ 수 ìžˆëŠ”ì´ íŒŒì¼ì˜ 새 ì¸ìŠ¤í„´ìŠ¤ë¥¼ 얻으려면 해야 합니다. +* `setMetadata` 함수는 ì‚¬ì–‘ì— ëª…ì‹œ ë˜ì§€ ì•Šì€ ì§€ì› `modificationTime` 필드 변경ì—ë§Œ 해당 합니다. +* `copyTo` ë° `moveTo` 함수는 디렉터리를 ì§€ì› í•˜ì§€ 않습니다. +* 디렉터리 메타 ë°ì´í„°ëŠ” ì§€ì› ë˜ì§€ 않습니다. +* 둘 다 Entry.remove와 directoryEntry.removeRecursively 비어 있지 ì•Šì€ ë””ë ‰í„°ë¦¬ë¥¼ 제거할 때 실패 하지 않습니다-디렉터리 제거 ë˜ëŠ” 대신 ë‚´ìš©ì„ í•¨ê»˜ 청소. +* `abort` ë° `truncate` 함수 ì§€ì› ë˜ì§€ 않습니다. +* ì§„í–‰ ì´ë²¤íŠ¸ê°€ ë°œìƒ í•˜ì§€ 합니다. 예를 들어,ì´ ì²˜ë¦¬ê¸° 하지 실행 ë©ë‹ˆë‹¤. + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## 업그레ì´ë“œ 노트 + +ì´ í”ŒëŸ¬ê·¸ì¸ì˜ v1.0.0ì— ê²Œì‹œ ëœ ì‚¬ì–‘ì— ë§žì¶° ë” ë§Žì€ ê²ƒ `FileEntry` ë° `DirectoryEntry` 구조 변경 ë˜ì—ˆìŠµë‹ˆë‹¤. + +플러그ì¸ì˜ ì´ì „ (pre-1.0.0) 버전 장치 절대 íŒŒì¼ ìœ„ì¹˜ `Entry` ê°œì²´ì˜ `fullPath` ì†ì„±ì— 저장 ë©ë‹ˆë‹¤. ì´ëŸ¬í•œ 경로 ì¼ë°˜ì ìœ¼ë¡œ 같습니다. + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +ì´ëŸ¬í•œ 경로 `항목` ê°œì²´ì˜ `toURL()` 메서드ì—서 반환 했다. + +V1.0.0, `fullPath` ì†ì„±ì€ *HTML íŒŒì¼ ì‹œìŠ¤í…œì˜ ë£¨íŠ¸ì— ìƒëŒ€ì ì¸* 파ì¼ì˜ 경로를. 그래서, ìœ„ì˜ ê²½ë¡œ 지금 둘 ë‹¤ì˜ `fullPath`와 `FileEntry` ê°œì²´ì— ì˜í•´ 표현 ë  ê²ƒ ì´ë‹¤ + + /path/to/file + + +ì‘ìš© 프로그램 ìž‘ë™ ìž¥ì¹˜ 절대 경로, ì´ì „ `항목` ê°œì²´ì˜ `fullPath` ì†ì„±ì„ 통해 ê·¸ 경로 검색 하는 경우ì—, ë‹¹ì‹ ì€ ëŒ€ì‹  `entry.toURL()`를 사용 하 ì—¬ 코드를 ì—…ë°ì´íЏ 해야 합니다. + +대 한 ë’¤ 호환성, `resolveLocalFileSystemURL()` 메서드는 장치-절대-경로 ìˆ˜ë½ í•˜ ê³  ê·¸ íŒŒì¼ ì¤‘ `TEMPORARY` ë˜ëŠ” `PERSISTENT` íŒŒì¼ ì‹œìŠ¤í…œ ë‚´ì—서 존재 하는 경우, 해당 `Entry` 개체를 반환 합니다. + +ì´ íŠ¹ížˆ ì´ì „ 장치 절대 경로 사용 하는 íŒŒì¼ ì „ì†¡ 플러그ì¸ì— 문제가 있다 (그리고 ì•„ì§ë„ ê·¸ë“¤ì„ ë°›ì•„ë“¤ì¼ ìˆ˜.) ê·¸ê²ƒì€ `entry.toURL()`와 `entry.fullPath`를 대체 í™•ì¸ ìž¥ì¹˜ì— íŒŒì¼ì„ 사용 하는 플러그ì¸ì„ ì§€ ê³  그래서 íŒŒì¼ ì‹œìŠ¤í…œ Url와 함께 제대로 ìž‘ë™ í•˜ë ¤ë©´ ì—…ë°ì´íЏ ë˜ì—ˆìŠµë‹ˆë‹¤. + +V1.1.0ì— `toURL()`ì˜ ë°˜í™˜ ê°’ (\[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394) 참조)로 바뀌었다 'file://' 절대 URLì„ ë°˜í™˜. 가능 하다 ë©´. 보장 하는 ' cdvfile:'-URL `toInternalURL()`를 지금 사용할 수 있습니다. ì´ ë©”ì„œë“œ ì´ì œ ì–‘ì‹ì˜ íŒŒì¼ Urlì„ ë°˜í™˜ 합니다. + + cdvfile://localhost/persistent/path/to/file + + +ì–´ë–¤ 파ì¼ì„ 고유 하 게 ì‹ë³„ 하려면 사용할 수 있습니다. + +## 오류 코드 ë° ì˜ë¯¸ì˜ ëª©ë¡ + +오류가 throw ë©ë‹ˆë‹¤ 때 ë‹¤ìŒ ì½”ë“œ 중 하나가 사용 ë©ë‹ˆë‹¤. + +| 코드 | ìƒìˆ˜ | +| --:|:----------------------------- | +| 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` | + +## (ì„ íƒ ì‚¬í•­) í”ŒëŸ¬ê·¸ì¸ êµ¬ì„± + +사용 가능한 íŒŒì¼ ì‹œìŠ¤í…œì˜ ì§‘í•© í”Œëž«í¼ ë‹¹ êµ¬ì„±ëœ ë  ìˆ˜ 있습니다. IOS와 안 드 로ì´ë“œë¥¼ ì¸ì‹ 한 `config.xml` 설치 ë  íŒŒì¼ ì‹œìŠ¤í…œ ì´ë¦„ì— íƒœê·¸. 기본ì ìœ¼ë¡œ 모든 íŒŒì¼ ì‹œìŠ¤í…œ 루트 사용할 수 있습니다. + + + + + +### 안 드 로ì´ë“œ + +* `files`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë‚´ë¶€ íŒŒì¼ ì €ìž¥ 디렉토리 +* `files-external`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ì™¸ë¶€ íŒŒì¼ ì €ìž¥ 디렉토리 +* `sdcard`: 글로벌 외부 íŒŒì¼ ì €ìž¥ 디렉토리 (ì´ê²ƒì€ SD ì¹´ë“œì˜ ë£¨íŠ¸ 설치 ëœ ê²½ìš°). ì´ê²ƒì„ 사용 하려면 `android.permission.WRITE_EXTERNAL_STORAGE` ê¶Œí•œì´ ìžˆì–´ì•¼ 합니다. +* `cache`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë‚´ë¶€ ìºì‹œ 디렉터리 +* `cache-external`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ì™¸ë¶€ ìºì‹œ 디렉터리 +* `root`: ì „ì²´ 장치 íŒŒì¼ ì‹œìŠ¤í…œ + +안 드 로ì´ë“œëŠ” ë˜í•œ "파ì¼" íŒŒì¼ ì‹œìŠ¤í…œ ë‚´ì—서 "/ 문서 /" 하위 디렉토리를 나타내는 "문서" ë¼ëŠ” 특별 한 파ì¼ì„ ì§€ì› í•©ë‹ˆë‹¤. + +### iOS + +* `library`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë¼ì´ë¸ŒëŸ¬ë¦¬ 디렉터리 +* `documents`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë¬¸ì„œ 디렉토리 +* `cache`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ìºì‹œ 디렉터리 +* `bundle`: ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ ë²ˆë“¤; (ì½ê¸° ì „ìš©) 디스í¬ì— ì‘ìš© 프로그램 ìžì²´ì˜ 위치 +* `root`: ì „ì²´ 장치 íŒŒì¼ ì‹œìŠ¤í…œ + +기본ì ìœ¼ë¡œ ë¼ì´ë¸ŒëŸ¬ë¦¬ ë° ë¬¸ì„œ 디렉토리 iCloudì— ë™ê¸°í™” í•  수 있습니다. ë˜í•œ 2 ê°œì˜ ì¶”ê°€ì ì¸ íŒŒì¼ ì‹œìŠ¤í…œ, `library-nosync` ë° `documents-nosync`, ë‚´ 특별 한 ë™ê¸°í™” ë˜ì§€ ì•Šì€ ë””ë ‰í„°ë¦¬ë¥¼ 대표 하는 요청할 수 있습니다는 `/Library` ë˜ëŠ” `/Documents` íŒŒì¼ ì‹œìŠ¤í…œ. diff --git a/plugins/cordova-plugin-file/doc/ko/plugins.md b/plugins/cordova-plugin-file/doc/ko/plugins.md new file mode 100644 index 0000000..31b274d --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ko/plugins.md @@ -0,0 +1,101 @@ + + +# í”ŒëŸ¬ê·¸ì¸ ê°œë°œìžë¥¼ 위한 노트 + +ì´ ë…¸íŠ¸ëŠ” 주로 íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì„ 사용 하 ì—¬ íŒŒì¼ ì‹œìŠ¤í…œ í”ŒëŸ¬ê·¸ì¸ ì¸í„°íŽ˜ì´ìŠ¤ë¥¼ 작성 하 ê³  ì‹¶ì€ ì•ˆ 드 로ì´ë“œì™€ iOS 개발ìžë¥¼ 위한 것입니다. + +## 코르 ë„ìš° ë°” íŒŒì¼ ì‹œìŠ¤í…œ Url 사용 + +버전 1.0.0, ì´í›„ì´ í”ŒëŸ¬ê·¸ì¸ê³¼ Url 사용 하고있다는 `cdvfile` êµëŸ‰, 모든 í†µì‹ ì— ëŒ€ 한 ì œë„ ë³´ë‹¤ëŠ” ìžë°” ì›ì‹œ 장치 íŒŒì¼ ì‹œìŠ¤í…œ 경로 노출. + +ìžë°” 스í¬ë¦½íЏ 측면ì—서 즉 ê·¸ FileEntry ë° DirectoryEntry 개체 fullPath ì†ì„±ì„ HTML íŒŒì¼ ì‹œìŠ¤í…œì˜ ë£¨íŠ¸ì— ìƒëŒ€ì ìž…니다. FileEntry ë˜ëŠ” DirectoryEntry 개체를 ìˆ˜ë½ í•˜ëŠ” 플러그ì¸ì˜ ìžë°” API를 호출 해야 `.toURL()` ë‹¤ë¦¬ì— ê±¸ì³ ë„¤ì´í‹°ë¸Œ ì½”ë“œì— ì „ë‹¬ 하기 ì „ì— í•´ë‹¹ 개체ì—. + +### Cdvfile 변환: / / fileystem 경로 Url + +í”ŒëŸ¬ê·¸ì¸ íŒŒì¼ ì‹œìŠ¤í…œì„ ìž‘ì„± 하는 실제 íŒŒì¼ ì‹œìŠ¤í…œ ìœ„ì¹˜ì— ë°›ì€ íŒŒì¼ ì‹œìŠ¤í…œ URLì„ ë³€í™˜ í•  수 있습니다. ì´ë ‡ê²Œ, 네ì´í‹°ë¸Œ 플랫í¼ì— ë”°ë¼ ì—¬ëŸ¬ ë°©ë²•ì´ ìžˆë‹¤. + +기억 하는 ê²ƒì´ ì¤‘ìš” 하다 모든 `cdvfile://` Urlì€ ì‹¤ì œ íŒŒì¼ ìž¥ì¹˜ì— ë§¤í•‘. ì¼ë¶€ Url 파ì¼ì— ì˜í•´ 표현 ë˜ì§€ 않는 ë˜ëŠ” 심지어 ì›ê²© 리소스를 참조할 수 있는 ìž¥ì¹˜ì— ìžì‚°ì„ 참조할 수 있습니다. ì´ëŸ¬í•œ 가능성 ë•Œë¬¸ì— í”ŒëŸ¬ê·¸ì¸ ê²½ë¡œë¥¼ Urlì„ ë³€í™˜ 하려고 í•  때 다시 ì˜ë¯¸ 있는 ê²°ê³¼ ì–»ì„ ì§€ 여부를 í•­ìƒ í…ŒìŠ¤íŠ¸ 해야 합니다. + +#### 안 드 로ì´ë“œ + +안 드 로ì´ë“œ, 변환 하는 간단한 방법ì—는 `cdvfile://` URLì„ íŒŒì¼ ì‹œìŠ¤í…œ 경로 사용 하는 `org.apache.cordova.CordovaResourceApi` . `CordovaResourceApi`처리할 수 있는 여러 가지 방법ì—는 `cdvfile://` Url: + + webView í”ŒëŸ¬ê·¸ì¸ í´ëž˜ìФ CordovaResourceApi resourceApiì˜ ë©¤ë²„ì¸ = webView.getResourceApi(); + + 장치ì—ì´ íŒŒì¼ì„ 나타내는 file:/// URL 얻기 / / ê°™ì€ URL 변경 íŒŒì¼ Uri fileURLì— ë§¤í•‘í•  수 없는 경우 ë˜ëŠ” = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +ê·¸ê²ƒì€ ë˜í•œ íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì„ ì§ì ‘ 사용할 수 있습니다: + + 가져오기 org.apache.cordova.file.FileUtils; + 가져오기 org.apache.cordova.file.FileSystem; + 가져오기 java.net.MalformedURLException; + + í”ŒëŸ¬ê·¸ì¸ ê´€ë¦¬ìžì—서 íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì„ ì–»ì„ FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + 그것 ì‹œë„ ëŒ€ 한 경로 ì–»ì„ URLì„ ê°ì•ˆí•  때, {문ìžì—´ 경로 = filePlugin.filesystemPathForURL(cdvfileURL);} catch (MalformedURLException e) {/ / íŒŒì¼ ì‹œìŠ¤í…œ url ì¸ì‹ ë˜ì§€ 않았습니다} + + +경로를 변환 하는 `cdvfile://` URL: + + 가져오기 org.apache.cordova.file.LocalFilesystemURL; + + 장치 경로 대 한 LocalFilesystemURL 개체를 가져오기 / / cdvfile URL로 나타낼 수 없는 경우 null. + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path); + URL 개체 문ìžì—´ cdvfileURLì˜ ë¬¸ìžì—´ í‘œí˜„ì„ = url.toString(); + + +í”ŒëŸ¬ê·¸ì¸ íŒŒì¼ì„ 만들고 ê·¸ê²ƒì— ëŒ€ 한 FileEntry 개체를 반환 하려면, íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì„ 사용. + + JSON 구조를 JavaScriptì— ë°˜í™˜ì„ ìœ„í•œ ì ë‹¹ 한 반환 / /ì´ íŒŒì¼ì€ cdvfile URL로 표현 하는 경우 null. + JSONObject 항목 = filePlugin.getEntryForFile(file); + + +#### iOS + +IOSì—서 코르ë„ë°” ê°™ì€ ì‚¬ìš© 하지 않는 `CordovaResourceApi` 안 드 로ì´ë“œ ê°œë…. Ios, Url ë° íŒŒì¼ ì‹œìŠ¤í…œ 경로 ì‚¬ì´ ë³€í™˜ íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì„ 사용 해야 합니다. + + URL 문ìžì—´ CDVFilesystemURL * urlì—서 CDVFilesystem URL 개체를 가져오기 = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL]; + íŒŒì¼ NSString * ê²½ë¡œì— ë§¤í•‘í•  수 없는 경우 URL 개체 ë˜ëŠ” ì—†ìŒì— 대 한 경로 ì–»ì„ = [filePlugin filesystemPathForURL:url]; + + + 장치 경로 대 한 CDVFilesystem URL 개체를 가져오기 ë˜ëŠ” / / ì—†ìŒ cdvfile URL로 나타낼 수 없는 경우. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + URL 개체 NSString * cdvfileURLì˜ ë¬¸ìžì—´ í‘œí˜„ì„ = [url absoluteString]; + + +í”ŒëŸ¬ê·¸ì¸ íŒŒì¼ì„ 만들고 ê·¸ê²ƒì— ëŒ€ 한 FileEntry 개체를 반환 하려면, íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ì„ 사용. + + 장치 경로 대 한 CDVFilesystem URL 개체를 가져오기 ë˜ëŠ” / / ì—†ìŒ cdvfile URL로 나타낼 수 없는 경우. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + ìžë°” 스í¬ë¦½íЏ NSDictionary * 항목으로 ëŒì•„가려면 구조를 ì–»ì„ = [filePlugin makeEntryForLocalURL:url] + + +#### ìžë°” 스í¬ë¦½íЏ + +ìžë°” 스í¬ë¦½íЏì—는 `cdvfile://` FileEntry ë˜ëŠ” DirectoryEntry 개체ì—서 URL 호출 `.toURL()` 그것ì—: + + var cdvfileURL = entry.toURL(); + + +í”ŒëŸ¬ê·¸ì¸ ì‘답 처리기ì—서 실제 항목 개체로 반환 ëœ FileEntry 구조ì—서 변환 처리기 코드 해야 íŒŒì¼ í”ŒëŸ¬ê·¸ì¸ ê°€ì ¸ì˜¤ê³  새 개체를 만들: + + ì ì ˆ 한 항목 개체 var 항목; + 경우 (entryStruct.isDirectory) {항목 = 새 DirectoryEntry (entryStruct.name, entryStruct.fullPath, 새로운 FileSystem(entryStruct.filesystemName));} 다른 {항목 = 새로운 FileEntry (entryStruct.name, entryStruct.fullPath, 새로운 FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/pl/README.md b/plugins/cordova-plugin-file/doc/pl/README.md new file mode 100644 index 0000000..166c3ce --- /dev/null +++ b/plugins/cordova-plugin-file/doc/pl/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +Ten plugin implementuje API pliku, dziÄ™ki czemu dostÄ™p do odczytu i zapisu do plików znajdujÄ…cych siÄ™ na urzÄ…dzeniu. + +Ten plugin jest oparty na kilka specyfikacje, w tym: HTML5 File API + +Katalogi (nieistniejÄ…cego już) i System Najnowsze rozszerzenia: , chociaż wiÄ™kszość z ten plugin kod zostaÅ‚ napisany podczas wczeÅ›niejszych specyfikacji byÅ‚y aktualne: + +To również implementuje specyfikacjÄ™ FileWriter: + +Wykorzystania, prosimy odnieść siÄ™ do skaÅ‚y HTML5 doskonaÅ‚e [plików art.](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +Omówienie innych opcji przechowywania odnoszÄ… siÄ™ do Cordova z [magazynu przewodnik](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html). + +Ten plugin okreÅ›la globalne `cordova.file` obiektu. + +Chociaż w globalnym zasiÄ™gu, to nie dostÄ™pne dopiero po `deviceready` imprezie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Instalacja + + cordova plugin add cordova-plugin-file + + +## ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * Firefox OS + * iOS + * Windows Phone 7 i 8 * + * Windows 8 * + * Windows* + * PrzeglÄ…darka + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## Gdzie przechowywać pliki + +Od v1.2.0 znajdujÄ… siÄ™ adresy URL do katalogów ważne systemu plików. Każdy adres URL jest w formie *file:///path/to/spot/* i mogÄ… być konwertowane na `DirectoryEntry` za pomocÄ… `window.resolveLocalFileSystemURL()`. + + * `cordova.file.applicationDirectory`-Tylko do odczytu katalogu gdzie jest zainstalowana aplikacja. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.applicationStorageDirectory`-Katalogu obszaru izolowanego aplikacji; na iOS to miejsce jest tylko do odczytu (ale podkatalogów okreÅ›lonego [jak `/Documents` ] sÄ… odczytu i zapisu). Wszystkie dane zawarte w jest prywatnÄ… do aplikacji. ( *iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.dataDirectory`-TrwaÅ‚e i prywatne dane magazynowanie w izolowanym aplikacji przy użyciu pamiÄ™ci wewnÄ™trznej (na Android, jeÅ›li trzeba użyć zewnÄ™trznej pamiÄ™ci, należy użyć `.externalDataDirectory` ). Na iOS, Katalog ten nie jest zsynchronizowane z iCloud (za pomocÄ… `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.cacheDirectory`-Katalog dla plików buforowanych danych lub pliki, które aplikacji ponownie można Å‚atwo tworzyć. System operacyjny może usunąć te pliki, gdy urzÄ…dzenie dziaÅ‚a niski na przechowywanie, niemniej jednak aplikacje nie powinny polegać na OS, aby usunąć pliki tutaj. (*iOS*, *Android*, *BlackBerry 10*) + + * `cordova.file.externalApplicationStorageDirectory`-Stosowania przestrzeni na zewnÄ™trznej pamiÄ™ci masowej. (*Android*) + + * `cordova.file.externalDataDirectory`-Gdzie umieÅ›cić pliki danych specyficznych dla aplikacji na zewnÄ™trznej pamiÄ™ci masowej. (*Android*) + + * `cordova.file.externalCacheDirectory`-PamiÄ™ci podrÄ™cznej aplikacji na zewnÄ™trznej pamiÄ™ci masowej. (*Android*) + + * `cordova.file.externalRootDirectory`-KorzeÅ„ zewnÄ™trznej pamiÄ™ci masowej (karty SD). (*Android*, *BlackBerry 10*) + + * `cordova.file.tempDirectory`-Temp katalogu systemu operacyjnego można wyczyÅ›cić w bÄ™dzie. Nie należy polegać na OS wobec usunąć ten katalog; aplikacji należy zawsze usunąć pliki jako obowiÄ…zujÄ…ce. (*iOS*) + + * `cordova.file.syncedDataDirectory`-Posiada pliki specyficzne dla aplikacji, które powinny być zsynchronizowane (np. do iCloud). (*iOS*) + + * `cordova.file.documentsDirectory`-Pliki prywatne do aplikacji, ale że majÄ… znaczenie dla innych aplikacji (np. plików pakietu Office). (*iOS*) + + * `cordova.file.sharedDirectory`-Pliki dostÄ™pne na caÅ‚ym Å›wiecie do wszystkich aplikacji (*BlackBerry 10*) + +## Plik System ukÅ‚ady + +Chociaż technicznie implementacyjnym, może być bardzo przydatne wiedzieć, jak `cordova.file.*` wÅ‚aÅ›ciwoÅ›ci mapy fizycznej Å›cieżki na prawdziwe urzÄ…dzenie. + +### iOS ukÅ‚ad systemu plików + +| Åšcieżka urzÄ…dzenia | `Cordova.File.*` | `iosExtraFileSystems` | r/w? | trwaÅ‚e? | CzyÅ›ci OS | Synchronizacja | prywatne | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:----:|:--------:|:-------------:|:--------------:|:--------:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/D! | N/D! | N/D! | Tak | +|    `appname.app/` | applicationDirectory | pakiet | r | N/D! | N/D! | N/D! | Tak | +|       `www/` | - | - | r | N/D! | N/D! | N/D! | Tak | +|    `Documents/` | documentsDirectory | dokumenty | r/w | Tak | Nr | Tak | Tak | +|       `NoCloud/` | - | dokumenty nosync | r/w | Tak | Nr | Nr | Tak | +|    `Library` | - | Biblioteka | r/w | Tak | Nr | Tak? | Tak | +|       `NoCloud/` | dataDirectory | Biblioteka nosync | r/w | Tak | Nr | Nr | Tak | +|       `Cloud/` | syncedDataDirectory | - | r/w | Tak | Nr | Tak | Tak | +|       `Caches/` | cacheDirectory | pamiÄ™ci podrÄ™cznej | r/w | Tak * | Yes**\* | Nr | Tak | +|    `tmp/` | tempDirectory | - | r/w | No** | Yes**\* | Nr | Tak | + +\ * Pliki utrzymywaÅ‚y aplikacja zostanie ponownie uruchomiony i uaktualnienia, ale w tym katalogu mogÄ… być rozliczone przy każdym OS pragnieÅ„. Aplikacji należy umożliwić odtworzenie treÅ›ci, które mogÄ… być usuniÄ™te. + +** Plików może utrzymywać siÄ™ po ponownym uruchomieniu aplikacji, ale nie opierajÄ… siÄ™ na tym zachowaniu. Pliki nie sÄ… gwarantowane w aktualizacji. Aplikacji należy usunąć pliki z tego katalogu, gdy ma to zastosowanie, ponieważ system operacyjny nie gwarantuje Kiedy (lub nawet jeÅ›li) te pliki zostanÄ… usuniÄ™te. + +**\ * System operacyjny może wyczyÅ›cić zawartość tego katalogu, gdy czuje, że jest to konieczne, ale nie powoÅ‚ywać siÄ™ na to. Należy wyczyÅ›cić ten katalog jako odpowiednie dla aplikacji. + +### UkÅ‚ad systemu Android plików + +| Åšcieżka urzÄ…dzenia | `Cordova.File.*` | `AndroidExtraFileSystems` | r/w? | trwaÅ‚e? | CzyÅ›ci OS | prywatne | +|:------------------------------------------------ |:----------------------------------- |:------------------------------- |:----:|:-------:|:---------:|:--------:| +| `file:///android_asset/` | applicationDirectory | | r | N/D! | N/D! | Tak | +| `/Data/danych/< Aplikacja id > /` | applicationStorageDirectory | - | r/w | N/D! | N/D! | Tak | +|    `cache` | cacheDirectory | pamiÄ™ci podrÄ™cznej | r/w | Tak | Yes\ * | Tak | +|    `files` | dataDirectory | pliki | r/w | Tak | Nr | Tak | +|       `Documents` | | dokumenty | r/w | Tak | Nr | Tak | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Tak | Nr | Nr | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Tak | Nr | Nr | +|       `cache` | externalCacheDirectry | zewnÄ™trznych pamiÄ™ci podrÄ™cznej | r/w | Tak | No** | Nr | +|       `files` | externalDataDirectory | zewnÄ™trznych plików | r/w | Tak | Nr | Nr | + +\ * OS może okresowo usunąć ten katalog, ale nie opierajÄ… siÄ™ na tym zachowaniu. Wyczyść zawartość tego katalogu jako odpowiednie dla danej aplikacji. Należy użytkownik przeczyÅ›cić pamięć podrÄ™cznÄ… rÄ™cznie, zawartość w tym katalogu sÄ… usuwane. + +** OS nie usunąć ten katalog automatycznie; JesteÅ› odpowiedzialny za zarzÄ…dzanie zawartość siebie. Należy użytkownik przeczyÅ›cić pamięć podrÄ™cznÄ… rÄ™cznie, zawartość katalogu sÄ… usuwane. + +**Uwaga**: JeÅ›li nie mogÄ… być montowane pamiÄ™ci masowej, wÅ‚aÅ›ciwoÅ›ci `cordova.file.external*` sÄ… `wartoÅ›ci null`. + +### UkÅ‚ad systemu plików blackBerry 10 + +| Åšcieżka urzÄ…dzenia | `Cordova.File.*` | r/w? | trwaÅ‚e? | CzyÅ›ci OS | prywatne | +|:----------------------------------------------------------- |:--------------------------- |:----:|:-------:|:---------:|:--------:| +| `file:///accounts/1000/AppData/ < id aplikacji > /` | applicationStorageDirectory | r | N/D! | N/D! | Tak | +|    `app/native` | applicationDirectory | r | N/D! | N/D! | Tak | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | Nr | Tak | Tak | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Tak | Nr | Tak | +| `file:///accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Tak | Nr | Nr | +| `file:///accounts/1000/Shared` | sharedDirectory | r/w | Tak | Nr | Nr | + +*Uwaga*: gdy aplikacja jest rozmieszczana do pracy obwodu, wszystkie Å›cieżki sÄ… wzglÄ™dne do /accounts/1000-enterprise. + +## Dziwactwa Androida + +### Lokalizacja przechowywania trwaÅ‚ych Android + +Istnieje wiele prawidÅ‚owe lokalizacje do przechowywania trwaÅ‚ych plików na telefonie z systemem Android. Zobacz [tÄ™ stronÄ™](http://developer.android.com/guide/topics/data/data-storage.html) do szerokiej dyskusji o różnych możliwoÅ›ciach. + +Poprzednie wersje pluginu wybrać lokalizacjÄ™ plików tymczasowych i trwaÅ‚e podczas uruchamiania, czy urzÄ…dzenie twierdziÅ‚, że karta SD (lub równoważne magazynowanie podzia³) byÅ‚ montowany w oparciu. Czy karta SD zostaÅ‚a zamontowana, czy duży wewnÄ™trzny magazynowanie podzia³ byÅ‚ dostÄ™pny (takie jak na Nexus urzÄ…dzenia,) a nastÄ™pnie trwaÅ‚e pliki bÄ™dÄ… przechowywane w katalogu głównego tego miejsca. OznaczaÅ‚o to, że wszystkie aplikacje Cordova może Zobacz wszystkie pliki dostÄ™pne na karcie. + +JeÅ›li karta SD nie byÅ‚ dostÄ™pny, a nastÄ™pnie poprzednie wersje bÄ™dzie przechowywać dane w `/data/data/`, która izoluje aplikacje od siebie, ale nadal może spowodować danych, które majÄ… być współużytkowane przez użytkowników. + +Teraz jest możliwe, aby zdecydować, czy do przechowywania plików w lokalizacji magazynu plików, lub przy użyciu poprzednich logiki, z preferencjÄ… w aplikacji w pliku `config.xml`. Aby to zrobić, Dodaj jednÄ… z tych dwóch linii do `pliku config.xml`: + + + + + + +Bez tej linii wtyczki pliku bÄ™dzie używać `Compatibility` jako domyÅ›lny. JeÅ›li znacznik preferencji jest obecny i to nie jednÄ… z tych wartoÅ›ci, aplikacja nie zostanie uruchomiona. + +JeÅ›li aplikacja wczeÅ›niej zostaÅ‚o wysÅ‚ane do użytkowników, przy użyciu starszych (pre-1.0) wersjÄ™ tego pluginu i ma zapisane na dysku pliki w trwaÅ‚ych plików, a nastÄ™pnie należy ustawić preferencje do `Compatibility`. Przełączania lokalizacji do "Internal" oznacza, że istniejÄ…cych użytkowników, którzy ich aplikacja może być niesÅ‚abnÄ…cy wobec dostÄ™p ich wczeÅ›niej zapisane pliki, w zależnoÅ›ci od ich urzÄ…dzenie. + +JeÅ›li aplikacja jest nowy, lub ma nigdy wczeÅ›niej przechowywane pliki w systemie plików trwaÅ‚e, ustawienie `Internal` generalnie jest zalecane. + +### Powolny cyklicznych operacji dla /android_asset + +Lista katalogów aktywów jest bardzo powolny na Android. Można przyspieszyć to siÄ™ jednak przez dodanie `src/android/build-extras.gradle` do katalogu głównego projektu android (również wymaga cordova-android@4.0.0 lub wiÄ™kszej). + +## Dziwactwa iOS + + * `cordova.file.applicationStorageDirectory`jest tylko do odczytu; próby przechowywania plików w katalogu głównym zakoÅ„czy siÄ™ niepowodzeniem. Użyj jednego z innych `cordova.file.*` wÅ‚aÅ›ciwoÅ›ci zdefiniowane dla iOS (tylko `applicationDirectory` i `applicationStorageDirectory` sÄ… tylko do odczytu). + * `FileReader.readAsText(blob, encoding)` + * `encoding`Parametr nie jest obsÅ‚ugiwana, i kodowanie UTF-8 jest zawsze w efekcie. + +### iOS lokalizacja przechowywania trwaÅ‚ych + +IstniejÄ… dwa ważne miejsca trwaÅ‚e pliki na urzÄ…dzenia iOS: katalogu dokumentów i katalogu biblioteki. Poprzednie wersje pluginu tylko kiedykolwiek przechowywane trwaÅ‚e pliki w katalogu dokumentów. To miaÅ‚ ten efekt uboczny od rozpoznawalnoÅ›ci wszystkie pliki aplikacji w iTunes, który byÅ‚ czÄ™sto niezamierzone, zwÅ‚aszcza dla aplikacji, które obsÅ‚ugujÄ… wiele maÅ‚ych plików, zamiast produkuje kompletne dokumenty do wywozu, który jest przeznaczenie katalogu. + +Teraz jest możliwe, aby zdecydować, czy do przechowywania plików w dokumentach lub katalogu biblioteki, z preferencjÄ… w pliku `config.xml` aplikacji. Aby to zrobić, Dodaj jednÄ… z tych dwóch linii do `pliku config.xml`: + + + + + + +Bez tej linii wtyczki pliku bÄ™dzie używać `Compatibility` jako domyÅ›lny. JeÅ›li znacznik preferencji jest obecny i to nie jednÄ… z tych wartoÅ›ci, aplikacja nie zostanie uruchomiona. + +JeÅ›li aplikacja wczeÅ›niej zostaÅ‚o wysÅ‚ane do użytkowników, przy użyciu starszych (pre-1.0) wersjÄ™ tego pluginu i ma zapisane na dysku pliki w trwaÅ‚ych plików, a nastÄ™pnie należy ustawić preferencje do `Compatibility`. Przełączania lokalizacji do `Library` oznaczaÅ‚oby, że istniejÄ…cych użytkowników, którzy ich aplikacja bÄ™dzie niesÅ‚abnÄ…cy wobec dostÄ™p ich wczeÅ›niej zapisane pliki. + +JeÅ›li aplikacja jest nowy, lub nigdy wczeÅ›niej przechowywane pliki w trwaÅ‚ych plików, ustawieÅ„ `Library` ogólnie jest zalecane. + +## Firefox OS dziwactwa + +API systemu plików nie jest obsÅ‚ugiwany macierzyÅ›cie przez Firefox OS i jest zaimplementowany jako podkÅ‚adki na indexedDB. + + * Nie usuwajÄ…c niepuste katalogi + * Nie obsÅ‚uguje metadane dla katalogów + * Metody `copyTo` i `moveTo` nie obsÅ‚uguje katalogi + +ObsÅ‚ugiwane sÄ… nastÄ™pujÄ…ce Å›cieżki danych: * `applicationDirectory` - używa `xhr`, aby uzyskać lokalne pliki, które sÄ… pakowane z aplikacji. * `dataDirectory` - na trwaÅ‚e dane specyficzne dla aplikacji pliki. * `cacheDirectory` - buforowanych plików, które powinny przetrwać ponowne uruchomienie aplikacji (aplikacje nie powinny polegać na OS, aby usunąć pliki tutaj). + +## Quirks przeglÄ…darki + +### Wspólne dziwactw i uwagi + + * Każda przeglÄ…darka używa wÅ‚asnej piaskownicy plików. IE i Firefox Użyj IndexedDB jako podstawa. Wszystkie przeglÄ…darki za pomocÄ… ukoÅ›nika jako separatora katalogu Å›cieżka. + * Wpisy w katalogu majÄ… być tworzone sukcesywnie. Na przykÅ‚ad wywoÅ‚anie `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` zakoÅ„czy siÄ™ niepowodzeniem, jeÅ›li nie istnieje dir1. + * Plugin żądania użytkownika uprawnieÅ„ do używania trwaÅ‚e przechowywanie przy pierwszym uruchomieniu aplikacji. + * Wtyczka obsÅ‚uguje `cdvfile://localhost` (lokalne zasoby) tylko. Czyli zewnÄ™trznych zasobów nie sÄ… obsÅ‚ugiwane przez `cdvfile`. + * Plugin nie nastÄ™pować po ["Plik API systemu nazw 8.3 ograniczenia"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). + * Obiektu BLOB i pliku "`close` funkcja nie jest obsÅ‚ugiwana. + * `FileSaver` i `BlobBuilder` nie sÄ… obsÅ‚ugiwane przez ten plugin i nie ma artykułów. + * Plugin nie obsÅ‚uguje `requestAllFileSystems`. Ta funkcja jest również brak w specyfikacji. + * Wpisy w katalogu nie zostanÄ… usuniÄ™te, jeÅ›li używasz `create: true` flaga dla istniejÄ…cego katalogu. + * Pliki utworzone za pomocÄ… konstruktora nie sÄ… obsÅ‚ugiwane. Zamiast tego należy użyć metody entry.file. + * Każda przeglÄ…darka używa wÅ‚asnej postaci URL odwoÅ‚ania blob. + * `readAsDataURL` funkcja jest obsÅ‚ugiwana, ale mediatype w Chrome zależy od wejÅ›cia z rozszerzeniem, mediatype w IE zawsze jest pusty (który jest taki sam jak `zwykÅ‚y tekst` wedÅ‚ug specyfikacji), mediatype w Firefox jest zawsze `aplikacji/oktet strumieÅ„`. Na przykÅ‚ad, jeÅ›li treść jest `abcdefg` Firefox wraca z `danych: stosowanie / octet-stream, base64, YWJjZGVmZw ==`, czyli zwraca `danych:; base64, YWJjZGVmZw ==`, Chrome zwraca `danych: < mediatype w zależnoÅ›ci od rozszerzenia nazwy; > base64, YWJjZGVmZw ==`. + * `toInternalURL` zwraca Å›cieżkÄ™ w postaci `file:///persistent/path/to/entry` (Firefox, IE). Chrom zwraca Å›cieżkÄ™ w postaci `cdvfile://localhost/persistent/file`. + +### Dziwactwa chrom + + * Chrom plików nie jest od razu gotowy po gotowe urzÄ…dzenia. Jako rozwiÄ…zanie alternatywne można subskrybować zdarzenia `filePluginIsReady`. PrzykÅ‚ad: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +Funkcja `window.isFilePluginReadyRaised` sÅ‚uży do sprawdzenia, czy zdarzenie już zostaÅ‚a podniesiona. -kwoty plików tymczasowych i trwaÅ‚e window.requestFileSystem nie sÄ… ograniczone w Chrome. -W celu zwiÄ™kszenia trwaÅ‚ego magazynu w Chrome, należy wywoÅ‚ać metodÄ™ `window.initPersistentFileSystem`. DomyÅ›lnie trwaÅ‚e dyskowa jest 5 MB. -Chrome wymaga `--pozwalajÄ…--dostÄ™p z plików` uruchomić argument na poparcie API za poÅ›rednictwem protokoÅ‚u `file:///`. -`Plik` obiekt bÄ™dzie nie zmieniÅ‚o jeÅ›li flaga `{create:true}` gdy już istniejÄ…cy `wpis`. -wydarzenia `zwrotu` wÅ‚aÅ›ciwość jest zestaw true w Chrome. Jest to sprzeczne ze [specyfikacji](http://dev.w3.org/2009/dap/file-system/file-writer.html). -Funkcja `toURL` w Chrome zwraca `plików:`-poprzedzona Å›cieżkÄ… w zależnoÅ›ci od aplikacji hosta. Na przykÅ‚ad, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -wynik funkcji `toURL` nie zawierajÄ… ukoÅ›nika w wpis w katalogu. Chrom usuwa katalogi z ciąć doczepiane adresów URL poprawnie choć. -Metoda `resolveLocalFileSystemURL` wymaga przychodzÄ…cych `url` majÄ… prefiks `plików`. Na przykÅ‚ad parametr `adresu url` do `resolveLocalFileSystemURL` powinny być w formie `filesystem:file:///persistent/somefile.txt`, w przeciwieÅ„stwie do formularza `file:///persistent/somefile.txt` w Android. -PrzestarzaÅ‚e `toNativeURL` funkcja nie jest obsÅ‚ugiwana i nie tylko. -Funkcja `setMetadata` jest nie podane w specyfikacji i nie jest obsÅ‚ugiwane. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast SYNTAX_ERR(code: 8) na żądanie nieistniejÄ…cÄ… plików. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast PATH_EXISTS_ERR(code: 12) próbuje stworzyć wyłącznie pliku lub katalogu, który już istnieje. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast NO_MODIFICATION_ALLOWED_ERR(code: 6) na próby wywoÅ‚ania removeRecursively w głównym systemie plików. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast NOT_FOUND_ERR(code: 1) na trudny do katalogu moveTo, który nie istnieje. + +### Na bazie IndexedDB impl dziwactw (Firefox i IE) + + * `.` i `.` nie sÄ… obsÅ‚ugiwane. + * IE obsÅ‚uguje `file:///`-tryb; tylko obsÅ‚ugiwane tryb jest obsÅ‚ugiwany (http://localhost:xxxx). + * Rozmiar plików Firefox nie jest ograniczona, ale każde rozszerzenie 50MB zwróci użytkownikowi uprawnienia. IE10 pozwala maksymalnie 10mb połączone "appcache" i IndexedDB używane w implementacji systemu plików bez monitowania, gdy trafisz na tym poziomie, które uzyskasz, jeÅ›li chcesz mogÅ‚a ona zostać zwiÄ™kszony do max 250mb na stronie. WiÄ™c `rozmiar` parametru funkcja `requestFileSystem` nie wpÅ‚ywa na system plików Firefox i IE. + * `readAsBinaryString` funkcja nie jest okreÅ›lona w specyfikacji i nie obsÅ‚ugiwane w IE i nie tylko. + * `File.Type` ma zawsze wartość null. + * Nie należy utworzyć wpis za pomocÄ… DirectoryEntry wystÄ…pienie wynik wywoÅ‚ania zwrotnego, który zostaÅ‚ usuniÄ™ty. W przeciwnym razie dostaniesz wpisem"wiszÄ…ce". + * Zanim bÄ™dzie można przeczytać plik, który zostaÅ‚ napisany tylko trzeba uzyskać nowe wystÄ…pienie tego pliku. + * Funkcja `setMetadata`, która nie jest okreÅ›lona w specyfikacji obsÅ‚uguje tylko zmian pola `modificationTime`. + * `copyTo` i `moveTo` funkcji nie obsÅ‚uguje katalogi. + * Metadanych w katalogów nie jest obsÅ‚ugiwana. + * Zarówno Entry.remove i directoryEntry.removeRecursively nie usuwajÄ…c niepuste katalogi - katalogi sÄ… usuwane sÄ… czyszczone z treÅ›ci zamiast. + * `abort` i `truncate` funkcje nie sÄ… obsÅ‚ugiwane. + * zdarzenia postÄ™pu nie sÄ… zwalniani. Na przykÅ‚ad to obsÅ‚uga bÄ™dzie nie wykonywane: + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## Uaktualniania notatek + +W v1.0.0 tego pluginu struktury `FileEntry` i `DirectoryEntry` zmieniÅ‚y siÄ™ wiÄ™cej zgodnie z opublikowanÄ… specyfikacjÄ…. + +Poprzednie wersje (pre-1.0.0) plugin przechowywane urzÄ…dzenia bezwzglÄ™dna plik lokalizacja we wÅ‚aÅ›ciwoÅ›ci `fullPath` `wpis` obiektów. Te Å›cieżki zazwyczaj bÄ™dzie wyglÄ…dać + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Te Å›cieżki byÅ‚y także zwracany przez metodÄ™ `toURL()` `Entry` obiektów. + +Z v1.0.0 atrybut `fullPath` jest Å›cieżkÄ… do pliku, *wzglÄ™dem katalogu głównego systemu plików HTML*. Tak powyżej Å›cieżki bÄ™dzie teraz zarówno być reprezentowane przez obiekt `FileEntry` z `fullPath` o + + /path/to/file + + +JeÅ›li aplikacja dziaÅ‚a z Å›cieżki bezwzglÄ™dnej urzÄ…dzeÅ„, i możesz wczeÅ›niej źródÅ‚o tych Å›cieżek przez wÅ‚aÅ›ciwość `fullPath` `wpis` obiektów, należy zaktualizować kod, aby zamiast tego użyj `entry.toURL()`. + +Dla wstecznej kompatybilnoÅ›ci, Metoda `resolveLocalFileSystemURL()` bÄ™dzie zaakceptować urzÄ…dzenia Å›cieżka bezwzglÄ™dna i zwróci obiekt `Entry` odpowiadajÄ…cy, tak dÅ‚ugo, jak ten plik istnieje w albo `TEMPORARY` lub `PERSISTENT` systemy plików. + +To szczególnie zostaÅ‚ problem z pluginem transferu plików, które poprzednio używane Å›cieżki bezwzglÄ™dnej urzÄ…dzeÅ„ (i wciąż można je przyjąć). ZostaÅ‚a zaktualizowana do pracy poprawnie z adresów URL plików, wiÄ™c wymiana `entry.fullPath` z `entry.toURL()` powinno rozwiÄ…zać wszelkie problemy dostawanie ten plugin do pracy z plików w pamiÄ™ci urzÄ…dzenia. + +W v1.1.0 wartość zwracana przez `toURL()` zostaÅ‚ zmieniony (patrz \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) zwraca adres URL absolutnej "file://". wszÄ™dzie tam, gdzie jest to możliwe. Aby zapewnić ' cdvfile:'-URL można użyć `toInternalURL()` teraz. Ta metoda zwróci teraz adresy URL plików formularza + + cdvfile://localhost/persistent/path/to/file + + +który sÅ‚uży do jednoznacznej identyfikacji pliku. + +## Wykaz kodów błędów i ich znaczenie + +Gdy błąd jest generowany, jeden z nastÄ™pujÄ…cych kodów bÄ™dzie sÅ‚użyć. + +| Kod | StaÅ‚a | +| ---:|:----------------------------- | +| 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` | + +## Konfigurowanie wtyczka (opcjonalny) + +Zestaw dostÄ™pnych plików może być skonfigurowany na platformie. Zarówno iOS i Android Tag w `pliku config.xml`, których nazwy plików do instalacji. DomyÅ›lnie włączone sÄ… wszystkie korzenie systemu plików. + + + + + +### Android + + * `files`: katalogu przechowywania plików aplikacji + * `files-external`: katalog aplikacji zewnÄ™trznych plików + * `sdcard`: katalog globalny plik zewnÄ™trzny (to jest głównym karty SD, jeÅ›li jedna jest zainstalowana). Musi mieć uprawnienia `android.permission.WRITE_EXTERNAL_STORAGE` wobec używać ten. + * `cache`: katalogu wewnÄ™trznej pamiÄ™ci podrÄ™cznej aplikacji + * `cache-external`: katalogu aplikacji zewnÄ™trznych pamiÄ™ci podrÄ™cznej + * `root`: caÅ‚e urzÄ…dzenie systemu plików + +Android obsÅ‚uguje również specjalnych plików o nazwie "dokumenty", który reprezentuje podkatalog "/ dokumenty /" w ramach systemu plików "pliki". + +### iOS + + * `library`: katalog biblioteki aplikacji + * `documents`: dokumenty katalogu aplikacji + * `cache`: katalogu pamiÄ™ci podrÄ™cznej aplikacji + * `bundle`: pakiet aplikacji; Lokalizacja aplikacji na dysku (tylko do odczytu) + * `root`: caÅ‚e urzÄ…dzenie systemu plików + +DomyÅ›lnie katalogi biblioteki i dokumenty mogÄ… być synchronizowane iCloud. Można również zażądać dwóch dodatkowych plików, `library-nosync` i `documents-nosync`, które stanowiÄ… specjalny katalog nie zsynchronizowane w `/Library` lub systemu plików `/Documents`. \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/pl/index.md b/plugins/cordova-plugin-file/doc/pl/index.md new file mode 100644 index 0000000..1ccb330 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/pl/index.md @@ -0,0 +1,338 @@ + + +# cordova-plugin-file + +Ten plugin implementuje API pliku, dziÄ™ki czemu dostÄ™p do odczytu i zapisu do plików znajdujÄ…cych siÄ™ na urzÄ…dzeniu. + +Ten plugin jest oparty na kilka specyfikacje, w tym: HTML5 File API + +Katalogi (nieistniejÄ…cego już) i System Najnowsze rozszerzenia: , chociaż wiÄ™kszość z ten plugin kod zostaÅ‚ napisany podczas wczeÅ›niejszych specyfikacji byÅ‚y aktualne: + +To również implementuje specyfikacjÄ™ FileWriter: + +Wykorzystania, prosimy odnieść siÄ™ do skaÅ‚y HTML5 doskonaÅ‚e [plików art.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +Omówienie innych opcji przechowywania odnoszÄ… siÄ™ do Cordova z [magazynu przewodnik][2]. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +Ten plugin okreÅ›la globalne `cordova.file` obiektu. + +Chociaż w globalnym zasiÄ™gu, to nie dostÄ™pne dopiero po `deviceready` imprezie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## Instalacja + + cordova plugin add cordova-plugin-file + + +## ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 i 8 * +* Windows 8 * +* PrzeglÄ…darka + +* *Nie obsÅ‚ugujÄ… tych platform, `FileReader.readAsArrayBuffer` ani `FileWriter.write(blob)`.* + +## Gdzie przechowywać pliki + +Od v1.2.0 znajdujÄ… siÄ™ adresy URL do katalogów ważne systemu plików. Każdy adres URL jest w formie *file:///path/to/spot/* i mogÄ… być konwertowane na `DirectoryEntry` za pomocÄ… `window.resolveLocalFileSystemURL()`. + +* `cordova.file.applicationDirectory`-Tylko do odczytu katalogu gdzie jest zainstalowana aplikacja. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.applicationStorageDirectory`-Katalogu obszaru izolowanego aplikacji; na iOS to miejsce jest tylko do odczytu (ale podkatalogów okreÅ›lonego [jak `/Documents` ] sÄ… odczytu i zapisu). Wszystkie dane zawarte w jest prywatnÄ… do aplikacji. ( *iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.dataDirectory`-TrwaÅ‚e i prywatne dane magazynowanie w izolowanym aplikacji przy użyciu pamiÄ™ci wewnÄ™trznej (na Android, jeÅ›li trzeba użyć zewnÄ™trznej pamiÄ™ci, należy użyć `.externalDataDirectory` ). Na iOS, Katalog ten nie jest zsynchronizowane z iCloud (za pomocÄ… `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.cacheDirectory`-Katalog dla plików buforowanych danych lub pliki, które aplikacji ponownie można Å‚atwo tworzyć. System operacyjny może usunąć te pliki, gdy urzÄ…dzenie dziaÅ‚a niski na przechowywanie, niemniej jednak aplikacje nie powinny polegać na OS, aby usunąć pliki tutaj. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.externalApplicationStorageDirectory`-Stosowania przestrzeni na zewnÄ™trznej pamiÄ™ci masowej. (*Android*) + +* `cordova.file.externalDataDirectory`-Gdzie umieÅ›cić pliki danych specyficznych dla aplikacji na zewnÄ™trznej pamiÄ™ci masowej. (*Android*) + +* `cordova.file.externalCacheDirectory`-PamiÄ™ci podrÄ™cznej aplikacji na zewnÄ™trznej pamiÄ™ci masowej. (*Android*) + +* `cordova.file.externalRootDirectory`-KorzeÅ„ zewnÄ™trznej pamiÄ™ci masowej (karty SD). (*Android*, *BlackBerry 10*) + +* `cordova.file.tempDirectory`-Temp katalogu systemu operacyjnego można wyczyÅ›cić w bÄ™dzie. Nie należy polegać na OS wobec usunąć ten katalog; aplikacji należy zawsze usunąć pliki jako obowiÄ…zujÄ…ce. (*iOS*) + +* `cordova.file.syncedDataDirectory`-Posiada pliki specyficzne dla aplikacji, które powinny być zsynchronizowane (np. do iCloud). (*iOS*) + +* `cordova.file.documentsDirectory`-Pliki prywatne do aplikacji, ale że majÄ… znaczenie dla innych aplikacji (np. plików pakietu Office). (*iOS*) + +* `cordova.file.sharedDirectory`-Pliki dostÄ™pne na caÅ‚ym Å›wiecie do wszystkich aplikacji (*BlackBerry 10*) + +## Plik System ukÅ‚ady + +Chociaż technicznie implementacyjnym, może być bardzo przydatne wiedzieć, jak `cordova.file.*` wÅ‚aÅ›ciwoÅ›ci mapy fizycznej Å›cieżki na prawdziwe urzÄ…dzenie. + +### iOS ukÅ‚ad systemu plików + +| Åšcieżka urzÄ…dzenia | `Cordova.File.*` | `iosExtraFileSystems` | r/w? | trwaÅ‚e? | CzyÅ›ci OS | Synchronizacja | prywatne | +|:-------------------------------------------- |:--------------------------- |:--------------------- |:----:|:-------:|:-----------:|:--------------:|:--------:| +| `/ var/mobile/Applications/< UUID > /` | applicationStorageDirectory | - | r | N/D! | N/D! | N/D! | Tak | +|    `appname.app/` | applicationDirectory | pakiet | r | N/D! | N/D! | N/D! | Tak | +|       `www/` | - | - | r | N/D! | N/D! | N/D! | Tak | +|    `Documents/` | documentsDirectory | dokumenty | r/w | Tak | Nr | Tak | Tak | +|       `NoCloud/` | - | dokumenty nosync | r/w | Tak | Nr | Nr | Tak | +|    `Library` | - | Biblioteka | r/w | Tak | Nr | Tak? | Tak | +|       `NoCloud/` | dataDirectory | Biblioteka nosync | r/w | Tak | Nr | Nr | Tak | +|       `Cloud/` | syncedDataDirectory | - | r/w | Tak | Nr | Tak | Tak | +|       `Caches/` | cacheDirectory | pamiÄ™ci podrÄ™cznej | r/w | Tak * | Tak * * *| | Nr | Tak | +|    `tmp/` | tempDirectory | - | r/w | Nie * * | Tak * * *| | Nr | Tak | + +* Pliki utrzymywaÅ‚y aplikacja zostanie ponownie uruchomiony i uaktualnienia, ale w tym katalogu mogÄ… być rozliczone, gdy OS pragnienia. Aplikacji powinny być w stanie odtworzyć zawartość, która może być usuniÄ™ta. + +* * Plików może utrzymywać siÄ™ po ponownym uruchomieniu aplikacji, ale nie opierajÄ… siÄ™ na tym zachowaniu. Pliki nie sÄ… gwarantowane w aktualizacji. Aplikacji należy usunąć pliki z tego katalogu, gdy ma to zastosowanie, ponieważ system operacyjny nie gwarantuje Kiedy (lub nawet jeÅ›li) te pliki zostanÄ… usuniÄ™te. + +* * *| System operacyjny może wyczyÅ›cić zawartość w tym katalogu, gdy czuje, że jest to konieczne, ale nie powoÅ‚ywać siÄ™ na to. Należy wyczyÅ›cić ten katalog jako odpowiednie dla aplikacji. + +### UkÅ‚ad systemu Android plików + +| Åšcieżka urzÄ…dzenia | `Cordova.File.*` | `AndroidExtraFileSystems` | r/w? | trwaÅ‚e? | CzyÅ›ci OS | prywatne | +|:--------------------------------------- |:----------------------------------- |:------------------------------- |:----:|:-------:|:---------:|:--------:| +| `file:///android_asset/` | applicationDirectory | | r | N/D! | N/D! | Tak | +| `/Data/danych/< Aplikacja id > /` | applicationStorageDirectory | - | r/w | N/D! | N/D! | Tak | +|    `cache` | cacheDirectory | pamiÄ™ci podrÄ™cznej | r/w | Tak | Tak * | Tak | +|    `files` | dataDirectory | pliki | r/w | Tak | Nr | Tak | +|       `Documents` | | dokumenty | r/w | Tak | Nr | Tak | +| `< sdcard > /` | externalRootDirectory | sdcard | r/w | Tak | Nr | Nr | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Tak | Nr | Nr | +|       `cache` | externalCacheDirectry | zewnÄ™trznych pamiÄ™ci podrÄ™cznej | r/w | Tak | Nie * * | Nr | +|       `files` | externalDataDirectory | zewnÄ™trznych plików | r/w | Tak | Nr | Nr | + +* System operacyjny może okresowo usunąć ten katalog, ale nie opierajÄ… siÄ™ na tym zachowaniu. Wyczyść zawartość tego katalogu jako odpowiednie dla danej aplikacji. Należy użytkownik przeczyÅ›cić pamięć podrÄ™cznÄ… rÄ™cznie, zawartość w tym katalogu sÄ… usuwane. + +* * System operacyjny nie usunąć ten katalog automatycznie; JesteÅ› odpowiedzialny za zarzÄ…dzanie zawartość siebie. Należy użytkownik przeczyÅ›cić pamięć podrÄ™cznÄ… rÄ™cznie, zawartość katalogu sÄ… usuwane. + +**Uwaga**: JeÅ›li nie mogÄ… być montowane pamiÄ™ci masowej, wÅ‚aÅ›ciwoÅ›ci `cordova.file.external*` sÄ… `wartoÅ›ci null`. + +### UkÅ‚ad systemu plików blackBerry 10 + +| Åšcieżka urzÄ…dzenia | `Cordova.File.*` | r/w? | trwaÅ‚e? | CzyÅ›ci OS | prywatne | +|:--------------------------------------------------------- |:--------------------------- |:----:|:-------:|:---------:|:--------:| +| `file:///accounts/1000/AppData/ < id aplikacji > /` | applicationStorageDirectory | r | N/D! | N/D! | Tak | +|    `app/native` | applicationDirectory | r | N/D! | N/D! | Tak | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | Nr | Tak | Tak | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Tak | Nr | Tak | +| `file:///accounts/1000/Removable/sdcard` | externalRemovableDirectory | r/w | Tak | Nr | Nr | +| `file:///accounts/1000/Shared` | sharedDirectory | r/w | Tak | Nr | Nr | + +*Uwaga*: gdy aplikacja jest rozmieszczana do pracy obwodu, wszystkie Å›cieżki sÄ… wzglÄ™dne do /accounts/1000-enterprise. + +## Dziwactwa Androida + +### Lokalizacja przechowywania trwaÅ‚ych Android + +Istnieje wiele prawidÅ‚owe lokalizacje do przechowywania trwaÅ‚ych plików na telefonie z systemem Android. Zobacz [tÄ™ stronÄ™][3] do szerokiej dyskusji o różnych możliwoÅ›ciach. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +Poprzednie wersje pluginu wybrać lokalizacjÄ™ plików tymczasowych i trwaÅ‚e podczas uruchamiania, czy urzÄ…dzenie twierdziÅ‚, że karta SD (lub równoważne magazynowanie podzia³) byÅ‚ montowany w oparciu. Czy karta SD zostaÅ‚a zamontowana, czy duży wewnÄ™trzny magazynowanie podzia³ byÅ‚ dostÄ™pny (takie jak na Nexus urzÄ…dzenia,) a nastÄ™pnie trwaÅ‚e pliki bÄ™dÄ… przechowywane w katalogu głównego tego miejsca. OznaczaÅ‚o to, że wszystkie aplikacje Cordova może Zobacz wszystkie pliki dostÄ™pne na karcie. + +JeÅ›li karta SD nie byÅ‚ dostÄ™pny, a nastÄ™pnie poprzednie wersje bÄ™dzie przechowywać dane w `/data/data/`, która izoluje aplikacje od siebie, ale nadal może spowodować danych, które majÄ… być współużytkowane przez użytkowników. + +Teraz jest możliwe, aby zdecydować, czy do przechowywania plików w lokalizacji magazynu plików, lub przy użyciu poprzednich logiki, z preferencjÄ… w aplikacji w pliku `config.xml`. Aby to zrobić, Dodaj jednÄ… z tych dwóch linii do `pliku config.xml`: + + + + + + +Bez tej linii wtyczki pliku bÄ™dzie używać `Compatibility` jako domyÅ›lny. JeÅ›li znacznik preferencji jest obecny i to nie jednÄ… z tych wartoÅ›ci, aplikacja nie zostanie uruchomiona. + +JeÅ›li aplikacja wczeÅ›niej zostaÅ‚o wysÅ‚ane do użytkowników, przy użyciu starszych (pre-1.0) wersjÄ™ tego pluginu i ma zapisane na dysku pliki w trwaÅ‚ych plików, a nastÄ™pnie należy ustawić preferencje do `Compatibility`. Przełączania lokalizacji do "Internal" oznacza, że istniejÄ…cych użytkowników, którzy ich aplikacja może być niesÅ‚abnÄ…cy wobec dostÄ™p ich wczeÅ›niej zapisane pliki, w zależnoÅ›ci od ich urzÄ…dzenie. + +JeÅ›li aplikacja jest nowy, lub ma nigdy wczeÅ›niej przechowywane pliki w systemie plików trwaÅ‚e, ustawienie `Internal` generalnie jest zalecane. + +## Dziwactwa iOS + +* `cordova.file.applicationStorageDirectory`jest tylko do odczytu; próby przechowywania plików w katalogu głównym zakoÅ„czy siÄ™ niepowodzeniem. Użyj jednego z innych `cordova.file.*` wÅ‚aÅ›ciwoÅ›ci zdefiniowane dla iOS (tylko `applicationDirectory` i `applicationStorageDirectory` sÄ… tylko do odczytu). +* `FileReader.readAsText(blob, encoding)` + * `encoding`Parametr nie jest obsÅ‚ugiwana, i kodowanie UTF-8 jest zawsze w efekcie. + +### iOS lokalizacja przechowywania trwaÅ‚ych + +IstniejÄ… dwa ważne miejsca trwaÅ‚e pliki na urzÄ…dzenia iOS: katalogu dokumentów i katalogu biblioteki. Poprzednie wersje pluginu tylko kiedykolwiek przechowywane trwaÅ‚e pliki w katalogu dokumentów. To miaÅ‚ ten efekt uboczny od rozpoznawalnoÅ›ci wszystkie pliki aplikacji w iTunes, który byÅ‚ czÄ™sto niezamierzone, zwÅ‚aszcza dla aplikacji, które obsÅ‚ugujÄ… wiele maÅ‚ych plików, zamiast produkuje kompletne dokumenty do wywozu, który jest przeznaczenie katalogu. + +Teraz jest możliwe, aby zdecydować, czy do przechowywania plików w dokumentach lub katalogu biblioteki, z preferencjÄ… w pliku `config.xml` aplikacji. Aby to zrobić, Dodaj jednÄ… z tych dwóch linii do `pliku config.xml`: + + + + + + +Bez tej linii wtyczki pliku bÄ™dzie używać `Compatibility` jako domyÅ›lny. JeÅ›li znacznik preferencji jest obecny i to nie jednÄ… z tych wartoÅ›ci, aplikacja nie zostanie uruchomiona. + +JeÅ›li aplikacja wczeÅ›niej zostaÅ‚o wysÅ‚ane do użytkowników, przy użyciu starszych (pre-1.0) wersjÄ™ tego pluginu i ma zapisane na dysku pliki w trwaÅ‚ych plików, a nastÄ™pnie należy ustawić preferencje do `Compatibility`. Przełączania lokalizacji do `Library` oznaczaÅ‚oby, że istniejÄ…cych użytkowników, którzy ich aplikacja bÄ™dzie niesÅ‚abnÄ…cy wobec dostÄ™p ich wczeÅ›niej zapisane pliki. + +JeÅ›li aplikacja jest nowy, lub nigdy wczeÅ›niej przechowywane pliki w trwaÅ‚ych plików, ustawieÅ„ `Library` ogólnie jest zalecane. + +## Firefox OS dziwactwa + +API systemu plików nie jest obsÅ‚ugiwany macierzyÅ›cie przez Firefox OS i jest zaimplementowany jako podkÅ‚adki na indexedDB. + +* Nie usuwajÄ…c niepuste katalogi +* Nie obsÅ‚uguje metadane dla katalogów +* Metody `copyTo` i `moveTo` nie obsÅ‚uguje katalogi + +ObsÅ‚ugiwane sÄ… nastÄ™pujÄ…ce Å›cieżki danych: * `applicationDirectory` - używa `xhr`, aby uzyskać lokalne pliki, które sÄ… pakowane z aplikacji. * `dataDirectory` - na trwaÅ‚e dane specyficzne dla aplikacji pliki. * `cacheDirectory` - buforowanych plików, które powinny przetrwać ponowne uruchomienie aplikacji (aplikacje nie powinny polegać na OS, aby usunąć pliki tutaj). + +## Quirks przeglÄ…darki + +### Wspólne dziwactw i uwagi + +* Każda przeglÄ…darka używa wÅ‚asnej piaskownicy plików. IE i Firefox Użyj IndexedDB jako podstawa. Wszystkie przeglÄ…darki za pomocÄ… ukoÅ›nika jako separatora katalogu Å›cieżka. +* Wpisy w katalogu majÄ… być tworzone sukcesywnie. Na przykÅ‚ad wywoÅ‚anie `fs.root.getDirectory (' dir1/dir2 ', {create:true}, successCallback, errorCallback)` zakoÅ„czy siÄ™ niepowodzeniem, jeÅ›li nie istnieje dir1. +* Plugin żądania użytkownika uprawnieÅ„ do używania trwaÅ‚e przechowywanie przy pierwszym uruchomieniu aplikacji. +* Wtyczka obsÅ‚uguje `cdvfile://localhost` (lokalne zasoby) tylko. Czyli zewnÄ™trznych zasobów nie sÄ… obsÅ‚ugiwane przez `cdvfile`. +* Plugin nie nastÄ™pować po ["Plik API systemu nazw 8.3 ograniczenia"][4]. +* Obiektu BLOB i pliku "`close` funkcja nie jest obsÅ‚ugiwana. +* `FileSaver` i `BlobBuilder` nie sÄ… obsÅ‚ugiwane przez ten plugin i nie ma artykułów. +* Plugin nie obsÅ‚uguje `requestAllFileSystems`. Ta funkcja jest również brak w specyfikacji. +* Wpisy w katalogu nie zostanÄ… usuniÄ™te, jeÅ›li używasz `create: true` flaga dla istniejÄ…cego katalogu. +* Pliki utworzone za pomocÄ… konstruktora nie sÄ… obsÅ‚ugiwane. Zamiast tego należy użyć metody entry.file. +* Każda przeglÄ…darka używa wÅ‚asnej postaci URL odwoÅ‚ania blob. +* `readAsDataURL` funkcja jest obsÅ‚ugiwana, ale mediatype w Chrome zależy od wejÅ›cia z rozszerzeniem, mediatype w IE zawsze jest pusty (który jest taki sam jak `zwykÅ‚y tekst` wedÅ‚ug specyfikacji), mediatype w Firefox jest zawsze `aplikacji/oktet strumieÅ„`. Na przykÅ‚ad, jeÅ›li treść jest `abcdefg` Firefox wraca z `danych: stosowanie / octet-stream, base64, YWJjZGVmZw ==`, czyli zwraca `danych:; base64, YWJjZGVmZw ==`, Chrome zwraca `danych: < mediatype w zależnoÅ›ci od rozszerzenia nazwy; > base64, YWJjZGVmZw ==`. +* `toInternalURL` zwraca Å›cieżkÄ™ w postaci `file:///persistent/path/to/entry` (Firefox, IE). Chrom zwraca Å›cieżkÄ™ w postaci `cdvfile://localhost/persistent/file`. + + [4]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### Dziwactwa chrom + +* Chrom plików nie jest od razu gotowy po gotowe urzÄ…dzenia. Jako rozwiÄ…zanie alternatywne można subskrybować zdarzenia `filePluginIsReady`. PrzykÅ‚ad: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +Funkcja `window.isFilePluginReadyRaised` sÅ‚uży do sprawdzenia, czy zdarzenie już zostaÅ‚a podniesiona. -kwoty plików tymczasowych i trwaÅ‚e window.requestFileSystem nie sÄ… ograniczone w Chrome. -W celu zwiÄ™kszenia trwaÅ‚ego magazynu w Chrome, należy wywoÅ‚ać metodÄ™ `window.initPersistentFileSystem`. DomyÅ›lnie trwaÅ‚e dyskowa jest 5 MB. -Chrome wymaga `--pozwalajÄ…--dostÄ™p z plików` uruchomić argument na poparcie API za poÅ›rednictwem protokoÅ‚u `file:///`. -`Plik` obiekt bÄ™dzie nie zmieniÅ‚o jeÅ›li flaga `{create:true}` gdy już istniejÄ…cy `wpis`. -wydarzenia `zwrotu` wÅ‚aÅ›ciwość jest zestaw true w Chrome. Jest to sprzeczne ze [specyfikacji][5]. -Funkcja `toURL` w Chrome zwraca `plików:`-poprzedzona Å›cieżkÄ… w zależnoÅ›ci od aplikacji hosta. Na przykÅ‚ad, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`. -wynik funkcji `toURL` nie zawierajÄ… ukoÅ›nika w wpis w katalogu. Chrom usuwa katalogi z ciąć doczepiane adresów URL poprawnie choć. -Metoda `resolveLocalFileSystemURL` wymaga przychodzÄ…cych `url` majÄ… prefiks `plików`. Na przykÅ‚ad parametr `adresu url` do `resolveLocalFileSystemURL` powinny być w formie `filesystem:file:///persistent/somefile.txt`, w przeciwieÅ„stwie do formularza `file:///persistent/somefile.txt` w Android. -PrzestarzaÅ‚e `toNativeURL` funkcja nie jest obsÅ‚ugiwana i nie tylko. -Funkcja `setMetadata` jest nie podane w specyfikacji i nie jest obsÅ‚ugiwane. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast SYNTAX_ERR(code: 8) na żądanie nieistniejÄ…cÄ… plików. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast PATH_EXISTS_ERR(code: 12) próbuje stworzyć wyłącznie pliku lub katalogu, który już istnieje. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast NO_MODIFICATION_ALLOWED_ERR(code: 6) na próby wywoÅ‚ania removeRecursively w głównym systemie plików. -INVALID_MODIFICATION_ERR (kod: 9) jest generowany zamiast NOT_FOUND_ERR(code: 1) na trudny do katalogu moveTo, który nie istnieje. + + [5]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +### Na bazie IndexedDB impl dziwactw (Firefox i IE) + +* `.` i `.` nie sÄ… obsÅ‚ugiwane. +* IE obsÅ‚uguje `file:///`-tryb; tylko obsÅ‚ugiwane tryb jest obsÅ‚ugiwany (http://localhost:xxxx). +* Rozmiar plików Firefox nie jest ograniczona, ale każde rozszerzenie 50MB zwróci użytkownikowi uprawnienia. IE10 pozwala maksymalnie 10mb połączone "appcache" i IndexedDB używane w implementacji systemu plików bez monitowania, gdy trafisz na tym poziomie, które uzyskasz, jeÅ›li chcesz mogÅ‚a ona zostać zwiÄ™kszony do max 250mb na stronie. WiÄ™c `rozmiar` parametru funkcja `requestFileSystem` nie wpÅ‚ywa na system plików Firefox i IE. +* `readAsBinaryString` funkcja nie jest okreÅ›lona w specyfikacji i nie obsÅ‚ugiwane w IE i nie tylko. +* `File.Type` ma zawsze wartość null. +* Nie należy utworzyć wpis za pomocÄ… DirectoryEntry wystÄ…pienie wynik wywoÅ‚ania zwrotnego, który zostaÅ‚ usuniÄ™ty. W przeciwnym razie dostaniesz wpisem"wiszÄ…ce". +* Zanim bÄ™dzie można przeczytać plik, który zostaÅ‚ napisany tylko trzeba uzyskać nowe wystÄ…pienie tego pliku. +* Funkcja `setMetadata`, która nie jest okreÅ›lona w specyfikacji obsÅ‚uguje tylko zmian pola `modificationTime`. +* `copyTo` i `moveTo` funkcji nie obsÅ‚uguje katalogi. +* Metadanych w katalogów nie jest obsÅ‚ugiwana. +* Zarówno Entry.remove i directoryEntry.removeRecursively nie usuwajÄ…c niepuste katalogi - katalogi sÄ… usuwane sÄ… czyszczone z treÅ›ci zamiast. +* `abort` i `truncate` funkcje nie sÄ… obsÅ‚ugiwane. +* zdarzenia postÄ™pu nie sÄ… zwalniani. Na przykÅ‚ad to obsÅ‚uga bÄ™dzie nie wykonywane: + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## Uaktualniania notatek + +W v1.0.0 tego pluginu struktury `FileEntry` i `DirectoryEntry` zmieniÅ‚y siÄ™ wiÄ™cej zgodnie z opublikowanÄ… specyfikacjÄ…. + +Poprzednie wersje (pre-1.0.0) plugin przechowywane urzÄ…dzenia bezwzglÄ™dna plik lokalizacja we wÅ‚aÅ›ciwoÅ›ci `fullPath` `wpis` obiektów. Te Å›cieżki zazwyczaj bÄ™dzie wyglÄ…dać + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Te Å›cieżki byÅ‚y także zwracany przez metodÄ™ `toURL()` `Entry` obiektów. + +Z v1.0.0 atrybut `fullPath` jest Å›cieżkÄ… do pliku, *wzglÄ™dem katalogu głównego systemu plików HTML*. Tak powyżej Å›cieżki bÄ™dzie teraz zarówno być reprezentowane przez obiekt `FileEntry` z `fullPath` o + + /path/to/file + + +JeÅ›li aplikacja dziaÅ‚a z Å›cieżki bezwzglÄ™dnej urzÄ…dzeÅ„, i możesz wczeÅ›niej źródÅ‚o tych Å›cieżek przez wÅ‚aÅ›ciwość `fullPath` `wpis` obiektów, należy zaktualizować kod, aby zamiast tego użyj `entry.toURL()`. + +Dla wstecznej kompatybilnoÅ›ci, Metoda `resolveLocalFileSystemURL()` bÄ™dzie zaakceptować urzÄ…dzenia Å›cieżka bezwzglÄ™dna i zwróci obiekt `Entry` odpowiadajÄ…cy, tak dÅ‚ugo, jak ten plik istnieje w albo `TEMPORARY` lub `PERSISTENT` systemy plików. + +To szczególnie zostaÅ‚ problem z pluginem transferu plików, które poprzednio używane Å›cieżki bezwzglÄ™dnej urzÄ…dzeÅ„ (i wciąż można je przyjąć). ZostaÅ‚a zaktualizowana do pracy poprawnie z adresów URL plików, wiÄ™c wymiana `entry.fullPath` z `entry.toURL()` powinno rozwiÄ…zać wszelkie problemy dostawanie ten plugin do pracy z plików w pamiÄ™ci urzÄ…dzenia. + +W v1.1.0 wartość zwracana przez `toURL()` zostaÅ‚ zmieniony (patrz \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) zwraca adres URL absolutnej "file://". wszÄ™dzie tam, gdzie jest to możliwe. Aby zapewnić ' cdvfile:'-URL można użyć `toInternalURL()` teraz. Ta metoda zwróci teraz adresy URL plików formularza + + cdvfile://localhost/persistent/path/to/file + + +który sÅ‚uży do jednoznacznej identyfikacji pliku. + +## Wykaz kodów błędów i ich znaczenie + +Gdy błąd jest generowany, jeden z nastÄ™pujÄ…cych kodów bÄ™dzie sÅ‚użyć. + +| Kod | StaÅ‚a | +| ---:|:----------------------------- | +| 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` | + +## Konfigurowanie wtyczka (opcjonalny) + +Zestaw dostÄ™pnych plików może być skonfigurowany na platformie. Zarówno iOS i Android Tag w `pliku config.xml`, których nazwy plików do instalacji. DomyÅ›lnie włączone sÄ… wszystkie korzenie systemu plików. + + + + + +### Android + +* `files`: katalogu przechowywania plików aplikacji +* `files-external`: katalog aplikacji zewnÄ™trznych plików +* `sdcard`: katalog globalny plik zewnÄ™trzny (to jest głównym karty SD, jeÅ›li jedna jest zainstalowana). Musi mieć uprawnienia `android.permission.WRITE_EXTERNAL_STORAGE` wobec używać ten. +* `cache`: katalogu wewnÄ™trznej pamiÄ™ci podrÄ™cznej aplikacji +* `cache-external`: katalogu aplikacji zewnÄ™trznych pamiÄ™ci podrÄ™cznej +* `root`: caÅ‚e urzÄ…dzenie systemu plików + +Android obsÅ‚uguje również specjalnych plików o nazwie "dokumenty", który reprezentuje podkatalog "/ dokumenty /" w ramach systemu plików "pliki". + +### iOS + +* `library`: katalog biblioteki aplikacji +* `documents`: dokumenty katalogu aplikacji +* `cache`: katalogu pamiÄ™ci podrÄ™cznej aplikacji +* `bundle`: pakiet aplikacji; Lokalizacja aplikacji na dysku (tylko do odczytu) +* `root`: caÅ‚e urzÄ…dzenie systemu plików + +DomyÅ›lnie katalogi biblioteki i dokumenty mogÄ… być synchronizowane iCloud. Można również zażądać dwóch dodatkowych plików, `library-nosync` i `documents-nosync`, które stanowiÄ… specjalny katalog nie zsynchronizowane w `/Library` lub systemu plików `/Documents`. diff --git a/plugins/cordova-plugin-file/doc/pl/plugins.md b/plugins/cordova-plugin-file/doc/pl/plugins.md new file mode 100644 index 0000000..6e8fe86 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/pl/plugins.md @@ -0,0 +1,101 @@ + + +# Uwagi dla programistów wtyczki + +Te notatki sÄ… przeznaczone przede wszystkim dla Androida i iOS deweloperów, którzy chcieli pisac pluginy które interfejs z systemu plików za pomocÄ… wtyczki pliku. + +## Praca z Cordova pliku system adresów URL + +Od wersji 1.0.0, ten plugin ma używane adresy URL z `cdvfile` system dla wszystkich komunikacji przez most, a nie narażać urzÄ…dzenia raw Å›cieżki systemu plików JavaScript. + +Na stronie JavaScript oznacza to, że FileEntry i DirectoryEntry obiekty majÄ… fullPath atrybut, który jest głównym systemie plików HTML. JeÅ›li twój plugin JavaScript API akceptuje obiektu FileEntry lub DirectoryEntry, należy zadzwonić `.toURL()` dla tego obiektu przed przekazaniem Altpradl do kodu macierzystego. + +### Konwersja cdvfile: / / URL do Å›cieżki fileystem + +Wtyczek, które trzeba pisać do systemu plików może chcesz przekonwertować odebranych plików systemu adres URL lokalizacji rzeczywistych plików. Istnieje wiele sposobów robi to, w zależnoÅ›ci od macierzystego platformy. + +Ważne jest, aby pamiÄ™tać, że nie wszystkie `cdvfile://` adresy URL sÄ… można zmapować na prawdziwe pliki w urzÄ…dzeniu. Niektóre adresy URL może odnosić siÄ™ do aktywów na urzÄ…dzeniu, które nie sÄ… reprezentowane przez pliki, lub nawet może odnosić siÄ™ do zasobów zdalnych. Ze wzglÄ™du na te możliwoÅ›ci wtyczki należy zawsze sprawdzić, czy siÄ™ znaczÄ…cego wyniku powrót podczas próby konwersji adresów URL do Å›cieżki. + +#### Androida + +Na Android, najprostsza metoda konwersji `cdvfile://` URL do Å›cieżki systemu plików jest użycie `org.apache.cordova.CordovaResourceApi` . `CordovaResourceApi`jest kilka metod, które mogÄ… obsÅ‚ugiwać `cdvfile://` adresów URL: + + Widok sieci Web jest czÅ‚onkiem Plugin klasy CordovaResourceApi resourceApi = webView.getResourceApi(); + + Uzyskać URL file:/// reprezentujÄ…cych ten plik na urzÄ…dzeniu / / lub ten sam adres URL niezmienione, jeÅ›li nie mogÄ… być mapowane do pliku fileURL Uri = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +Jest również możliwe, aby korzystać z wtyczki pliku bezpoÅ›rednio: + + org.apache.cordova.file.FileUtils przywóz; + org.apache.cordova.file.FileSystem przywóz; + java.net.MalformedURLException przywóz; + + Uzyskać pliku plugin manager wtyczki FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + BiorÄ…c pod uwagÄ™ adres URL, uzyskać Å›cieżkÄ™ dla to spróbuj {String Å›cieżka = filePlugin.filesystemPathForURL(cdvfileURL);} catch (MalformedURLException e) {/ / url plików nie byÅ‚o uznane} + + +Do przeliczenia Å›cieżki do `cdvfile://` URL: + + org.apache.cordova.file.LocalFilesystemURL przywóz; + + Uzyskanie obiektu LocalFilesystemURL na Å›cieżkÄ™ urzÄ…dzenia / / lub null, jeÅ›li nie może być reprezentowana jako adres URL cdvfile. + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path); + Dostać reprezentacjÄ™ ciÄ…g adresu URL obiektu String cdvfileURL = url.toString(); + + +JeÅ›li twój plugin tworzy plik, i chcesz zwraca obiekt FileEntry dla niego, użyj pliku plugin: + + Zwraca JSON struktura nadaje siÄ™ do powrotu do JavaScript, / / lub null, jeÅ›li ten plik nie jest reprezentować jako adres URL cdvfile. + Wpis JSONObject = filePlugin.getEntryForFile(file); + + +#### iOS + +Cordova na iOS nie korzystać z tego samego `CordovaResourceApi` koncepcji jak Android. Na iOS należy użyć pliku plugin do konwersji miÄ™dzy adresach URL i Å›cieżkach plików. + + Uzyskać obiekt CDVFilesystem URL URL url ciÄ…g CDVFilesystemURL * = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL]; + Uzyskać Å›cieżkÄ™ dla URL obiektu, stawka zerowa, jeÅ›li nie mogÄ… być mapowane do Å›cieżki pliku NSString * = [filePlugin filesystemPathForURL:url]; + + + Dostać CDVFilesystem URL obiektu na Å›cieżkÄ™ urzÄ…dzenia lub / / zerowe, jeÅ›li nie może być reprezentowana jako adres URL cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + Dostać reprezentacjÄ™ ciÄ…g adresu URL obiektu NSString * cdvfileURL = [url absoluteString]; + + +JeÅ›li twój plugin tworzy plik, i chcesz zwraca obiekt FileEntry dla niego, użyj pliku plugin: + + Dostać CDVFilesystem URL obiektu na Å›cieżkÄ™ urzÄ…dzenia lub / / zerowe, jeÅ›li nie może być reprezentowana jako adres URL cdvfile. + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + Struktura wrócić do JavaScript NSDictionary * wpis = [filePlugin makeEntryForLocalURL:url] + + +#### JavaScript + +W JavaScript, aby uzyskać `cdvfile://` adres URL z obiektu FileEntry lub DirectoryEntry, wystarczy zadzwonić `.toURL()` na to: + + var cdvfileURL = entry.toURL(); + + +W plugin reakcji obsÅ‚ugi przerobić od zwróconych struktury FileEntry do rzeczywistego obiektu wejÅ›cia, kod obsÅ‚ugi należy zaimportować pliku plugin i utworzyć nowy obiekt: + + utworzyć odpowiedni wpis obiektu var wpis; + JeÅ›li (entryStruct.isDirectory) {wpis = nowy DirectoryEntry (entryStruct.name, entryStruct.fullPath, nowe FileSystem(entryStruct.filesystemName));} jeszcze {wpis = nowy FileEntry (entryStruct.name, entryStruct.fullPath, nowe FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/plugins.md b/plugins/cordova-plugin-file/doc/plugins.md new file mode 100644 index 0000000..a3329d6 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/plugins.md @@ -0,0 +1,120 @@ + + +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/plugins/cordova-plugin-file/doc/ru/index.md b/plugins/cordova-plugin-file/doc/ru/index.md new file mode 100644 index 0000000..3c9fca9 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ru/index.md @@ -0,0 +1,275 @@ + + +# cordova-plugin-file + +Этот плагин реализует API файла, позволÑÑ Ð´Ð¾Ñтуп на чтение и запиÑÑŒ в файлы, находÑщиеÑÑ Ð½Ð° уÑтройÑтве. + +Этот плагин оÑнован на неÑкольких Ñпецификации, в том чиÑле: HTML5 API файла + +(Ðыне неÑущеÑтвующей) каталоги и ÑиÑтему раÑширений ПоÑледнее: , Ñ…Ð¾Ñ‚Ñ Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ Ñ‡Ð°Ñть кода, плагин был напиÑан, когда ранее Ñпец был текущим: + +Он также реализует уничтожал spec: + +Ð”Ð»Ñ Ð¸ÑпользованиÑ, пожалуйÑта, обратитеÑÑŒ к HTML5 Ñкалы отличные [файловой ÑиÑтемы Ñтатьи.][1] + + [1]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +Обзор других вариантов Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð½Ð°Ð¹Ñ‚Ð¸ Cordova [Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ€ÑƒÐºÐ¾Ð²Ð¾Ð´Ñтва][2]. + + [2]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +## УÑтановка + + cordova plugin add cordova-plugin-file + + +## Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* Firefox OS +* iOS +* Windows Phone 7 и 8 * +* Windows 8 * + +* *Эти платформы не поддерживают `FileReader.readAsArrayBuffer` , ни `FileWriter.write(blob)` .* + +## Где хранить файлы + +По ÑоÑтоÑнию на v1.2.0 приведены URL-адреÑа Ð´Ð»Ñ Ð²Ð°Ð¶Ð½Ñ‹Ñ… файлов в ÑиÑтемные каталоги. Каждый URL-Ð°Ð´Ñ€ÐµÑ Ð² виде *file:///path/to/spot/*и может быть преобразован в `DirectoryEntry` Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ`window.resolveLocalFileSystemURL()`. + +* `cordova.file.applicationDirectory`-Каталог только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, где уÑтановлено приложение. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.applicationStorageDirectory`-Корневой каталог Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² пеÑочнице; на iOS Ñто меÑто только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ (но определенных подкаталогов [как `/Documents` ], Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñи). Ð’Ñе данные, ÑодержащиеÑÑ Ð² чаÑтных в приложение. ( *iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.dataDirectory`-Ð¥Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñтойкие и чаÑтных данных в Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² пеÑочнице Ñ Ð¸Ñпользованием внутренней памÑти (на Android, еÑли необходимо иÑпользовать внешнюю памÑть, иÑпользовать `.externalDataDirectory` ). Ðа iOS, Ñтот каталог не ÑинхронизируетÑÑ Ñ iCloud (иÑпользование `.syncedDataDirectory` ). (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.cacheDirectory`-Каталог Ð´Ð»Ñ ÐºÑшированных данных файлов или вÑе файлы, которые ваше приложение может повторно Ñоздать легко. ОС может удалить Ñти файлы, когда уÑтройÑтва хватает на хранение, тем не менее, Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ должны опиратьÑÑ Ð½Ð° OS, чтобы удалить файлы здеÑÑŒ. (*iOS*, *Android*, *BlackBerry 10*) + +* `cordova.file.externalApplicationStorageDirectory`-ПроÑтранÑтво Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° внешнем хранилище. (*Android*) + +* `cordova.file.externalDataDirectory`-Куда положить файлы данных конкретного Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° внешнем хранилище. (*Android*) + +* `cordova.file.externalCacheDirectory`-Применение кÑш на внешние накопители. (*Android*) + +* `cordova.file.externalRootDirectory`-Корень внешние накопители (SD карта). (*Android*, *BlackBerry 10*) + +* `cordova.file.tempDirectory`-Временный каталог, что ОС можно ÑнÑть на будет. Ðе Ñледует полагатьÑÑ Ð½Ð° OS, чтобы очиÑтить Ñтот каталог; Ваше приложение вÑегда Ñледует удалÑть файлы в ÑоответÑтвующих ÑлучаÑÑ…. (*iOS*) + +* `cordova.file.syncedDataDirectory`-Содержит файлы приложениÑ, которые должны быть Ñинхронизированы (например, к iCloud). (*iOS*) + +* `cordova.file.documentsDirectory`-Файлы Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ, но Ñто ÑвлÑÑŽÑ‚ÑÑ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ð¼Ð¸ Ð´Ð»Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… приложений (например, файлы Office). (*iOS*) + +* `cordova.file.sharedDirectory`-Файлы глобально доÑтупной Ð´Ð»Ñ Ð²Ñех приложений (*BlackBerry 10*) + +## Макеты файловой ÑиÑтемы + +Ð¥Ð¾Ñ‚Ñ Ñ‚ÐµÑ…Ð½Ð¸Ñ‡ÐµÑки деталь реализации, она может быть очень полезно знать как `cordova.file.*` карта ÑвойÑтва физичеÑкие пути на реальном уÑтройÑтве. + +### iOS раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы + +| Путь к уÑтройÑтву | `Cordova.File.*` | `iosExtraFileSystems` | r/w? | Ñтойкие? | Очищает ОС | Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ | чаÑтные | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:----:|:--------:|:----------:|:-------------:|:-------:| +| `/ var/мобильного/применениÑ/< UUID > /` | applicationStorageDirectory | - | r | Ð/Д | Ð/Д | Ð/Д | Да | +|    `appname.app/` | applicationDirectory | раÑÑлоение | r | Ð/Д | Ð/Д | Ð/Д | Да | +|       `www/` | - | - | r | Ð/Д | Ð/Д | Ð/Д | Да | +|    `Documents/` | documentsDirectory | документы | r/w | Да | Ðет | Да | Да | +|       `NoCloud/` | - | документы nosync | r/w | Да | Ðет | Ðет | Да | +|    `Library` | - | Библиотека | r/w | Да | Ðет | Да? | Да | +|       `NoCloud/` | dataDirectory | Библиотека nosync | r/w | Да | Ðет | Ðет | Да | +|       `Cloud/` | syncedDataDirectory | - | r/w | Да | Ðет | Да | Да | +|       `Caches/` | cacheDirectory | кÑш | r/w | Да * | Да * * *| | Ðет | Да | +|    `tmp/` | tempDirectory | - | r/w | Ðе * * | Да * * *| | Ðет | Да | + +* Файлы ÑохранÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ и обновлениÑ, но Ñтот каталог может быть очищен, когда ОС желаний. Ваше приложение должно иметь возможноÑть воÑÑоздать любое Ñодержание, которое может быть удалено. + +* * Файлы могут ÑохранÑтьÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ°Ð¼Ð¸ app, но не полагайтеÑÑŒ на Ñто поведение. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² не гарантируетÑÑ. Приложение должно удалить файлы из Ñтого каталога, когда Ñто применимо, как Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÑиÑтема не гарантирует когда (или даже еÑли) Ñти файлы будут удалены. + +* * *| ОС может очиÑтить Ñодержимое Ñтого каталога, когда он Ñчитает Ñто необходимым, но не полагайтеÑÑŒ на Ñто. Ðеобходимо ÑнÑть Ñтот каталог в завиÑимоÑти от вашего приложениÑ. + +### РаÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Android файловой ÑиÑтемы + +| Путь к уÑтройÑтву | `Cordova.File.*` | `AndroidExtraFileSystems` | r/w? | Ñтойкие? | Очищает ОС | чаÑтные | +|:--------------------------------- |:----------------------------------- |:------------------------- |:----:|:--------:|:----------:|:-------:| +| `File:///android_asset/` | applicationDirectory | | r | Ð/Д | Ð/Д | Да | +| `/ Data/data/< app-id > /` | applicationStorageDirectory | - | r/w | Ð/Д | Ð/Д | Да | +|    `cache` | cacheDirectory | кÑш | r/w | Да | Да * | Да | +|    `files` | dataDirectory | файлы | r/w | Да | Ðет | Да | +|       `Documents` | | документы | r/w | Да | Ðет | Да | +| `< sdcard > /` | externalRootDirectory | SDCard | r/w | Да | Ðет | Ðет | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Да | Ðет | Ðет | +|       `cache` | externalCacheDirectry | кÑш внешние | r/w | Да | Ðе * * | Ðет | +|       `files` | externalDataDirectory | файлы внешние | r/w | Да | Ðет | Ðет | + +* ОС может периодичеÑки удалÑть Ñтот каталог, но не полагайтеÑÑŒ на Ñто поведение. ОчиÑтите Ñодержимое Ñтого каталога в завиÑимоÑти от вашего приложениÑ. ЕÑли пользователь вручную очиÑтить кÑш, Ñодержимое Ñтого каталога будут удалены. + +* * ОС не ÑÑно Ñтот каталог автоматичеÑки; Ð’Ñ‹ неÑете ответÑтвенноÑть за управление Ñодержимым ÑамоÑтоÑтельно. ЕÑли пользователь вручную очиÑтить кÑш, Ñодержимое каталога будут удалены. + +**Примечание**: ЕÑли Ð½ÐµÐ»ÑŒÐ·Ñ Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ внешние накопители, `cordova.file.external*` СвойÑтва`null`. + +### BlackBerry 10 раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы + +| Путь к уÑтройÑтву | `Cordova.File.*` | r/w? | Ñтойкие? | Очищает ОС | чаÑтные | +|:--------------------------------------------------- |:--------------------------- |:----:|:--------:|:----------:|:-------:| +| `File:///Accounts/1000/AppData/ < app id > /` | applicationStorageDirectory | r | Ð/Д | Ð/Д | Да | +|    `app/native` | applicationDirectory | r | Ð/Д | Ð/Д | Да | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | Ðет | Да | Да | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | Да | Ðет | Да | +| `File:///Accounts/1000/Removable/SDCard` | externalRemovableDirectory | r/w | Да | Ðет | Ðет | +| `File:///Accounts/1000/Shared` | sharedDirectory | r/w | Да | Ðет | Ðет | + +*Примечание*: при развертывании Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ периметра, вÑе пути отноÑительны /accounts/1000-enterprise. + +## ОÑобенноÑти Android + +### МеÑтоположение Android поÑтоÑнного Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ + +ЕÑть неÑколько допуÑтимых меÑÑ‚ Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾ÑтоÑнных файлов на уÑтройÑтве Android. Смотрите [Ñту Ñтраницу][3] Ð´Ð»Ñ ÑˆÐ¸Ñ€Ð¾ÐºÐ¾Ð³Ð¾ обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð»Ð¸Ñ‡Ð½Ñ‹Ñ… возможноÑтей. + + [3]: http://developer.android.com/guide/topics/data/data-storage.html + +Предыдущие верÑии плагина будет выбирать раÑположение временных и поÑтоÑнных файлов при запуÑке, оÑнованный на ли уÑтройÑтво утверждал, что SD-карта (или Ñквивалентные Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ») был Ñмонтирован. ЕÑли была Ñмонтирована SD-карты, или еÑли большой внутренней памÑти раздел был доÑтупен (такие как на уÑтройÑтвах Nexus,), то поÑтоÑнные файлы будут хранитьÑÑ Ð² корне Ñтого проÑтранÑтва. Это означало, что вÑе apps Cordova могли видеть вÑе файлы, имеющиеÑÑ Ð½Ð° карте. + +ЕÑли SD-карты не был доÑтупен, то предыдущих верÑий будет хранить данные в `/data/data/` , ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¸Ð·Ð¾Ð»Ð¸Ñ€ÑƒÐµÑ‚ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ñ€ÑƒÐ³ от друга, но вÑе еще может причина данные раÑпределÑÑŽÑ‚ÑÑ Ð¼ÐµÐ¶Ð´Ñƒ пользователÑми. + +Это теперь можно выбрать, Ñледует ли хранить файлы в меÑте Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½Ð¸Ñ… файлов, или Ñ Ð¸Ñпользованием предыдущей логики, Ñ Ð¿Ñ€ÐµÐ´Ð¿Ð¾Ñ‡Ñ‚ÐµÐ½Ð¸ÐµÐ¼ в вашем приложении `config.xml` файл. Чтобы Ñделать Ñто, добавить один из Ñтих двух линий в `config.xml` : + + + + + + +Без Ñтой линии, будет иÑпользовать файл плагина `Compatibility` по умолчанию. ЕÑли тег предпочтений приÑутÑтвует и не ÑвлÑетÑÑ Ð¾Ð´Ð½Ð¸Ð¼ из Ñтих значений, приложение не запуÑтитÑÑ. + +ЕÑли ранее была отгружена Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñтарые (до 1.0) верÑию Ñтого плагина и имеет Ñохраненные файлы в файловой ÑиÑтеме поÑтоÑнных, то вы должны уÑтановить Ð¿Ñ€ÐµÐ´Ð¿Ð¾Ñ‡Ñ‚ÐµÐ½Ð¸Ñ `Compatibility` . Переключение меÑтоположение Ð´Ð»Ñ Â«Ð’Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ³Ð¾Â» будет означать, что ÑущеÑтвующие пользователи, которые обновить их применение может быть не в ÑоÑтоÑнии получить доÑтуп к их Ñохраненные ранее файлы, в завиÑимоÑти от их уÑтройÑтва. + +ЕÑли ваше приложение ÑвлÑетÑÑ Ð½Ð¾Ð²Ñ‹Ð¼ или ранее никогда не хранит файлы в Ñтойких файловой ÑиÑтемы, то `Internal` как правило рекомендуетÑÑ Ð½Ð°Ñтройка. + +## ОÑобенноÑти iOS + +* `cordova.file.applicationStorageDirectory`доÑтупен только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ; попытка Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² в корневом каталоге не удаÑÑ‚ÑÑ. ИÑпользовать один из других `cordova.file.*` ÑвойÑтва, определенные Ð´Ð»Ñ iOS (только `applicationDirectory` и `applicationStorageDirectory` доÑтупны только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ). +* `FileReader.readAsText(blob, encoding)` + * `encoding`Параметр не поддерживаетÑÑ, и UTF-8 кодирование дейÑтвует вÑегда. + +### iOS меÑто поÑтоÑнного Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ + +СущеÑтвует два допуÑтимых меÑтоположений Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾ÑтоÑнных файлов на уÑтройÑтва iOS: документы каталогов и библиотека. Предыдущие верÑии плагина только когда-либо поÑтоÑнные файлы хранÑÑ‚ÑÑ Ð² папке документы. Это был побочный Ñффект делает вÑе файлы Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² iTunes, который чаÑто был непреднамеренным, оÑобенно Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹, которые обрабатывают большое количеÑтво мелких файлов, вмеÑто того, чтобы производить полный комплект документов Ð´Ð»Ñ ÑкÑпорта, который ÑвлÑетÑÑ Ñ†ÐµÐ»ÑŒ каталога. + +Это теперь можно выбрать, Ñледует ли хранить файлы в документы или каталоге библиотеки, Ñ Ð¿Ñ€ÐµÐ´Ð¿Ð¾Ñ‡Ñ‚ÐµÐ½Ð¸ÐµÐ¼ в вашем приложении `config.xml` файл. Чтобы Ñделать Ñто, добавить один из Ñтих двух линий в `config.xml` : + + + + + + +Без Ñтой линии, будет иÑпользовать файл плагина `Compatibility` по умолчанию. ЕÑли тег предпочтений приÑутÑтвует и не ÑвлÑетÑÑ Ð¾Ð´Ð½Ð¸Ð¼ из Ñтих значений, приложение не запуÑтитÑÑ. + +ЕÑли ранее была отгружена Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñтарые (до 1.0) верÑию Ñтого плагина и имеет Ñохраненные файлы в файловой ÑиÑтеме поÑтоÑнных, то вы должны уÑтановить Ð¿Ñ€ÐµÐ´Ð¿Ð¾Ñ‡Ñ‚ÐµÐ½Ð¸Ñ `Compatibility` . Переключение раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ `Library` будет означать, что ÑущеÑтвующих пользователей обновить их Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ не в ÑоÑтоÑнии получить доÑтуп к их Ñохраненные ранее файлы. + +ЕÑли ваше приложение ÑвлÑетÑÑ Ð½Ð¾Ð²Ñ‹Ð¼ или ранее никогда не хранит файлы в Ñтойких файловой ÑиÑтемы, то `Library` как правило рекомендуетÑÑ Ð½Ð°Ñтройка. + +## ОÑобенноÑти Firefox OS + +API файловой ÑиÑтемы изначально не поддерживаетÑÑ Firefox OS и реализуетÑÑ ÐºÐ°Ðº оболочка поверх indexedDB. + +* Ðе Ð²Ñ‹Ð½Ð¸Ð¼Ð°Ñ Ð½ÐµÐ¿ÑƒÑтые каталоги +* Ðе поддерживает метаданные Ð´Ð»Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð² +* Методы `copyTo` и `moveTo` не поддерживает каталоги + +ПоддерживаютÑÑ Ñледующие пути данных: * `applicationDirectory` -иÑпользует `xhr` чтобы получить локальные файлы, которые упакованы Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸ÐµÐ¼. * `dataDirectory` - Ð”Ð»Ñ Ð¿Ð¾ÑтоÑнных данных конкретного Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð². * `cacheDirectory` -КÑшированных файлов, которые должны выжить перезагрузки Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ (Apps не Ñледует полагатьÑÑ Ð½Ð° OS, чтобы удалить файлы из здеÑÑŒ). + +## Обновление Ð¿Ñ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ + +Ð’ v1.0.0 Ñтого плагина `FileEntry` и `DirectoryEntry` Ñтруктур изменилиÑÑŒ, более ÑоответÑтвует опубликованной Ñпецификации. + +Предыдущий (pre-1.0.0) верÑии плагина хранÑÑ‚ÑÑ ÑƒÑтройÑтва ÐбÑолют файл раÑположение в `fullPath` ÑвойÑтва `Entry` объектов. Эти пути обычно будет выглÑдеть + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +Эти пути также были возвращены `toURL()` метода `Entry` объектов. + +С v1.0.0 `fullPath` атрибут ÑвлÑетÑÑ Ð¿ÑƒÑ‚ÑŒ к файлу, *заданный отноÑительно ÐºÐ¾Ñ€Ð½Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы HTML*. Таким образом, выше пути будет теперь оба быть предÑтавлено `FileEntry` объект Ñ `fullPath` из + + /path/to/file + + +ЕÑли ваше приложение работает Ñ ÑƒÑтройÑтва ÐбÑолют путÑми, и ранее были получены Ñти пути через `fullPath` ÑвойÑтва `Entry` объектов, то вам Ñледует обновить код Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ `entry.toURL()` вмеÑто Ñтого. + +Ð”Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти, `resolveLocalFileSystemURL()` метод будет принимать путь ÐбÑолют уÑтройÑтва и будет возвращать `Entry` объект, ÑоответÑтвующий его, до тех пор, как Ñтот файл ÑущеÑтвует в рамках либо `TEMPORARY` или `PERSISTENT` файловых ÑиÑтем. + +Это оÑобенно была проблема Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð¼ передачи файлов, который ранее иÑпользовалÑÑ ÑƒÑтройÑтва ÐбÑолют пути (и вÑе еще может принÑть их). Он был обновлен Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемы URL, так что замена `entry.fullPath` Ñ `entry.toURL()` должен решить любые вопроÑÑ‹, получить Ñтот плагин Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸ на уÑтройÑтве. + +Ð’ v1.1.0 возвращаемое значение из `toURL()` был изменен (Ñм. \[CB-6394\] (https://issues.apache.org/jira/browse/CB-6394)) Ð´Ð»Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ URL-Ð°Ð´Ñ€ÐµÑ Ð°Ð±Ñолютным «file://». где Ñто возможно. Ð”Ð»Ñ Ð¾Ð±ÐµÑÐ¿ÐµÑ‡ÐµÐ½Ð¸Ñ ' cdvfile:'-вы можете иÑпользовать URL-Ð°Ð´Ñ€ÐµÑ `toInternalURL()` ÑейчаÑ. Этот метод будет возвращать теперь файловой ÑиÑтемы URL формы + + cdvfile://localhost/persistent/path/to/file + + +который может иÑпользоватьÑÑ Ð´Ð»Ñ ÑƒÐ½Ð¸ÐºÐ°Ð»ÑŒÐ½Ð¾Ð¹ идентификации файла. + +## СпиÑок кодов ошибок и Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ + +Когда возникает ошибка, один из Ñледующих кодов будет иÑпользоватьÑÑ. + +| Код | ПоÑтоÑÐ½Ð½Ð°Ñ | +| ---:|:----------------------------- | +| 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` | + +## ÐаÑтройка плагина (опционально) + +Ðабор доÑтупных файловых ÑиÑтем может быть наÑтроен на платформе. IOS и Android признают тег в `config.xml` имена которых файловые ÑиÑтемы Ð´Ð»Ñ ÑƒÑтановки. По умолчанию включены вÑе корни файловой ÑиÑтемы. + + + + + +### Android + +* `files`: Каталог Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ³Ð¾ файла Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ +* `files-external`: Каталог Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ файла Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ +* `sdcard`: Глобальный внешний файл каталог Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ (Ñто корень SD-карты, еÑли Ñ‚Ð°ÐºÐ¾Ð²Ð°Ñ ÑƒÑтановлена). Ð’Ñ‹ должны иметь `android.permission.WRITE_EXTERNAL_STORAGE` разрешение иÑпользовать Ñто. +* `cache`: Каталог внутреннего кÑша Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ +* `cache-external`: Каталог Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ кÑш-памÑти +* `root`: Ð’Ñе уÑтройÑтво файловой ÑиÑтемы + +Android поддерживает также Специальный файловую ÑиÑтему под названием «документы», ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ñ€ÐµÐ´ÑтавлÑет подкаталог «/ документы /» в пределах файловой ÑиÑтемы «файлы». + +### iOS + +* `library`: Каталог библиотеки Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ +* `documents`: Каталог документов Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ +* `cache`: Каталог кÑша Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ +* `bundle`: Пакет приложениÑ; раÑположение Ñамого Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° диÑке (только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ) +* `root`: Ð’Ñе уÑтройÑтво файловой ÑиÑтемы + +По умолчанию каталоги библиотеки и документы можно Ñинхронизировать Ñ iCloud. Ð’Ñ‹ также можете заказать два дополнительных файловых ÑиÑтем, `library-nosync` и `documents-nosync` , которые предÑтавлÑÑŽÑ‚ Специальный каталог не ÑинхронизируютÑÑ Ð² `/Library` или `/Documents` файловой ÑиÑтемы. diff --git a/plugins/cordova-plugin-file/doc/ru/plugins.md b/plugins/cordova-plugin-file/doc/ru/plugins.md new file mode 100644 index 0000000..2445d09 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/ru/plugins.md @@ -0,0 +1,124 @@ + + +# ÐŸÑ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð² плагинов + +Эти Ð¿Ñ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÐ´Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ñ‹ прежде вÑего Ð´Ð»Ñ Android и iOS разработчиков, которые хотÑÑ‚ пиÑать плагины какой Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ ÑиÑтемой, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ файла плагина. + +## Работа Ñ ÐšÐ¾Ñ€Ð´Ð¾Ð²Ð° файловой ÑиÑтемы URL + +ÐÐ°Ñ‡Ð¸Ð½Ð°Ñ Ñ Ð²ÐµÑ€Ñии 1.0.0, Ñтот плагин иÑпользует URL-адреÑов Ñ `cdvfile` Ñхема Ð´Ð»Ñ Ð²Ñех коммуникации через моÑÑ‚, а не Ð¿Ð¾Ð´Ð²ÐµÑ€Ð³Ð°Ñ Ð¿ÑƒÑ‚Ð¸ файловой ÑиÑтемы raw уÑтройÑтва Ð´Ð»Ñ JavaScript. + +Ðа Ñтороне JavaScript Ñто означает, что объекты DirectoryEntry и FileEntry fullPath атрибут, который ÑвлÑетÑÑ Ð¿Ð¾ отношению к корневой файловой ÑиÑтеме HTML. ЕÑли ваш плагин JavaScript API принимает объект DirectoryEntry или FileEntry, необходимо вызвать `.toURL()` Ð´Ð»Ñ Ñтого объекта перед передачей их через моÑÑ‚ в машинный код. + +### Преобразование cdvfile: / / URL-адреÑа в пути fileystem + +Плагины, которые нужно напиÑать в файловой ÑиÑтеме может потребоватьÑÑ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ñ‚ÑŒ URL-адреÑа ÑиÑтемы полученный файл в меÑто фактичеÑкой файловой ÑиÑтемы. СущеÑтвует неÑколько ÑпоÑобов Ñделать Ñто, в завиÑимоÑти от родной платформе. + +Важно помнить, что не вÑе `cdvfile://` URL-адреÑа ÑвлÑÑŽÑ‚ÑÑ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶Ð°ÐµÐ¼Ñ‹Ð¼Ð¸ файлами на уÑтройÑтве. Ðекоторые URL может отноÑитьÑÑ Ðº активам на уÑтройÑтве, которые не предÑтавлены файлы, или может даже обратитьÑÑ Ðº удаленным реÑурÑам. Из-за Ñти возможноÑти плагины Ñледует вÑегда проверÑть ли они получают результат обратно при попытке преобразовать URL-адреÑа в пути. + +#### Android + +Ðа Android, Ñамый проÑтой ÑпоÑоб Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ `cdvfile://` URL-Ð°Ð´Ñ€ÐµÑ Ðº пути файловой ÑиÑтемы заключаетÑÑ Ð² иÑпользовании `cordova-plugin-CordovaResourceApi` . `CordovaResourceApi`ЕÑть неÑколько методов, которые можно обработать `cdvfile://` URL-адреÑа: + + // 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)); + + +Это также можно иÑпользовать плагин файл непоÑредÑтвенно: + + import cordova-plugin-file.FileUtils; + import cordova-plugin-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 + } + + +Ð”Ð»Ñ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ к `cdvfile://` URL-адреÑ: + + import cordova-plugin-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(); + + +ЕÑли ваш плагин Ñоздает файл, и вы хотите вернуть объект FileEntry Ð´Ð»Ñ Ð½ÐµÐ³Ð¾, иÑпользуйте файл плагина: + + // 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 + +Кордова на iOS не иÑпользовать те же `CordovaResourceApi` понÑтие, как Android. Ðа iOS вы должны иÑпользовать файл плагин Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ URL-адреÑа и пути файловой ÑиÑтемы. + + // 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]; + + +ЕÑли ваш плагин Ñоздает файл, и вы хотите вернуть объект FileEntry Ð´Ð»Ñ Ð½ÐµÐ³Ð¾, иÑпользуйте файл плагина: + + // 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 + +Ð’ JavaScript, чтобы получить `cdvfile://` URL от объекта DirectoryEntry или FileEntry, проÑто позвоните `.toURL()` на него: + + var cdvfileURL = entry.toURL(); + + +Ð’ плагин обработчики ответ Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð· возвращаемой Ñтруктуры FileEntry объект фактичеÑкого вÑтуплениÑ, код обработчика Ñледует импортировать файл плагина и Ñоздайте новый объект: + + // 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/plugins/cordova-plugin-file/doc/zh/README.md b/plugins/cordova-plugin-file/doc/zh/README.md new file mode 100644 index 0000000..c600587 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/zh/README.md @@ -0,0 +1,335 @@ + + +# cordova-plugin-file + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-file.svg)](https://travis-ci.org/apache/cordova-plugin-file) + +這個外掛程å¼å¯¦ç¾æª” API å…è¨±å°æª”é§ç•™åœ¨è©²è¨­å‚™ä¸Šçš„讀/寫訪å•。 + +這個外掛程å¼åŸºæ–¼å¹¾å€‹è¦æ ¼ï¼ŒåŒ…括: HTML5 檔 API [HTTP://www.w3.org/TR/FileAPI/](http://www.w3.org/TR/FileAPI/) + +(ç¾å·²è§£æ•£ï¼‰ 目錄和系統擴展最新: [HTTP://www.w3.org/TR/2012/WD-file-system-api-20120417/](http://www.w3.org/TR/2012/WD-file-system-api-20120417/)雖然大部分的外掛程å¼ä»£ç¢¼å¯«æ™‚è¼ƒæ—©çš„è¦æ ¼æ˜¯ç•¶å‰ï¼š [HTTP://www.w3.org/TR/2011/WD-file-system-api-20110419/](http://www.w3.org/TR/2011/WD-file-system-api-20110419/) + +å®ƒé‚„å¯¦ç¾ FileWriter è¦æ ¼ï¼š [HTTP://dev.w3.org/2009/dap/file-system/file-writer.html](http://dev.w3.org/2009/dap/file-system/file-writer.html) + +用法,請åƒé–±å° HTML5 的岩石優秀[檔案系統文章。](http://www.html5rocks.com/en/tutorials/file/filesystem/) + +其他存儲é¸é …的概述,請åƒé–±ç§‘爾多瓦的[存儲指å—](http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html). + +這個外掛程å¼å®šç¾©å…¨çƒ `cordova.file` 物件。 + +雖然在全çƒç¯„åœå…§ï¼Œå®ƒä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-file + + +## 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ± + * iOS + * Windows Phone 7 å’Œ 8 * + * Windows 8 * + * Windows* + * ç€è¦½å™¨ + +\* *These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`.* + +## 存儲檔的ä½ç½® + +截至 v1.2.0,æä¾›é‡è¦çš„æª”案系統目錄的 Url。 æ¯å€‹ URL 使–¼è¡¨å–® *file:///path/to/spot/*,和å¯ä»¥è½‰æ›ç‚ºä½¿ç”¨ `window.resolveLocalFileSystemURL()` çš„ `DirectoryEntry`. + + * `cordova.file.applicationDirectory`-唯讀目錄在哪裡安è£çš„æ‡‰ç”¨ç¨‹å¼ã€‚(*iOS*ã€*安å“*ã€*黑莓 10*) + + * `cordova.file.applicationStorageDirectory`-根目錄下的應用程å¼çš„æ²™ç®± ;在 iOS 上此ä½ç½®æ˜¯å”¯è®€ (但特定的å­ç›®éŒ„ [åƒ `/Documents` ] 都是讀寫)。 中包å«çš„æ‰€æœ‰è³‡æ–™éƒ½æ˜¯ç§æœ‰çš„æ‡‰ç”¨ç¨‹å¼ã€‚ ( *iOS*ã€*安å“*ã€*黑莓 10*) + + * `cordova.file.dataDirectory`資料æŒä¹…æ€§å’Œç§æœ‰è³‡æ–™å­˜å„²åœ¨å…§éƒ¨è¨˜æ†¶é«”使用的應用程å¼çš„æ²™ç®±å…§ (在安å“系統,如果你需è¦ä½¿ç”¨å¤–部儲存體,使用 `.externalDataDirectory` )。 在 iOS,此目錄ä¸èˆ‡ iCloud åŒæ­¥ (使用 `.syncedDataDirectory` )。 (*iOS*ã€*安å“*ã€*黑莓 10*) + + * `cordova.file.cacheDirectory`-緩存的資料檔案或您的應用程å¼é‡æ–°å¯ä»¥è¼•鬆地創建的任何檔的目錄。 作業系統å¯èƒ½æœƒåˆªé™¤é€™äº›æª”,該設備在存儲上é‹è¡Œä½Žæ™‚,然而,應用程å¼ä¸æ‡‰ä¾è³´çš„作業系統,請刪除檔在這裡。 (*iOS*ã€*安å“*ã€*黑莓 10*) + + * `cordova.file.externalApplicationStorageDirectory`-應用程å¼å¤–部存儲上的空間。(*安å“*) + + * `cordova.file.externalDataDirectory`-放在外部存儲特定于應用程å¼çš„資料檔案的ä½ç½®ã€‚(*安å“*) + + * `cordova.file.externalCacheDirectory`-在外部存儲應用程å¼ç·©å­˜ã€‚(*安å“*) + + * `cordova.file.externalRootDirectory`-外部存儲 (SD å¡ï¼‰ 的根。(*安å“*ã€*黑莓 10*) + + * `cordova.file.tempDirectory`-OS å¯ä»¥æ¸…除時的空目錄會。 ä¸ä¾è³´äºŽ OS,以清除此目錄 ;您的應用程å¼ï¼Œæ‡‰ç¸½æ˜¯ç§»é™¤ä½œç‚ºé©ç”¨çš„æª”。 (*iOS*) + + * `cordova.file.syncedDataDirectory`-ä¿å­˜æ‡‰åŒæ­¥ (例如到 iCloud) 的特定于應用程å¼çš„æª”。(*iOS*) + + * `cordova.file.documentsDirectory`-æª”ç§æœ‰çš„æ‡‰ç”¨ç¨‹å¼ï¼Œä½†é€™æ˜¯å°å…¶ä»–æ‡‰ç”¨ç¨‹å¼ ï¼ˆä¾‹å¦‚ Office 檔) 有æ„義。(*iOS*) + + * `cordova.file.sharedDirectory`-å°æ‰€æœ‰æ‡‰ç”¨ç¨‹å¼ (*黑莓 10*全域å¯ç”¨çš„æª”) + +## 檔案系統佈局 + +雖然技術上實ç¾çš„細節,它å¯ä»¥æ˜¯å¾ˆæœ‰å¿…è¦çž­è§£å¦‚何 `cordova.file.*` å±¬æ€§å°æ‡‰åˆ°å¯¦é«”路徑在實際設備上。 + +### iOS 檔案系統佈局 + +| 設備路徑 | `cordova.file.*` | `iosExtraFileSystems` | r/w 嗎? | æŒçºŒæ€§å—Žï¼Ÿ | OS 清除 | åŒæ­¥ | ç§äºº | +|:---------------------------------------------- |:--------------------------- |:--------------------- |:------:|:-------:|:------------:|:---:|:--:| +| `/ 無功/移動/應用程å¼/< UUID > /` | applicationStorageDirectory | - | r | ä¸é©ç”¨ | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `appname.app/` | applicationDirectory | æŸ | r | ä¸é©ç”¨ | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|       `www/` | - | - | r | ä¸é©ç”¨ | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `Documents/` | documentsDirectory | 檔 | r/w | 是啊 | ç„¡ | 是啊 | 是啊 | +|       `NoCloud/` | - | 檔 nosync | r/w | 是啊 | ç„¡ | ç„¡ | 是啊 | +|    `Library` | - | 圖書館 | r/w | 是啊 | ç„¡ | 是嗎? | 是啊 | +|       `NoCloud/` | dataDirectory | 圖書館 nosync | r/w | 是啊 | ç„¡ | ç„¡ | 是啊 | +|       `Cloud/` | syncedDataDirectory | - | r/w | 是啊 | ç„¡ | 是啊 | 是啊 | +|       `Caches/` | cacheDirectory | å¿«å–記憶體 | r/w | 是啊 * | 是啊**\* | ç„¡ | 是啊 | +|    `tmp/` | tempDirectory | - | r/w | ç„¡** | 是啊**\* | ç„¡ | 是啊 | + +\ * 檔堅æŒè·¨æ‡‰ç”¨ç¨‹å¼é‡æ–°é–‹æ©Ÿå’Œå‡ç´šï¼Œä½†æ¯ç•¶ OS 的欲望,å¯ä»¥æ¸…é™¤æ­¤ç›®éŒ„ã€‚æ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ‡‰è©²èƒ½å¤ é‡æ–°å‰µå»ºä»»ä½•內容å¯èƒ½æœƒè¢«åˆªé™¤ã€‚ + +**檔å¯èƒ½æœƒæŒçºŒæ•´å€‹æ‡‰ç”¨ç¨‹å¼é‡æ–°é–‹æ©Ÿï¼Œä½†ä¸æ˜¯ä¾è³´äºŽé€™ç¨®è¡Œç‚ºã€‚ ä¸ä¿è­‰æª”在更新之間æŒçºŒå­˜åœ¨ã€‚ æ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ™‚é©ç”¨ï¼Œæ˜¯æ‡‰è©²åˆªé™¤æ­¤ç›®éŒ„的檔,因為作業系統並ä¸èƒ½ä¿è­‰ä½•時 (或å³ä½¿ï¼‰ 中刪除這些檔。 + +**\ * 作業系統å¯èƒ½æœƒæ¸…除此目錄的內容,æ¯ç•¶å®ƒæ„Ÿè¦ºå®ƒæ˜¯å¿…è¦çš„但åšä¸ä¾è³´äºŽæ­¤ã€‚ 你應該清除此目錄為é©åˆæ‚¨çš„æ‡‰ç”¨ç¨‹å¼ã€‚ + +### Android 的檔案系統佈局 + +| 設備路徑 | `cordova.file.*` | `AndroidExtraFileSystems` | r/w 嗎? | æŒçºŒæ€§å—Žï¼Ÿ | OS 清除 | ç§äºº | +|:------------------------------------------------ |:----------------------------------- |:------------------------- |:------:|:-----:|:-------:|:--:| +| `file:///android_asset/` | applicationDirectory | | r | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +| `/ 資料資料/< æ‡‰ç”¨ç¨‹å¼ id > /` | applicationStorageDirectory | - | r/w | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `cache` | cacheDirectory | å¿«å–記憶體 | r/w | 是啊 | 是啊\* | 是啊 | +|    `files` | dataDirectory | 檔 | r/w | 是啊 | ç„¡ | 是啊 | +|       `Documents` | | 檔 | r/w | 是啊 | ç„¡ | 是啊 | +| `< sd å¡ > /` | externalRootDirectory | sd å¡ | r/w | 是啊 | ç„¡ | ç„¡ | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | 是啊 | ç„¡ | ç„¡ | +|       `cache` | externalCacheDirectry | 外部快å–記憶體 | r/w | 是啊 | ç„¡** | ç„¡ | +|       `files` | externalDataDirectory | 外部檔 | r/w | 是啊 | ç„¡ | ç„¡ | + +\ * 作業系統å¯èƒ½å®šæœŸæ¸…é™¤æ­¤ç›®éŒ„ï¼Œä½†ä¸æ˜¯ä¾è³´äºŽé€™ç¨®è¡Œç‚ºã€‚ 清除此為é©åˆæ‚¨çš„æ‡‰ç”¨ç¨‹å¼çš„目錄的內容。 使用者應手動清除緩存,將刪除此目錄的內容。 + +** OS 䏿œƒæ¸…除此目錄自動;你是負責管ç†è‡ªå·±çš„內容。 使用者應手動清除緩存,目錄中的內容將被刪除。 + +**注**: 如果外部存儲無法è£å…¥ï¼Œ`cordova.file.external*` 屬性為 `空`. + +### 黑莓 10 檔案系統佈局 + +| 設備路徑 | `cordova.file.*` | r/w 嗎? | æŒçºŒæ€§å—Žï¼Ÿ | OS 清除 | ç§äºº | +|:----------------------------------------------------------- |:--------------------------- |:------:|:-----:|:-----:|:--:| +| `file:///accounts/1000/appdata/ < æ‡‰ç”¨ç¨‹å¼ id > /` | applicationStorageDirectory | r | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `app/native` | applicationDirectory | r | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | ç„¡ | 是啊 | 是啊 | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | 是啊 | ç„¡ | 是啊 | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | 是啊 | ç„¡ | ç„¡ | +| `file:///accounts/1000/shared` | sharedDirectory | r/w | 是啊 | ç„¡ | ç„¡ | + +*注æ„*: 當應用程å¼éƒ¨ç½²å·¥ä½œé€±é‚Šæ™‚ï¼Œæ‰€æœ‰è·¯å¾‘éƒ½æ˜¯ç›¸å°æ–¼ /accounts/1000-enterprise。 + +## Android 的怪癖 + +### Android çš„æŒä¹…性存儲ä½ç½® + +有很多有效的ä½ç½®ä¾†å­˜å„²æŒä¹…性檔在 Android 設備上。 è«‹åƒé–± [æ­¤é é¢](http://developer.android.com/guide/topics/data/data-storage.html) çš„å„種å¯èƒ½æ€§é€²è¡Œå»£æ³›è¨Žè«–。 + +以å‰ç‰ˆæœ¬çš„å¤–æŽ›ç¨‹å¼æœƒé¸æ“‡åœ¨å•Ÿå‹•時,基於該設備是å¦è²ç¨± SD å¡ ï¼ˆæˆ–ç­‰æ•ˆå­˜å„²åˆ†å€ï¼‰ 展開,臨時和永久檔的ä½ç½®ã€‚ 如果被掛載 SD å¡ï¼Œæˆ–一個大的內部存儲分å€å¯ç”¨ (如 nexus 系列設備上) 然後æŒä¹…性檔將存儲在該空間的根目錄中。 這æ„味著所有的科爾多瓦應用程å¼å¯ä»¥çœ‹åˆ°æ‰€æœ‰å¯ç”¨çš„æª”在å¡ç‰‡ä¸Šã€‚ + +如果 SD å¡ä¸æ˜¯å¯ç”¨çš„那麼以å‰çš„版本中將存儲資料下的 `/data/data/`,其中隔離應用程å¼å¾žå½¼æ­¤ï¼Œä½†ä»å¯èƒ½å°Žè‡´ä½¿ç”¨è€…之間共用的資料。 + +ç¾åœ¨å¯ä»¥é¸æ“‡æ˜¯å¦å°‡æª”存儲在內部檔的存儲ä½ç½®ï¼Œæˆ–使用以å‰çš„é‚輯,與您的應用程å¼çš„ `config.xml` 檔中的å好。 è¦åŸ·è¡Œæ­¤æ“作,請將以下兩行之一添加到 `config.xml`: + + + + + + +如果這æ¢ç·šï¼Œæ²’有檔外掛程å¼å°‡ä½¿ç”¨ `Compatibility` 作為é è¨­å€¼ã€‚如果首é¸é …æ¨™è¨˜å­˜åœ¨ï¼Œä¸¦ä¸æ˜¯é€™äº›å€¼ä¹‹ä¸€ï¼Œæ‡‰ç”¨ç¨‹å¼å°‡ç„¡æ³•啟動。 + +如果您的應用程å¼ä»¥å‰å·²è¢«é‹åˆ°ä½¿ç”¨è€…,使用較舊的 ï¼ˆé  1.0) 版本的這個外掛程å¼ï¼Œä¸¦å…·æœ‰æŒä¹…性的檔,系統中存儲的檔,然後您應該設置 `Compatibility` å好。 切æ›åˆ°"Internal"çš„ä½ç½®ï¼Œå°‡æ„å‘³è‘—ç¾æœ‰ä½¿ç”¨è€…å‡ç´šä»–們的應用程å¼å¯èƒ½ç„¡æ³•訪å•他們以å‰å­˜å„²çš„æª”ï¼Œå…·é«”å–æ±ºæ–¼ä»–們的設備。 + +å¦‚æžœæ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ˜¯æ–°çš„æˆ–以å‰å¾žæœªæœ‰æŒä¹…性的檔案系統中存儲檔,那麼通常建議使用 `Internal` 設置。 + +### 緩慢的éžè¿´æ“作為 /android_asset çš„ + +上市資產目錄是在 android 系統很慢的。 ä½ å¯ä»¥è®“時間加速了ä¸éŽï¼Œé€šéŽå°‡`src/android/build-extras.gradle`添加到您的 android 專案的根 (ä¹Ÿéœ€è¦ cordova-android@4.0.0 或更高版本)。 + +## iOS 的怪癖 + + * `cordova.file.applicationStorageDirectory`是唯讀的 ;試圖存儲內的根目錄中的檔將會失敗。 使用的å¦ä¸€å€‹ `cordova.file.*` 為 iOS 定義的屬性 ï¼ˆåªæœ‰ `applicationDirectory` å’Œ `applicationStorageDirectory` 都是唯讀)。 + * `FileReader.readAsText(blob, encoding)` + * `encoding`åƒæ•¸ä¸å—支æ´ï¼Œè€Œ utf-8 編碼總是效果。 + +### iOS çš„æŒä¹…性存儲ä½ç½® + +有兩個有效的ä½ç½®ä¾†å­˜å„²æŒä¹…性在 iOS 設備上的檔: 檔目錄和圖書館目錄。 以å‰ç‰ˆæœ¬çš„å¤–æŽ›ç¨‹å¼æ°¸é åªèƒ½å°‡æŒä¹…性檔存儲在文檔目錄中。 é€™å·²ç¶“ä½¿æ‰€æœ‰æ‡‰ç”¨ç¨‹å¼æª”å¯è¦‹åœ¨ iTunes,往往是無æ„ç‚ºä¹‹ï¼Œå°¤å…¶æ˜¯å°æ–¼è™•ç†å¤§é‡å°æª”的應用程å¼ä¸­ï¼Œè€Œä¸æ˜¯ç”Ÿç”¢ä¾›å‡ºå£ï¼Œè©²ç›®éŒ„的既定的目標是證件齊全的副作用。 + +ç¾åœ¨å¯ä»¥é¸æ“‡æ˜¯å¦å°‡æª”存儲在檔或庫目錄,與您的應用程å¼çš„ `config.xml` 檔中的å好。 è¦åŸ·è¡Œæ­¤æ“作,請將以下兩行之一添加到 `config.xml`: + + + + + + +如果這æ¢ç·šï¼Œæ²’有檔外掛程å¼å°‡ä½¿ç”¨ `Compatibility` 作為é è¨­å€¼ã€‚如果首é¸é …æ¨™è¨˜å­˜åœ¨ï¼Œä¸¦ä¸æ˜¯é€™äº›å€¼ä¹‹ä¸€ï¼Œæ‡‰ç”¨ç¨‹å¼å°‡ç„¡æ³•啟動。 + +如果您的應用程å¼ä»¥å‰å·²è¢«é‹åˆ°ä½¿ç”¨è€…,使用較舊的 ï¼ˆé  1.0) 版本的這個外掛程å¼ï¼Œä¸¦å…·æœ‰æŒä¹…性的檔,系統中存儲的檔,然後您應該設置 `Compatibility` å好。 切æ›åˆ° `Library` çš„ä½ç½®ï¼Œå°‡æ„å‘³è‘—ç¾æœ‰ä½¿ç”¨è€…å‡ç´šä»–們的應用程å¼å°‡ç„¡æ³•訪å•他們以å‰å­˜å„²çš„æª”。 + +å¦‚æžœæ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ˜¯æ–°çš„æˆ–以å‰å¾žæœªæœ‰æŒä¹…性的檔案系統中存儲檔,那麼通常建議使用 `Internal` 設置。 + +## ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +檔案系統 API æœ¬èº«ä¸æ”¯æ´ç«ç‹ç€è¦½å™¨çš„作業系統,作為墊片在 indexedDB 上實ç¾çš„。 + + * 䏿œƒå¤±æ•—時刪除éžç©ºçš„目錄 + * 䏿”¯æ´ä¸­ç¹¼è³‡æ–™çš„目錄 + * 方法 `copyTo` å’Œ `moveTo` 䏿”¯æ´ç›®éŒ„ + +支æ´ä»¥ä¸‹è³‡æ–™è·¯å¾‘: * `applicationDirectory`-使用 `xhr` ç²å–èˆ‡æ‡‰ç”¨ç¨‹å¼æ‰“包的本地檔。 `dataDirectory`-用於æŒä¹…性的特定于應用程å¼çš„資料檔案。 `cacheDirectory`-ç”Ÿå­˜æ‡‰é‡æ–°é–‹æ©Ÿæ‡‰ç”¨ç¨‹å¼çš„å¿«å–æª”案 (應用程å¼ä¸æ‡‰ä¾è³´ä½œæ¥­ç³»çµ±ä¾†åˆªé™¤æª”在這裡)。 + +## ç€è¦½å™¨çš„æ€ªç™– + +### 常見的怪癖和備註 + + * æ¯å€‹ç€è¦½å™¨ä½¿ç”¨å…¶è‡ªå·±çš„æ²™ç®±æª”案系統。IE å’Œç«ç‹ç€è¦½å™¨ä½¿ç”¨ IndexedDB 作為一個基地。所有ç€è¦½å™¨éƒ½ä½¿ç”¨æ­£æ–œæ ä½œç‚ºè·¯å¾‘中的目錄分隔符號。 + * 目錄æ¢ç›®ä¸å¾—ä¸å…ˆå¾Œå‰µå»ºã€‚ 例如,調用 `fs.root.getDirectory (' dir1/dir2 ',{create:true},successCallback,errorCallback)`,如果ä¸å­˜åœ¨ dir1 將失敗。 + * 外掛程å¼å°‡è«‹æ±‚ä½¿ç”¨è€…è¨±å¯æ¬Šï¼Œä»¥ä¾¿åœ¨æ‡‰ç”¨ç¨‹å¼åˆæ¬¡é–‹æ©Ÿä½¿ç”¨æŒä¹…性存儲。 + * å¤–æŽ›ç¨‹å¼æ”¯æ´ `cdvfile://localhost` (本地資æºï¼‰ åªã€‚é€šéŽ `cdvfile` 䏿”¯æ´å¤–部資æºå³. + * 該外掛程å¼ä¸éµå¾ª ["檔案系統 API 8.3 命åé™åˆ¶"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). + * Blob 和檔 ' `close` 功能ä¸å—支æ´ã€‚ + * `FileSaver` å’Œ `BlobBuilder` 䏿”¯æ´é€™å€‹å¤–掛程å¼ï¼Œæ²’有存根 (stub)。 + * 該外掛程å¼ä¸æ”¯æ´ `requestAllFileSystems`。這個功能也是缺少è¦ç¯„中。 + * 在目錄中的æ¢ç›®å°‡ä¸æœƒè¢«åˆªé™¤ï¼Œå¦‚果您使用 `create: true` æ¨™èªŒç‚ºç¾æœ‰ç›®éŒ„。 + * 䏿”¯æ´é€šéŽå»ºæ§‹å‡½å¼å‰µå»ºçš„æª”。你應該使用 entry.file 方法。 + * æ¯å€‹ç€è¦½å™¨ä½¿ç”¨å®ƒè‡ªå·±çš„å½¢å¼ç‚º blob çš„ URL 引用。 + * æ”¯æ´ `readAsDataURL` 功能,但在 Chrome ä¸­çš„åª’é«”é¡žåž‹å–æ±ºæ–¼è¼¸å…¥å‰¯æª”å,在 IE 中的媒體類型都始終空著 (這是 `純文字` 按照說明書一樣),在 Firefox 中的媒體類型始終是 `應用程å¼/å…«ä½ä½å…ƒçµ„æµ`。 例如,如果內容是 `abcdefg` 然後ç«ç‹ç€è¦½å™¨è¿”回 `資料: æ‡‰ç”¨ç¨‹å¼ / å…«ä½ä½å…ƒçµ„æµ ï¼› base64,YWJjZGVmZw = =`,å³è¿”回 `資料: ï¼› base64,YWJjZGVmZw = =`,鉻返回 `資料: < 媒體類型根據擴展æ¢ç›®å稱 > ï¼› base64,YWJjZGVmZw = =`. + * 在表單 `file:///persistent/path/to/entry` ç«ç‹ç€è¦½å™¨ IE),`toInternalURL` 返回的路徑。 鉻在表單 `cdvfile://localhost/persistent/file` 返回的路徑. + +### 鉻的怪癖 + + * 設備準備好事件之後,chrome 檔案系統並ä¸èƒ½ç«‹å³æº–備。作為一種變通方法,您å¯ä»¥è¨‚閱到 `filePluginIsReady` 事件。示例: + +```javascript +window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); +``` + +ä½ å¯ä»¥ä½¿ç”¨ `window.isFilePluginReadyRaised` 函數來檢查是å¦å·²ç¶“引發了事件。 -window.requestFileSystem 臨時和永久性檔案系統é…é¡ä¸¦ä¸å±€é™äºŽé‰»ã€‚ 為增加中鉻的æŒä¹…性存儲,您需è¦èª¿ç”¨ `window.initPersistentFileSystem` 方法。 é è¨­æƒ…æ³ä¸‹ï¼ŒæŒä¹…性存儲é…é¡ç‚º 5 MB。 é‰»éœ€è¦ `— — å…許--訪å•-從-檔` é€šéŽ `file:///` å”定é‹è¡Œåƒæ•¸å° API 的支æ´ã€‚ -å¦‚æžœæ‚¨ä½¿ç”¨æ¨™èªŒï¼Œå°‡ä¸æ›´æ”¹ `檔` 物件 `{create:true}` ç¾æœ‰ `æ¢ç›®` 的時候。 -事件 `å¯å–消` 屬性設置為 true 在 Chrome 中。 這是é•å了 [è¦ç¯„](http://dev.w3.org/2009/dap/file-system/file-writer.html)。 -中鉻的 `toURL` 函數返回 `檔案系統:`-é¦–ç¢¼è·¯å¾‘å…·é«”å–æ±ºæ–¼æ‡‰ç”¨ç¨‹å¼ä¸»æ©Ÿã€‚ 例如,`filesystem:file:///persistent/somefile.txt`,`filesystem:HTTP://localhost:8080/persistent/somefile.txt`。 -`toURL` å‡½æ•¸çµæžœä¸åŒ…å«å°¾éƒ¨å斜線在目錄æ¢ç›®çš„æƒ…æ³ä¸‹ã€‚ 鉻雖然正確解æžç›®éŒ„帶斜æ è½å¾Œçš„ url。 -`resolveLocalFileSystemURL` 方法需è¦å…¥ç«™çš„ `url` 必須具有 `檔案系統` 首碼。 例如,`resolveLocalFileSystemURL` çš„ `url` åƒæ•¸æ‡‰åœ¨è¡¨å–® `filesystem:file:///persistent/somefile.txt` è€Œä¸æ˜¯è¡¨å–® `file:///persistent/somefile.txt` 在安å“系統。 -ä¸æŽ¨è–¦ä½¿ç”¨ `toNativeURL` 函數ä¸å—支æ´ï¼Œä¸¦ä¸”沒有存根 (stub)。 -`setMetadata` åŠŸèƒ½æ˜¯æ²’æœ‰èªªå‡ºçš„è¦æ ¼ï¼Œä¸¦ä¸”䏿”¯æ´ã€‚ -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 SYNTAX_ERR(code: 8) 上請求一個ä¸å­˜åœ¨çš„æª”案系統。 -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 PATH_EXISTS_ERR(code: 12) 上嘗試專門創建一個檔或目錄,它已經存在。 -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 NO_MODIFICATION_ALLOWED_ERR(code: 6) 在試圖調用 removeRecursively 的根檔案系統上。 -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 NOT_FOUND_ERR(code: 1) 試到 moveTo 目錄ä¸å­˜åœ¨ã€‚ + +### 基於 IndexedDB çš„ impl 怪癖 (Firefox å’Œ IE) + + * `.` å’Œ `.` ä¸å—支æ´ã€‚ + * IE 䏿”¯æ´ `file:///`-æ¨¡å¼ ï¼›åªæœ‰è¨—ç®¡çš„æ¨¡å¼æ˜¯æ”¯æ´ (HTTP://localhost:xxxx)。 + * ç«ç‹ç€è¦½å™¨çš„æª”案系統大å°ä¸æ˜¯æœ‰é™ï¼Œä½†æ¯å€‹ 50 MB æ“´å±•æœƒè¦æ±‚ä½¿ç”¨è€…çš„è¨±å¯æ¬Šã€‚ IE10 å…è¨±é” 10 mb çš„ AppCache å’Œ IndexedDB 檔案系統的實ç¾ä¸­ä½¿ç”¨è€Œä¸æœƒæç¤ºï¼Œä¸€æ—¦ä½ é”åˆ°é€™ä¸€æ°´æº–ä½ æœƒè©¢å•æ‚¨æ˜¯å¦å…許它增加到æ¯å€‹ç¶²ç«™çš„ 250 mb 的最大åˆä½µã€‚ 所以 `requestFileSystem` 函數的 `大å°` åƒæ•¸ä¸¦ä¸å½±éŸ¿æª”案系統相容 Firefox å’Œ IE。 + * `readAsBinaryString` 函數在è¦ç¯„ä¸­æ²’æœ‰æ³¨æ˜Žä¸æ”¯æ´åœ¨ IE 中和沒有存根 (stub)。 + * `file.type` 始終為 null。 + * æ‚¨ä¸æ‡‰å‰µå»ºæ¢ç›®ä½¿ç”¨å·²åˆªé™¤çš„ç›®éŒ„å¯¦ä¾‹å›žæª”çµæžœã€‚å¦å‰‡ï¼Œä½ æœƒå¾—到一個 '掛æ¢ç›®'。 + * 您å¯ä»¥è®€å–ä¸€å€‹æª”ï¼Œå…¶ä¸­åªæ˜¯å¯«ä¹‹å‰ä½ éœ€è¦ç²å¾—的此檔的新實例。 + * `setMetadata` 函數,在è¦ç¯„ä¸­æ²’æœ‰æ³¨æ˜Žæ”¯æ´ `modificationTime` 欄ä½çš„æ›´æ”¹ã€‚ + * `copyTo` å’Œ `moveTo` å‡½æ•¸ä¸æ”¯æ´ç›®éŒ„。 + * 䏿”¯æ´ç›®éŒ„的中繼資料。 + * 兩個 Entry.remove å’Œ directoryEntry.removeRecursively ä¸å¤±æ•—時刪除éžç©ºçš„目錄-相å與內容一起清ç†ç›®éŒ„被刪除。 + * 䏿”¯æ´ `abort` å’Œ `truncate` 函數。 + * é€²åº¦äº‹ä»¶ä¸æœƒè§¸ç™¼ã€‚例如,將ä¸åŸ·è¡Œæ­¤è™•ç†ç¨‹å¼ï¼š + +```javascript +writer.onprogress = function() { /*commands*/ }; +``` + +## å‡ç´šç­†è¨˜ + +åœ¨é€™å€‹å¤–æŽ›ç¨‹å¼ v1.0.0,`FileEntry` å’Œ `DirectoryEntry` çš„çµæ§‹å·²ç¶“改變,以更加一致的已發表說明書。 + +ä»¥å‰ (pre-1.0.0) 版本的外掛程å¼ä¸­ `輸入` 物件的 `完整路徑` 屬性存放è£ç½®å›ºå®šæª”案ä½ç½®ã€‚é€™äº›è·¯å¾‘é€šå¸¸æœƒçœ‹èµ·ä¾†åƒ + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +這些路徑還返回的 `Entry` 物件的 `toURL()` 方法。 + +與 v1.0.0,`完整路徑` 屬性是到檔中,*ç›¸å°æ–¼ HTML 檔案系統的根目錄* 的路徑。 因此,上述路徑會ç¾åœ¨éƒ½ç”±ä¸€å€‹ `FileEntry` 物件的 `完整路徑`, + + /path/to/file + + +如果您的應用程å¼èˆ‡è¨­å‚™-絕å°è·¯å¾‘ï¼Œä¸¦ä¸”æ‚¨ä»¥å‰æª¢ç´¢åˆ°é€™äº›è·¯å¾‘é€šéŽ `æ¢ç›®` 物件的 `完整路徑` 屬性,然後您應該更新您的代碼以改用 `entry.toURL()`。 + +為å‘後相容性,`resolveLocalFileSystemURL()` 方法將接å—一個設備-絕å°è·¯å¾‘,並將返回相應的 `æ¢ç›®` 物件,åªè¦åœ¨ `臨時` 或 `永久性` 的檔案系統內的檔是å¦å­˜åœ¨ã€‚ + +這尤其是一直與檔案傳輸外掛程å¼ï¼Œä»¥å‰ä½¿ç”¨è¨­å‚™çµ•å°è·¯å¾‘çš„å•題 (å’Œä»ç„¶å¯ä»¥æŽ¥å—他們)。 已更新它能夠正常é‹è¡Œèˆ‡æª”案系統的 Url,所以用 `entry.toURL()` æ›¿æ› `entry.fullPath` 應解決任何å•題,得到該外掛程å¼ä¾†è™•ç†è¨­å‚™ä¸Šçš„æª”。 + +在 v1.1.0 `toURL()` 的傳回值被更改 (見 [CB-6394] (HTTPs://issues.apache.org/jira/browse/CB-6394)) ç‚ºè¿”å›žçµ•å° file:// URL。 åªè¦æœ‰å¯èƒ½ã€‚ ç¢ºä¿ 'cdvfile:' — — ä½ ç¾åœ¨å¯ä»¥ç”¨ `toInternalURL()` çš„ URL。 ç¾åœ¨ï¼Œæ­¤æ–¹æ³•將返回檔案系統表單的 Url + + cdvfile://localhost/persistent/path/to/file + + +它å¯ä»¥ç”¨æ–¼å”¯ä¸€åœ°æ¨™è­˜è©²æª”。 + +## 錯誤代碼åŠå…¶å«ç¾©çš„æ¸…單中 + +當拋出一個錯誤時,將使用以下代碼之一。 + +| 代碼 | æ’ | +| --:|:----------------------------- | +| 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` | + +## é…ç½®å¤–æŽ›ç¨‹å¼ ï¼ˆå¯é¸ï¼‰ + +å¯ç”¨çš„æª”案系統的一整套å¯ä»¥é…ç½®æ¯å€‹å¹³è‡ºã€‚IOS 和安å“系統èªè­˜åˆ° 在 `config.xml` å稱è¦å®‰è£çš„æª”案系統中的標記。é è¨­æƒ…æ³ä¸‹ï¼Œå•Ÿç”¨æ‰€æœ‰æª”案系統的根。 + + + + + +### Android 系統 + + * `files`: 該應用程å¼çš„內部檔存儲目錄 + * `files-external`: 應用程å¼çš„外部檔存儲目錄 + * `sdcard`: å…¨çƒå¤–部檔存儲目錄 (如果安è£äº†ä¸€å€‹ï¼Œé€™æ˜¯ SD å¡çš„æ ¹ç›®éŒ„)。 你必須具有 `android.permission.WRITE_EXTERNAL_STORAGE` è¨±å¯æ¬Šï¼Œç”¨é€™å€‹ã€‚ + * `cache`: 應用程å¼çš„內部緩存目錄 + * `cache-external`: 應用程å¼çš„外部快å–記憶體目錄 + * `root`: 整個設備的檔案系統 + +安å“系統還支æ´ç‰¹æ®Šçš„æª”命å為"檔",表示"/ 檔 /""檔"的檔案系統中的å­ç›®éŒ„。 + +### iOS + + * `library`: 應用程å¼çš„庫目錄 + * `documents`: 應用程å¼çš„æª”目錄 + * `cache`: 應用程å¼çš„緩存目錄 + * `bundle`: 應用程å¼çš„包 ï¼›æ‡‰ç”¨ç¨‹å¼æœ¬èº« (唯讀) çš„ç£ç‰‡ä¸Šçš„ä½ç½® + * `root`: 整個設備的檔案系統 + +é è¨­æƒ…æ³ä¸‹ï¼Œåœ–書館和檔目錄å¯ä»¥åŒæ­¥åˆ° iCloud。 您也å¯ä»¥è¦æ±‚兩個é¡å¤–的檔案系統〠`library-nosync` å’Œ `documents-nosync`,代表一個特殊的éžåŒæ­¥ç›®éŒ„å…§ `/Library` 或 `/Documents` 的檔案系統。 \ No newline at end of file diff --git a/plugins/cordova-plugin-file/doc/zh/index.md b/plugins/cordova-plugin-file/doc/zh/index.md new file mode 100644 index 0000000..6eec44d --- /dev/null +++ b/plugins/cordova-plugin-file/doc/zh/index.md @@ -0,0 +1,343 @@ + + +# cordova-plugin-file + +這個外掛程å¼å¯¦ç¾æª” API å…è¨±å°æª”é§ç•™åœ¨è©²è¨­å‚™ä¸Šçš„讀/寫訪å•。 + +這個外掛程å¼åŸºæ–¼å¹¾å€‹è¦æ ¼ï¼ŒåŒ…括: HTML5 檔 API [HTTP://www.w3.org/TR/FileAPI/][1] + + [1]: http://www.w3.org/TR/FileAPI/ + +(ç¾å·²è§£æ•£ï¼‰ 目錄和系統擴展最新: [HTTP://www.w3.org/TR/2012/WD-file-system-api-20120417/][2]雖然大部分的外掛程å¼ä»£ç¢¼å¯«æ™‚è¼ƒæ—©çš„è¦æ ¼æ˜¯ç•¶å‰ï¼š [HTTP://www.w3.org/TR/2011/WD-file-system-api-20110419/][3] + + [2]: http://www.w3.org/TR/2012/WD-file-system-api-20120417/ + [3]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/ + +å®ƒé‚„å¯¦ç¾ FileWriter è¦æ ¼ï¼š [HTTP://dev.w3.org/2009/dap/file-system/file-writer.html][4] + + [4]: http://dev.w3.org/2009/dap/file-system/file-writer.html + +用法,請åƒé–±å° HTML5 的岩石優秀[檔案系統文章。][5] + + [5]: http://www.html5rocks.com/en/tutorials/file/filesystem/ + +其他存儲é¸é …的概述,請åƒé–±ç§‘爾多瓦的[存儲指å—][6]. + + [6]: http://cordova.apache.org/docs/en/edge/cordova_storage_storage.md.html + +這個外掛程å¼å®šç¾©å…¨çƒ `cordova.file` 物件。 + +雖然在全çƒç¯„åœå…§ï¼Œå®ƒä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(cordova.file); + } + + +## å®‰è£ + + cordova plugin add cordova-plugin-file + + +## 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* ç«ç‹ç€è¦½å™¨çš„作業系統 +* iOS +* Windows Phone 7 å’Œ 8 * +* Windows 8 * +* ç€è¦½å™¨ + +* *é€™äº›å¹³è‡ºä¸æ”¯æ´ `FileReader.readAsArrayBuffer` 或 `FileWriter.write(blob)`.* + +## 存儲檔的ä½ç½® + +截至 v1.2.0,æä¾›é‡è¦çš„æª”案系統目錄的 Url。 æ¯å€‹ URL 使–¼è¡¨å–® *file:///path/to/spot/*,和å¯ä»¥è½‰æ›ç‚ºä½¿ç”¨ `window.resolveLocalFileSystemURL()` çš„ `DirectoryEntry`. + +* `cordova.file.applicationDirectory`-唯讀目錄在哪裡安è£çš„æ‡‰ç”¨ç¨‹å¼ã€‚(*iOS*ã€*安å“*ã€*黑莓 10*) + +* `cordova.file.applicationStorageDirectory`-根目錄下的應用程å¼çš„æ²™ç®± ;在 iOS 上此ä½ç½®æ˜¯å”¯è®€ (但特定的å­ç›®éŒ„ [åƒ `/Documents` ] 都是讀寫)。 中包å«çš„æ‰€æœ‰è³‡æ–™éƒ½æ˜¯ç§æœ‰çš„æ‡‰ç”¨ç¨‹å¼ã€‚ ( *iOS*ã€*安å“*ã€*黑莓 10*) + +* `cordova.file.dataDirectory`資料æŒä¹…æ€§å’Œç§æœ‰è³‡æ–™å­˜å„²åœ¨å…§éƒ¨è¨˜æ†¶é«”使用的應用程å¼çš„æ²™ç®±å…§ (在安å“系統,如果你需è¦ä½¿ç”¨å¤–部儲存體,使用 `.externalDataDirectory` )。 在 iOS,此目錄ä¸èˆ‡ iCloud åŒæ­¥ (使用 `.syncedDataDirectory` )。 (*iOS*ã€*安å“*ã€*黑莓 10*) + +* `cordova.file.cacheDirectory`-緩存的資料檔案或您的應用程å¼é‡æ–°å¯ä»¥è¼•鬆地創建的任何檔的目錄。 作業系統å¯èƒ½æœƒåˆªé™¤é€™äº›æª”,該設備在存儲上é‹è¡Œä½Žæ™‚,然而,應用程å¼ä¸æ‡‰ä¾è³´çš„作業系統,請刪除檔在這裡。 (*iOS*ã€*安å“*ã€*黑莓 10*) + +* `cordova.file.externalApplicationStorageDirectory`-應用程å¼å¤–部存儲上的空間。(*安å“*) + +* `cordova.file.externalDataDirectory`-放在外部存儲特定于應用程å¼çš„資料檔案的ä½ç½®ã€‚(*安å“*) + +* `cordova.file.externalCacheDirectory`-在外部存儲應用程å¼ç·©å­˜ã€‚(*安å“*) + +* `cordova.file.externalRootDirectory`-外部存儲 (SD å¡ï¼‰ 的根。(*安å“*ã€*黑莓 10*) + +* `cordova.file.tempDirectory`-OS å¯ä»¥æ¸…除時的空目錄會。 ä¸ä¾è³´äºŽ OS,以清除此目錄 ;您的應用程å¼ï¼Œæ‡‰ç¸½æ˜¯ç§»é™¤ä½œç‚ºé©ç”¨çš„æª”。 (*iOS*) + +* `cordova.file.syncedDataDirectory`-ä¿å­˜æ‡‰åŒæ­¥ (例如到 iCloud) 的特定于應用程å¼çš„æª”。(*iOS*) + +* `cordova.file.documentsDirectory`-æª”ç§æœ‰çš„æ‡‰ç”¨ç¨‹å¼ï¼Œä½†é€™æ˜¯å°å…¶ä»–æ‡‰ç”¨ç¨‹å¼ ï¼ˆä¾‹å¦‚ Office 檔) 有æ„義。(*iOS*) + +* `cordova.file.sharedDirectory`-å°æ‰€æœ‰æ‡‰ç”¨ç¨‹å¼ (*黑莓 10*全域å¯ç”¨çš„æª”) + +## 檔案系統佈局 + +雖然技術上實ç¾çš„細節,它å¯ä»¥æ˜¯å¾ˆæœ‰å¿…è¦çž­è§£å¦‚何 `cordova.file.*` å±¬æ€§å°æ‡‰åˆ°å¯¦é«”路徑在實際設備上。 + +### iOS 檔案系統佈局 + +| 設備路徑 | `cordova.file.*` | `iosExtraFileSystems` | r/w 嗎? | æŒçºŒæ€§å—Žï¼Ÿ | OS 清除 | åŒæ­¥ | ç§äºº | +|:------------------------------- |:--------------------------- |:--------------------- |:------:|:------:|:----------:|:---:|:--:| +| `/ 無功/移動/應用程å¼/< UUID > /` | applicationStorageDirectory | - | r | ä¸é©ç”¨ | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `appname.app/` | applicationDirectory | æŸ | r | ä¸é©ç”¨ | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|       `www/` | - | - | r | ä¸é©ç”¨ | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `Documents/` | documentsDirectory | 檔 | r/w | 是啊 | ç„¡ | 是啊 | 是啊 | +|       `NoCloud/` | - | 檔 nosync | r/w | 是啊 | ç„¡ | ç„¡ | 是啊 | +|    `Library` | - | 圖書館 | r/w | 是啊 | ç„¡ | 是嗎? | 是啊 | +|       `NoCloud/` | dataDirectory | 圖書館 nosync | r/w | 是啊 | ç„¡ | ç„¡ | 是啊 | +|       `Cloud/` | syncedDataDirectory | - | r/w | 是啊 | ç„¡ | 是啊 | 是啊 | +|       `Caches/` | cacheDirectory | å¿«å–記憶體 | r/w | 是啊 * | 是的 * * *| | ç„¡ | 是啊 | +|    `tmp/` | tempDirectory | - | r/w | 沒有 * * | 是的 * * *| | ç„¡ | 是啊 | + +* 檔堅æŒè·¨æ‡‰ç”¨ç¨‹å¼é‡æ–°é–‹æ©Ÿå’Œå‡ç´šï¼Œä½†æ˜¯æ¯ç•¶ä½œæ¥­ç³»çµ±çš„æ¬²æœ›ï¼Œå¯ä»¥æ¸…é™¤æ­¤ç›®éŒ„ã€‚æ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ‡‰è©²èƒ½å¤ é‡æ–°å‰µå»ºä»»ä½•內容å¯èƒ½æœƒè¢«åˆªé™¤ã€‚ + +* * 檔å¯èƒ½æœƒæŒçºŒæ•´å€‹æ‡‰ç”¨ç¨‹å¼é‡æ–°é–‹æ©Ÿï¼Œä½†ä¸è¦ä¾è³´æ­¤è¡Œç‚ºã€‚ ä¸ä¿è­‰æª”在更新之間æŒçºŒå­˜åœ¨ã€‚ æ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ™‚é©ç”¨ï¼Œæ˜¯æ‡‰è©²åˆªé™¤æ­¤ç›®éŒ„的檔,因為作業系統並ä¸èƒ½ä¿è­‰ä½•時 (或å³ä½¿ï¼‰ 中刪除這些檔。 + +* * *| 作業系統å¯èƒ½æœƒæ¸…除此目錄的內容,æ¯ç•¶å®ƒæ„Ÿè¦ºå®ƒæ˜¯å¿…è¦çš„但ä¸è¦ä¾è³´äºŽæ­¤ã€‚ 你應該清除此目錄為é©åˆæ‚¨çš„æ‡‰ç”¨ç¨‹å¼ã€‚ + +### Android 的檔案系統佈局 + +| 設備路徑 | `cordova.file.*` | `AndroidExtraFileSystems` | r/w 嗎? | æŒçºŒæ€§å—Žï¼Ÿ | OS 清除 | ç§äºº | +|:--------------------------------- |:----------------------------------- |:------------------------- |:------:|:-----:|:------:|:--:| +| `file:///android_asset/` | applicationDirectory | | r | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +| `/ 資料資料/< æ‡‰ç”¨ç¨‹å¼ id > /` | applicationStorageDirectory | - | r/w | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `cache` | cacheDirectory | å¿«å–記憶體 | r/w | 是啊 | 是啊 * | 是啊 | +|    `files` | dataDirectory | 檔 | r/w | 是啊 | ç„¡ | 是啊 | +|       `Documents` | | 檔 | r/w | 是啊 | ç„¡ | 是啊 | +| `< sd å¡ > /` | externalRootDirectory | sd å¡ | r/w | 是啊 | ç„¡ | ç„¡ | +|    `Android/data//` | externalApplicationStorageDirectory | - | r/w | 是啊 | ç„¡ | ç„¡ | +|       `cache` | externalCacheDirectry | 外部快å–記憶體 | r/w | 是啊 | 沒有 * * | ç„¡ | +|       `files` | externalDataDirectory | 外部檔 | r/w | 是啊 | ç„¡ | ç„¡ | + +* 的作業系統å¯èƒ½æœƒå®šæœŸæ¸…é™¤æ­¤ç›®éŒ„ä¸­ï¼Œä½†ä¸æ˜¯ä¾è³´äºŽé€™ç¨®è¡Œç‚ºã€‚ 清除此為é©åˆæ‚¨çš„æ‡‰ç”¨ç¨‹å¼çš„目錄的內容。 使用者應手動清除緩存,將刪除此目錄的內容。 + +* * ä½œæ¥­ç³»çµ±ä¸æœƒè‡ªå‹•清除此目錄你是負責管ç†è‡ªå·±çš„內容。 使用者應手動清除緩存,目錄中的內容將被刪除。 + +**注**: 如果外部存儲無法è£å…¥ï¼Œ`cordova.file.external*` 屬性為 `空`. + +### 黑莓 10 檔案系統佈局 + +| 設備路徑 | `cordova.file.*` | r/w 嗎? | æŒçºŒæ€§å—Žï¼Ÿ | OS 清除 | ç§äºº | +|:---------------------------------------------------- |:--------------------------- |:------:|:-----:|:-----:|:--:| +| `file:///accounts/1000/appdata/ < æ‡‰ç”¨ç¨‹å¼ id > /` | applicationStorageDirectory | r | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `app/native` | applicationDirectory | r | ä¸é©ç”¨ | ä¸é©ç”¨ | 是啊 | +|    `data/webviews/webfs/temporary/local__0` | cacheDirectory | r/w | ç„¡ | 是啊 | 是啊 | +|    `data/webviews/webfs/persistent/local__0` | dataDirectory | r/w | 是啊 | ç„¡ | 是啊 | +| `file:///accounts/1000/removable/sdcard` | externalRemovableDirectory | r/w | 是啊 | ç„¡ | ç„¡ | +| `file:///accounts/1000/shared` | sharedDirectory | r/w | 是啊 | ç„¡ | ç„¡ | + +*注æ„*: 當應用程å¼éƒ¨ç½²å·¥ä½œé€±é‚Šæ™‚ï¼Œæ‰€æœ‰è·¯å¾‘éƒ½æ˜¯ç›¸å°æ–¼ /accounts/1000-enterprise。 + +## Android 的怪癖 + +### Android çš„æŒä¹…性存儲ä½ç½® + +有很多有效的ä½ç½®ä¾†å­˜å„²æŒä¹…性檔在 Android 設備上。 è«‹åƒé–± [æ­¤é é¢][7] çš„å„種å¯èƒ½æ€§é€²è¡Œå»£æ³›è¨Žè«–。 + + [7]: http://developer.android.com/guide/topics/data/data-storage.html + +以å‰ç‰ˆæœ¬çš„å¤–æŽ›ç¨‹å¼æœƒé¸æ“‡åœ¨å•Ÿå‹•時,基於該設備是å¦è²ç¨± SD å¡ ï¼ˆæˆ–ç­‰æ•ˆå­˜å„²åˆ†å€ï¼‰ 展開,臨時和永久檔的ä½ç½®ã€‚ 如果被掛載 SD å¡ï¼Œæˆ–一個大的內部存儲分å€å¯ç”¨ (如 nexus 系列設備上) 然後æŒä¹…性檔將存儲在該空間的根目錄中。 這æ„味著所有的科爾多瓦應用程å¼å¯ä»¥çœ‹åˆ°æ‰€æœ‰å¯ç”¨çš„æª”在å¡ç‰‡ä¸Šã€‚ + +如果 SD å¡ä¸æ˜¯å¯ç”¨çš„那麼以å‰çš„版本中將存儲資料下的 `/data/data/`,其中隔離應用程å¼å¾žå½¼æ­¤ï¼Œä½†ä»å¯èƒ½å°Žè‡´ä½¿ç”¨è€…之間共用的資料。 + +ç¾åœ¨å¯ä»¥é¸æ“‡æ˜¯å¦å°‡æª”存儲在內部檔的存儲ä½ç½®ï¼Œæˆ–使用以å‰çš„é‚輯,與您的應用程å¼çš„ `config.xml` 檔中的å好。 è¦åŸ·è¡Œæ­¤æ“作,請將以下兩行之一添加到 `config.xml`: + + + + + + +如果這æ¢ç·šï¼Œæ²’有檔外掛程å¼å°‡ä½¿ç”¨ `Compatibility` 作為é è¨­å€¼ã€‚如果首é¸é …æ¨™è¨˜å­˜åœ¨ï¼Œä¸¦ä¸æ˜¯é€™äº›å€¼ä¹‹ä¸€ï¼Œæ‡‰ç”¨ç¨‹å¼å°‡ç„¡æ³•啟動。 + +如果您的應用程å¼ä»¥å‰å·²è¢«é‹åˆ°ä½¿ç”¨è€…,使用較舊的 ï¼ˆé  1.0) 版本的這個外掛程å¼ï¼Œä¸¦å…·æœ‰æŒä¹…性的檔,系統中存儲的檔,然後您應該設置 `Compatibility` å好。 切æ›åˆ°"Internal"çš„ä½ç½®ï¼Œå°‡æ„å‘³è‘—ç¾æœ‰ä½¿ç”¨è€…å‡ç´šä»–們的應用程å¼å¯èƒ½ç„¡æ³•訪å•他們以å‰å­˜å„²çš„æª”ï¼Œå…·é«”å–æ±ºæ–¼ä»–們的設備。 + +å¦‚æžœæ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ˜¯æ–°çš„æˆ–以å‰å¾žæœªæœ‰æŒä¹…性的檔案系統中存儲檔,那麼通常建議使用 `Internal` 設置。 + +## iOS 的怪癖 + +* `cordova.file.applicationStorageDirectory`是唯讀的 ;試圖存儲內的根目錄中的檔將會失敗。 使用的å¦ä¸€å€‹ `cordova.file.*` 為 iOS 定義的屬性 ï¼ˆåªæœ‰ `applicationDirectory` å’Œ `applicationStorageDirectory` 都是唯讀)。 +* `FileReader.readAsText(blob, encoding)` + * `encoding`åƒæ•¸ä¸å—支æ´ï¼Œè€Œ utf-8 編碼總是效果。 + +### iOS çš„æŒä¹…性存儲ä½ç½® + +有兩個有效的ä½ç½®ä¾†å­˜å„²æŒä¹…性在 iOS 設備上的檔: 檔目錄和圖書館目錄。 以å‰ç‰ˆæœ¬çš„å¤–æŽ›ç¨‹å¼æ°¸é åªèƒ½å°‡æŒä¹…性檔存儲在文檔目錄中。 é€™å·²ç¶“ä½¿æ‰€æœ‰æ‡‰ç”¨ç¨‹å¼æª”å¯è¦‹åœ¨ iTunes,往往是無æ„ç‚ºä¹‹ï¼Œå°¤å…¶æ˜¯å°æ–¼è™•ç†å¤§é‡å°æª”的應用程å¼ä¸­ï¼Œè€Œä¸æ˜¯ç”Ÿç”¢ä¾›å‡ºå£ï¼Œè©²ç›®éŒ„的既定的目標是證件齊全的副作用。 + +ç¾åœ¨å¯ä»¥é¸æ“‡æ˜¯å¦å°‡æª”存儲在檔或庫目錄,與您的應用程å¼çš„ `config.xml` 檔中的å好。 è¦åŸ·è¡Œæ­¤æ“作,請將以下兩行之一添加到 `config.xml`: + + + + + + +如果這æ¢ç·šï¼Œæ²’有檔外掛程å¼å°‡ä½¿ç”¨ `Compatibility` 作為é è¨­å€¼ã€‚如果首é¸é …æ¨™è¨˜å­˜åœ¨ï¼Œä¸¦ä¸æ˜¯é€™äº›å€¼ä¹‹ä¸€ï¼Œæ‡‰ç”¨ç¨‹å¼å°‡ç„¡æ³•啟動。 + +如果您的應用程å¼ä»¥å‰å·²è¢«é‹åˆ°ä½¿ç”¨è€…,使用較舊的 ï¼ˆé  1.0) 版本的這個外掛程å¼ï¼Œä¸¦å…·æœ‰æŒä¹…性的檔,系統中存儲的檔,然後您應該設置 `Compatibility` å好。 切æ›åˆ° `Library` çš„ä½ç½®ï¼Œå°‡æ„å‘³è‘—ç¾æœ‰ä½¿ç”¨è€…å‡ç´šä»–們的應用程å¼å°‡ç„¡æ³•訪å•他們以å‰å­˜å„²çš„æª”。 + +å¦‚æžœæ‚¨çš„æ‡‰ç”¨ç¨‹å¼æ˜¯æ–°çš„æˆ–以å‰å¾žæœªæœ‰æŒä¹…性的檔案系統中存儲檔,那麼通常建議使用 `Internal` 設置。 + +## ç«ç‹ç€è¦½å™¨ä½œæ¥­ç³»çµ±çš„æ€ªç™– + +檔案系統 API æœ¬èº«ä¸æ”¯æ´ç«ç‹ç€è¦½å™¨çš„作業系統,作為墊片在 indexedDB 上實ç¾çš„。 + +* 䏿œƒå¤±æ•—時刪除éžç©ºçš„目錄 +* 䏿”¯æ´ä¸­ç¹¼è³‡æ–™çš„目錄 +* 方法 `copyTo` å’Œ `moveTo` 䏿”¯æ´ç›®éŒ„ + +支æ´ä»¥ä¸‹è³‡æ–™è·¯å¾‘: * `applicationDirectory`-使用 `xhr` ç²å–èˆ‡æ‡‰ç”¨ç¨‹å¼æ‰“包的本地檔。 `dataDirectory`-用於æŒä¹…性的特定于應用程å¼çš„資料檔案。 `cacheDirectory`-ç”Ÿå­˜æ‡‰é‡æ–°é–‹æ©Ÿæ‡‰ç”¨ç¨‹å¼çš„å¿«å–æª”案 (應用程å¼ä¸æ‡‰ä¾è³´ä½œæ¥­ç³»çµ±ä¾†åˆªé™¤æª”在這裡)。 + +## ç€è¦½å™¨çš„æ€ªç™– + +### 常見的怪癖和備註 + +* æ¯å€‹ç€è¦½å™¨ä½¿ç”¨å…¶è‡ªå·±çš„æ²™ç®±æª”案系統。IE å’Œç«ç‹ç€è¦½å™¨ä½¿ç”¨ IndexedDB 作為一個基地。所有ç€è¦½å™¨éƒ½ä½¿ç”¨æ­£æ–œæ ä½œç‚ºè·¯å¾‘中的目錄分隔符號。 +* 目錄æ¢ç›®ä¸å¾—ä¸å…ˆå¾Œå‰µå»ºã€‚ 例如,調用 `fs.root.getDirectory (' dir1/dir2 ',{create:true},successCallback,errorCallback)`,如果ä¸å­˜åœ¨ dir1 將失敗。 +* 外掛程å¼å°‡è«‹æ±‚ä½¿ç”¨è€…è¨±å¯æ¬Šï¼Œä»¥ä¾¿åœ¨æ‡‰ç”¨ç¨‹å¼åˆæ¬¡é–‹æ©Ÿä½¿ç”¨æŒä¹…性存儲。 +* å¤–æŽ›ç¨‹å¼æ”¯æ´ `cdvfile://localhost` (本地資æºï¼‰ åªã€‚é€šéŽ `cdvfile` 䏿”¯æ´å¤–部資æºå³. +* 該外掛程å¼ä¸éµå¾ª ["檔案系統 API 8.3 命åé™åˆ¶"][8]. +* Blob 和檔 ' `close` 功能ä¸å—支æ´ã€‚ +* `FileSaver` å’Œ `BlobBuilder` 䏿”¯æ´é€™å€‹å¤–掛程å¼ï¼Œæ²’有存根 (stub)。 +* 該外掛程å¼ä¸æ”¯æ´ `requestAllFileSystems`。這個功能也是缺少è¦ç¯„中。 +* 在目錄中的æ¢ç›®å°‡ä¸æœƒè¢«åˆªé™¤ï¼Œå¦‚果您使用 `create: true` æ¨™èªŒç‚ºç¾æœ‰ç›®éŒ„。 +* 䏿”¯æ´é€šéŽå»ºæ§‹å‡½å¼å‰µå»ºçš„æª”。你應該使用 entry.file 方法。 +* æ¯å€‹ç€è¦½å™¨ä½¿ç”¨å®ƒè‡ªå·±çš„å½¢å¼ç‚º blob çš„ URL 引用。 +* æ”¯æ´ `readAsDataURL` 功能,但在 Chrome ä¸­çš„åª’é«”é¡žåž‹å–æ±ºæ–¼è¼¸å…¥å‰¯æª”å,在 IE 中的媒體類型都始終空著 (這是 `純文字` 按照說明書一樣),在 Firefox 中的媒體類型始終是 `應用程å¼/å…«ä½ä½å…ƒçµ„æµ`。 例如,如果內容是 `abcdefg` 然後ç«ç‹ç€è¦½å™¨è¿”回 `資料: æ‡‰ç”¨ç¨‹å¼ / å…«ä½ä½å…ƒçµ„æµ ï¼› base64,YWJjZGVmZw = =`,å³è¿”回 `資料: ï¼› base64,YWJjZGVmZw = =`,鉻返回 `資料: < 媒體類型根據擴展æ¢ç›®å稱 > ï¼› base64,YWJjZGVmZw = =`. +* 在表單 `file:///persistent/path/to/entry` ç«ç‹ç€è¦½å™¨ IE),`toInternalURL` 返回的路徑。 鉻在表單 `cdvfile://localhost/persistent/file` 返回的路徑. + + [8]: http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions + +### 鉻的怪癖 + +* 設備準備好事件之後,chrome 檔案系統並ä¸èƒ½ç«‹å³æº–備。作為一種變通方法,您å¯ä»¥è¨‚閱到 `filePluginIsReady` 事件。示例: + + javascript + window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); + + +ä½ å¯ä»¥ä½¿ç”¨ `window.isFilePluginReadyRaised` 函數來檢查是å¦å·²ç¶“引發了事件。 -window.requestFileSystem 臨時和永久性檔案系統é…é¡ä¸¦ä¸å±€é™äºŽé‰»ã€‚ 為增加中鉻的æŒä¹…性存儲,您需è¦èª¿ç”¨ `window.initPersistentFileSystem` 方法。 é è¨­æƒ…æ³ä¸‹ï¼ŒæŒä¹…性存儲é…é¡ç‚º 5 MB。 é‰»éœ€è¦ `— — å…許--訪å•-從-檔` é€šéŽ `file:///` å”定é‹è¡Œåƒæ•¸å° API 的支æ´ã€‚ -å¦‚æžœæ‚¨ä½¿ç”¨æ¨™èªŒï¼Œå°‡ä¸æ›´æ”¹ `檔` 物件 `{create:true}` ç¾æœ‰ `æ¢ç›®` 的時候。 -事件 `å¯å–消` 屬性設置為 true 在 Chrome 中。 這是é•å了 [è¦ç¯„][4]。 -中鉻的 `toURL` 函數返回 `檔案系統:`-é¦–ç¢¼è·¯å¾‘å…·é«”å–æ±ºæ–¼æ‡‰ç”¨ç¨‹å¼ä¸»æ©Ÿã€‚ 例如,`filesystem:file:///persistent/somefile.txt`,`filesystem:HTTP://localhost:8080/persistent/somefile.txt`。 -`toURL` å‡½æ•¸çµæžœä¸åŒ…å«å°¾éƒ¨å斜線在目錄æ¢ç›®çš„æƒ…æ³ä¸‹ã€‚ 鉻雖然正確解æžç›®éŒ„帶斜æ è½å¾Œçš„ url。 -`resolveLocalFileSystemURL` 方法需è¦å…¥ç«™çš„ `url` 必須具有 `檔案系統` 首碼。 例如,`resolveLocalFileSystemURL` çš„ `url` åƒæ•¸æ‡‰åœ¨è¡¨å–® `filesystem:file:///persistent/somefile.txt` è€Œä¸æ˜¯è¡¨å–® `file:///persistent/somefile.txt` 在安å“系統。 -ä¸æŽ¨è–¦ä½¿ç”¨ `toNativeURL` 函數ä¸å—支æ´ï¼Œä¸¦ä¸”沒有存根 (stub)。 -`setMetadata` åŠŸèƒ½æ˜¯æ²’æœ‰èªªå‡ºçš„è¦æ ¼ï¼Œä¸¦ä¸”䏿”¯æ´ã€‚ -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 SYNTAX_ERR(code: 8) 上請求一個ä¸å­˜åœ¨çš„æª”案系統。 -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 PATH_EXISTS_ERR(code: 12) 上嘗試專門創建一個檔或目錄,它已經存在。 -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 NO_MODIFICATION_ALLOWED_ERR(code: 6) 在試圖調用 removeRecursively 的根檔案系統上。 -INVALID_MODIFICATION_ERR (代碼: 9) è€Œä¸æ˜¯å¼•發 NOT_FOUND_ERR(code: 1) 試到 moveTo 目錄ä¸å­˜åœ¨ã€‚ + +### 基於 IndexedDB çš„ impl 怪癖 (Firefox å’Œ IE) + +* `.` å’Œ `.` ä¸å—支æ´ã€‚ +* IE 䏿”¯æ´ `file:///`-æ¨¡å¼ ï¼›åªæœ‰è¨—ç®¡çš„æ¨¡å¼æ˜¯æ”¯æ´ (HTTP://localhost:xxxx)。 +* ç«ç‹ç€è¦½å™¨çš„æª”案系統大å°ä¸æ˜¯æœ‰é™ï¼Œä½†æ¯å€‹ 50 MB æ“´å±•æœƒè¦æ±‚ä½¿ç”¨è€…çš„è¨±å¯æ¬Šã€‚ IE10 å…è¨±é” 10 mb çš„ AppCache å’Œ IndexedDB 檔案系統的實ç¾ä¸­ä½¿ç”¨è€Œä¸æœƒæç¤ºï¼Œä¸€æ—¦ä½ é”åˆ°é€™ä¸€æ°´æº–ä½ æœƒè©¢å•æ‚¨æ˜¯å¦å…許它增加到æ¯å€‹ç¶²ç«™çš„ 250 mb 的最大åˆä½µã€‚ 所以 `requestFileSystem` 函數的 `大å°` åƒæ•¸ä¸¦ä¸å½±éŸ¿æª”案系統相容 Firefox å’Œ IE。 +* `readAsBinaryString` 函數在è¦ç¯„ä¸­æ²’æœ‰æ³¨æ˜Žä¸æ”¯æ´åœ¨ IE 中和沒有存根 (stub)。 +* `file.type` 始終為 null。 +* æ‚¨ä¸æ‡‰å‰µå»ºæ¢ç›®ä½¿ç”¨å·²åˆªé™¤çš„ç›®éŒ„å¯¦ä¾‹å›žæª”çµæžœã€‚å¦å‰‡ï¼Œä½ æœƒå¾—到一個 '掛æ¢ç›®'。 +* 您å¯ä»¥è®€å–ä¸€å€‹æª”ï¼Œå…¶ä¸­åªæ˜¯å¯«ä¹‹å‰ä½ éœ€è¦ç²å¾—的此檔的新實例。 +* `setMetadata` 函數,在è¦ç¯„ä¸­æ²’æœ‰æ³¨æ˜Žæ”¯æ´ `modificationTime` 欄ä½çš„æ›´æ”¹ã€‚ +* `copyTo` å’Œ `moveTo` å‡½æ•¸ä¸æ”¯æ´ç›®éŒ„。 +* 䏿”¯æ´ç›®éŒ„的中繼資料。 +* 兩個 Entry.remove å’Œ directoryEntry.removeRecursively ä¸å¤±æ•—時刪除éžç©ºçš„目錄-相å與內容一起清ç†ç›®éŒ„被刪除。 +* 䏿”¯æ´ `abort` å’Œ `truncate` 函數。 +* é€²åº¦äº‹ä»¶ä¸æœƒè§¸ç™¼ã€‚例如,將ä¸åŸ·è¡Œæ­¤è™•ç†ç¨‹å¼ï¼š + + javascript + writer.onprogress = function() { /*commands*/ }; + + +## å‡ç´šç­†è¨˜ + +åœ¨é€™å€‹å¤–æŽ›ç¨‹å¼ v1.0.0,`FileEntry` å’Œ `DirectoryEntry` çš„çµæ§‹å·²ç¶“改變,以更加一致的已發表說明書。 + +ä»¥å‰ (pre-1.0.0) 版本的外掛程å¼ä¸­ `輸入` 物件的 `完整路徑` 屬性存放è£ç½®å›ºå®šæª”案ä½ç½®ã€‚é€™äº›è·¯å¾‘é€šå¸¸æœƒçœ‹èµ·ä¾†åƒ + + /var/mobile/Applications//Documents/path/to/file (iOS) + /storage/emulated/0/path/to/file (Android) + + +這些路徑還返回的 `Entry` 物件的 `toURL()` 方法。 + +與 v1.0.0,`完整路徑` 屬性是到檔中,*ç›¸å°æ–¼ HTML 檔案系統的根目錄* 的路徑。 因此,上述路徑會ç¾åœ¨éƒ½ç”±ä¸€å€‹ `FileEntry` 物件的 `完整路徑`, + + /path/to/file + + +如果您的應用程å¼èˆ‡è¨­å‚™-絕å°è·¯å¾‘ï¼Œä¸¦ä¸”æ‚¨ä»¥å‰æª¢ç´¢åˆ°é€™äº›è·¯å¾‘é€šéŽ `æ¢ç›®` 物件的 `完整路徑` 屬性,然後您應該更新您的代碼以改用 `entry.toURL()`。 + +為å‘後相容性,`resolveLocalFileSystemURL()` 方法將接å—一個設備-絕å°è·¯å¾‘,並將返回相應的 `æ¢ç›®` 物件,åªè¦åœ¨ `臨時` 或 `永久性` 的檔案系統內的檔是å¦å­˜åœ¨ã€‚ + +這尤其是一直與檔案傳輸外掛程å¼ï¼Œä»¥å‰ä½¿ç”¨è¨­å‚™çµ•å°è·¯å¾‘çš„å•題 (å’Œä»ç„¶å¯ä»¥æŽ¥å—他們)。 已更新它能夠正常é‹è¡Œèˆ‡æª”案系統的 Url,所以用 `entry.toURL()` æ›¿æ› `entry.fullPath` 應解決任何å•題,得到該外掛程å¼ä¾†è™•ç†è¨­å‚™ä¸Šçš„æª”。 + +在 v1.1.0 `toURL()` 的傳回值被更改 (見 [CB-6394] (HTTPs://issues.apache.org/jira/browse/CB-6394)) ç‚ºè¿”å›žçµ•å° file:// URL。 åªè¦æœ‰å¯èƒ½ã€‚ ç¢ºä¿ 'cdvfile:' — — ä½ ç¾åœ¨å¯ä»¥ç”¨ `toInternalURL()` çš„ URL。 ç¾åœ¨ï¼Œæ­¤æ–¹æ³•將返回檔案系統表單的 Url + + cdvfile://localhost/persistent/path/to/file + + +它å¯ä»¥ç”¨æ–¼å”¯ä¸€åœ°æ¨™è­˜è©²æª”。 + +## 錯誤代碼åŠå…¶å«ç¾©çš„æ¸…單中 + +當拋出一個錯誤時,將使用以下代碼之一。 + +| 代碼 | æ’ | +| --:|:----------------------------- | +| 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` | + +## é…ç½®å¤–æŽ›ç¨‹å¼ ï¼ˆå¯é¸ï¼‰ + +å¯ç”¨çš„æª”案系統的一整套å¯ä»¥é…ç½®æ¯å€‹å¹³è‡ºã€‚IOS 和安å“系統èªè­˜åˆ° 在 `config.xml` å稱è¦å®‰è£çš„æª”案系統中的標記。é è¨­æƒ…æ³ä¸‹ï¼Œå•Ÿç”¨æ‰€æœ‰æª”案系統的根。 + + + + + +### Android 系統 + +* `files`: 該應用程å¼çš„內部檔存儲目錄 +* `files-external`: 應用程å¼çš„外部檔存儲目錄 +* `sdcard`: å…¨çƒå¤–部檔存儲目錄 (如果安è£äº†ä¸€å€‹ï¼Œé€™æ˜¯ SD å¡çš„æ ¹ç›®éŒ„)。 你必須具有 `android.permission.WRITE_EXTERNAL_STORAGE` è¨±å¯æ¬Šï¼Œç”¨é€™å€‹ã€‚ +* `cache`: 應用程å¼çš„內部緩存目錄 +* `cache-external`: 應用程å¼çš„外部快å–記憶體目錄 +* `root`: 整個設備的檔案系統 + +安å“系統還支æ´ç‰¹æ®Šçš„æª”命å為"檔",表示"/ 檔 /""檔"的檔案系統中的å­ç›®éŒ„。 + +### iOS + +* `library`: 應用程å¼çš„庫目錄 +* `documents`: 應用程å¼çš„æª”目錄 +* `cache`: 應用程å¼çš„緩存目錄 +* `bundle`: 應用程å¼çš„包 ï¼›æ‡‰ç”¨ç¨‹å¼æœ¬èº« (唯讀) çš„ç£ç‰‡ä¸Šçš„ä½ç½® +* `root`: 整個設備的檔案系統 + +é è¨­æƒ…æ³ä¸‹ï¼Œåœ–書館和檔目錄å¯ä»¥åŒæ­¥åˆ° iCloud。 您也å¯ä»¥è¦æ±‚兩個é¡å¤–的檔案系統〠`library-nosync` å’Œ `documents-nosync`,代表一個特殊的éžåŒæ­¥ç›®éŒ„å…§ `/Library` 或 `/Documents` 的檔案系統。 diff --git a/plugins/cordova-plugin-file/doc/zh/plugins.md b/plugins/cordova-plugin-file/doc/zh/plugins.md new file mode 100644 index 0000000..c9a2d61 --- /dev/null +++ b/plugins/cordova-plugin-file/doc/zh/plugins.md @@ -0,0 +1,101 @@ + + +# 外掛程å¼é–‹ç™¼äººå“¡æ³¨æ„事項 + +這些筆記主è¦ç”¨æ–¼ Android å’Œ iOS 開發者想è¦ç·¨å¯«å¤–掛程å¼çš„介é¢èˆ‡ä½¿ç”¨æª”外掛程å¼çš„æª”案系統。 + +## 工作與科爾多瓦檔案系統 Url + +自從版本 1.0.0,這個外掛程å¼ä¸€ç›´ä½¿ç”¨ Url 與 `cdvfile` å¤§æ©‹ï¼Œè¨ˆç•«ç‚ºæ‰€æœ‰é€šä¿¡ï¼Œè€Œä¸æ˜¯æ­éœ² JavaScript 的原始設備檔案系統路徑。 + +在 JavaScript æ–¹é¢ï¼Œé€™æ„味著 FileEntry å’Œ DirectoryEntry çš„ç‰©ä»¶æœ‰ä¸€å€‹å®Œæ•´è·¯å¾‘å±¬æ€§æ˜¯ç›¸å°æ–¼ HTML 檔案系統的根目錄。 如果你的外掛程å¼çš„ JavaScript API 接å—一個 FileEntry 或 DirectoryEntry 的物件,你應該打電話給 `.toURL()` å°è©²ç‰©ä»¶ä¹‹å‰å°‡å®ƒå¾žæ©‹ä¸Šå‚³éžçµ¦æœ¬æ©Ÿä»£ç¢¼ã€‚ + +### è½‰æ› cdvfile: / / fileystem 路徑的 Url + +需è¦å¯«å…¥åˆ°æª”案系統的外掛程å¼å¯èƒ½æƒ³è¦å°‡æŽ¥æ”¶çš„æª”案系統 URL 轉æ›ç‚ºå¯¦éš›çš„æª”案系統ä½ç½®ã€‚有åšé€™ï¼Œæ ¹æ“šæœ¬æ©Ÿå¹³è‡ºçš„å¤šç¨®æ–¹å¼ã€‚ + +很é‡è¦çš„æ˜¯è¦è¨˜ä½ï¼Œä¸¦ä¸æ˜¯æ‰€æœ‰ `cdvfile://` Url æ˜¯å¯æ˜ å°„到設備上的實際檔。 æŸäº› Url å¯ä»¥æŒ‡åœ¨è¨­å‚™ä¸Šæ²’有代表的檔,或甚至å¯ä»¥å¼•用é ç«¯è³‡æºçš„資產。 由於這些å¯èƒ½æ€§ï¼Œå¤–æŽ›ç¨‹å¼æ‡‰å§‹çµ‚測試是å¦å›žä¾†æ™‚試圖將 Url è½‰æ›æˆè·¯å¾‘得到有æ„ç¾©çš„çµæžœã€‚ + +#### 安å“系統 + +在 android ç³»çµ±ï¼Œæœ€ç°¡å–®çš„æ–¹æ³•ä¾†è½‰æ› `cdvfile://` 檔案系統路徑的 URL 是使用 `org.apache.cordova.CordovaResourceApi` 。 `CordovaResourceApi`有幾種方法,å¯è™•ç† `cdvfile://` ç¶²å€: + + web 視圖是æˆå“¡çš„外掛程å¼é¡ž CordovaResourceApi resourceApi = webView.getResourceApi(); + + ç²å–表示此檔在設備上,file:/// URL / / 或 URL 相åŒè®Šçš„如果它ä¸èƒ½æ˜ å°„到檔 Uri fileURL = resourceApi.remapUri(Uri.parse(cdvfileURL)); + + +它也是å¯ä»¥ç›´æŽ¥ä½¿ç”¨æª”外掛程å¼: + + å°Žå…¥ org.apache.cordova.file.FileUtils; + å°Žå…¥ org.apache.cordova.file.FileSystem; + å°Žå…¥ java.net.MalformedURLException; + + 得到檔外掛程å¼å¤–掛程å¼ç®¡ç†å™¨å¾ž FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File"); + + 給定 URL,得到的路徑,因為它嘗試 {字串路徑 = filePlugin.filesystemPathForURL(cdvfileURL);} 趕上 (MalformedURLException e) {/ / 檔案系統 url 䏿‰¿èª} + + +è¦è½‰æ›åˆ°çš„路徑從 `cdvfile://` URL: + + å°Žå…¥ org.apache.cordova.file.LocalFilesystemURL; + + ç²å–設備的路徑,一個 LocalFilesystemURL 物件 / / 或如果它ä¸èƒ½è¡¨ç¤ºç‚º cdvfile çš„ URL,則為 null。 + LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path); + 得到的字串表示形å¼çš„ URL 物件字串 cdvfileURL = url.toString(); + + +如果你的外掛程å¼å‰µå»ºä¸€å€‹æª”,並且您想è¦ç‚ºå®ƒè¿”回一個 FileEntry 物件,使用該檔的外掛程å¼: + + 返回一個 JSON çµæ§‹é©åˆæ–¼å›žåˆ° JavaScript,/ / æˆ–å¦‚æžœæ­¤æª”ä¸æ˜¯å¯è¡¨ç¤ºç‚º cdvfile çš„ URL,則為 null。 + JSONObject æ¢ç›® = filePlugin.getEntryForFile(file); + + +#### iOS + +科爾多瓦在 iOS 上的ä¸ä½¿ç”¨ç›¸åŒ `CordovaResourceApi` 作為 android 系統的概念。在 iOSï¼Œæ‡‰ä½¿ç”¨æª”å¤–æŽ›ç¨‹å¼ Url 和檔案系統路徑之間進行轉æ›ã€‚ + + ç²å–一個物件,CDVFilesystem URL 從 url 字串 CDVFilesystemURL * = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL]; + ç²å–路徑 URL 物件,或為零,如果它ä¸èƒ½æ˜ å°„到檔 NSString * 路徑 = [filePlugin filesystemPathForURL:url]; + + + CDVFilesystem URL 物件ç²å–設備的路徑,或 / / 為零,如果它ä¸èƒ½è¡¨ç¤ºç‚º cdvfile çš„ URL。 + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + 得到的字串表示形å¼çš„ URL 物件 NSString * cdvfileURL = [url absoluteString]; + + +如果你的外掛程å¼å‰µå»ºä¸€å€‹æª”,並且您想è¦ç‚ºå®ƒè¿”回一個 FileEntry 物件,使用該檔的外掛程å¼: + + CDVFilesystem URL 物件ç²å–設備的路徑,或 / / 為零,如果它ä¸èƒ½è¡¨ç¤ºç‚º cdvfile çš„ URL。 + CDVFilesystemURL * url = [filePlugin fileSystemURLforLocalPath:path]; + å¾—åˆ°ä¸€å€‹çµæ§‹ä¾†è¿”回進入 JavaScript NSDictionary * = [filePlugin makeEntryForLocalURL:url] + + +#### JavaScript + +在 JavaScript 中,得到 `cdvfile://` URL 從 FileEntry 或 DirectoryEntry 的物件,åªéœ€èª¿ç”¨ `.toURL()` å°å®ƒ: + + var cdvfileURL = entry.toURL(); + + +在外掛程å¼å›žæ‡‰è™•ç†å¸¸å¼ï¼Œå°‡å¾žè¿”回的 FileEntry çµæ§‹è½‰æ›ç‚ºå¯¦éš›çš„æ¢ç›®ç‰©ä»¶ï¼Œè™•ç†å¸¸å¼ä»£ç¢¼æ‡‰è©²å°Žå…¥æª”外掛程å¼å’Œå‰µå»ºæ–°çš„物件: + + 創建é©ç•¶çš„æ¢ç›®ç‰©ä»¶ var æ¢ç›®; + 如果 (entryStruct.isDirectory) {æ¢ç›® = 新目錄 (entryStruct.name,entryStruct.fullPath,新 FileSystem(entryStruct.filesystemName));} å…¶ä»– {æ¢ç›® = æ–°çš„ FileEntry (entryStruct.name,entryStruct.fullPath,新 FileSystem(entryStruct.filesystemName));} \ No newline at end of file diff --git a/plugins/cordova-plugin-file/package.json b/plugins/cordova-plugin-file/package.json new file mode 100644 index 0000000..ffe1662 --- /dev/null +++ b/plugins/cordova-plugin-file/package.json @@ -0,0 +1,57 @@ +{ + "name": "cordova-plugin-file", + "version": "4.3.1", + "description": "Cordova File Plugin", + "cordova": { + "id": "cordova-plugin-file", + "platforms": [ + "android", + "amazon-fireos", + "ubuntu", + "ios", + "osx", + "wp7", + "wp8", + "blackberry10", + "windows8", + "windows", + "firefoxos" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-plugin-file" + }, + "keywords": [ + "cordova", + "file", + "ecosystem:cordova", + "cordova-android", + "cordova-amazon-fireos", + "cordova-ubuntu", + "cordova-ios", + "cordova-osx", + "cordova-wp7", + "cordova-wp8", + "cordova-blackberry10", + "cordova-windows8", + "cordova-windows", + "cordova-firefoxos" + ], + "scripts": { + "test": "npm run jshint", + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "engines": { + "cordovaDependencies": { + "5.0.0": { + "cordova": ">100" + } + } + }, + "devDependencies": { + "jshint": "^2.6.0" + } +} diff --git a/plugins/cordova-plugin-file/plugin.xml b/plugins/cordova-plugin-file/plugin.xml new file mode 100644 index 0000000..51affa2 --- /dev/null +++ b/plugins/cordova-plugin-file/plugin.xml @@ -0,0 +1,424 @@ + + + + + File + Cordova File Plugin + Apache 2.0 + cordova,file + https://git-wip-us.apache.org/repos/asf/cordova-plugin-file.git + https://issues.apache.org/jira/browse/CB/component/12320651 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +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: + + "<preference name="AndroidPersistentFileLocation" value="Compatibility" />" + +to config.xml in order for the application to find previously stored files. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-file/src/android/AssetFilesystem.java b/plugins/cordova-plugin-file/src/android/AssetFilesystem.java new file mode 100644 index 0000000..b035c40 --- /dev/null +++ b/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 listCache; + private static Map 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) ois.readObject(); + lengthCache = (Map) 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(); + } + } + } + } + + 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/plugins/cordova-plugin-file/src/android/ContentFilesystem.java b/plugins/cordova-plugin-file/src/android/ContentFilesystem.java new file mode 100644 index 0000000..6b983c0 --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/DirectoryManager.java b/plugins/cordova-plugin-file/src/android/DirectoryManager.java new file mode 100644 index 0000000..07af5ea --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/EncodingException.java b/plugins/cordova-plugin-file/src/android/EncodingException.java new file mode 100644 index 0000000..e9e1653 --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/FileExistsException.java b/plugins/cordova-plugin-file/src/android/FileExistsException.java new file mode 100644 index 0000000..5c4d83d --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/FileUtils.java b/plugins/cordova-plugin-file/src/android/FileUtils.java new file mode 100644 index 0000000..2b88d52 --- /dev/null +++ b/plugins/cordova-plugin-file/src/android/FileUtils.java @@ -0,0 +1,1224 @@ +/* + 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 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 availableFileSystems) { + HashSet installedFileSystems = new HashSet(); + + /* 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 getAvailableFileSystems(Activity activity) { + Context context = activity.getApplicationContext(); + HashMap availableFileSystems = new HashMap(); + + 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(); + 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 allowedStorageDirectories = new ArrayList(); + 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/plugins/cordova-plugin-file/src/android/Filesystem.java b/plugins/cordova-plugin-file/src/android/Filesystem.java new file mode 100644 index 0000000..c69d3bd --- /dev/null +++ b/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 components = new ArrayList(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/plugins/cordova-plugin-file/src/android/InvalidModificationException.java b/plugins/cordova-plugin-file/src/android/InvalidModificationException.java new file mode 100644 index 0000000..8f6bec5 --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/LocalFilesystem.java b/plugins/cordova-plugin-file/src/android/LocalFilesystem.java new file mode 100644 index 0000000..051f994 --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java b/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java new file mode 100644 index 0000000..b96b6ee --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java b/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java new file mode 100644 index 0000000..627eafb --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/PendingRequests.java b/plugins/cordova-plugin-file/src/android/PendingRequests.java new file mode 100644 index 0000000..23d6d73 --- /dev/null +++ b/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 requests = new SparseArray(); + + /** + * 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/plugins/cordova-plugin-file/src/android/TypeMismatchException.java b/plugins/cordova-plugin-file/src/android/TypeMismatchException.java new file mode 100644 index 0000000..1315f9a --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/android/build-extras.gradle b/plugins/cordova-plugin-file/src/android/build-extras.gradle new file mode 100644 index 0000000..a0a7844 --- /dev/null +++ b/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/plugins/cordova-plugin-file/src/blackberry10/index.js b/plugins/cordova-plugin-file/src/blackberry10/index.js new file mode 100644 index 0000000..e995986 --- /dev/null +++ b/plugins/cordova-plugin-file/src/blackberry10/index.js @@ -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. + * +*/ + +/* global PluginResult */ + +module.exports = { + setSandbox : function (success, fail, args, env) { + require("lib/webview").setSandbox(JSON.parse(decodeURIComponent(args[0]))); + new PluginResult(args, env).ok(); + }, + + getHomePath: function (success, fail, args, env) { + var homeDir = window.qnx.webplatform.getApplication().getEnv("HOME"); + new PluginResult(args, env).ok(homeDir); + }, + + requestAllPaths: function (success, fail, args, env) { + var homeDir = 'file://' + window.qnx.webplatform.getApplication().getEnv("HOME").replace('/data', ''), + paths = { + applicationDirectory: homeDir + '/app/native/', + applicationStorageDirectory: homeDir + '/', + dataDirectory: homeDir + '/data/webviews/webfs/persistent/local__0/', + cacheDirectory: homeDir + '/data/webviews/webfs/temporary/local__0/', + externalRootDirectory: 'file:///accounts/1000/removable/sdcard/', + sharedDirectory: homeDir + '/shared/' + }; + success(paths); + } +}; diff --git a/plugins/cordova-plugin-file/src/browser/FileProxy.js b/plugins/cordova-plugin-file/src/browser/FileProxy.js new file mode 100644 index 0000000..2d937f4 --- /dev/null +++ b/plugins/cordova-plugin-file/src/browser/FileProxy.js @@ -0,0 +1,984 @@ +/* + * + * 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*/ + + /* 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 + "/", + // 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'), + FileSystem = require('./FileSystem'), + FileEntry = require('./FileEntry'), + FileError = require('./FileError'), + DirectoryEntry = require('./DirectoryEntry'), + 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 + "/", + // 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]; // jshint ignore: line + + 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, '_'); + + 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], + data = args[1], + position = args[2], + isBinary = args[3]; // jshint ignore: line + + if (!data) { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + return; + } + + if (typeof data === 'string' || data instanceof String) { + data = new Blob([data]); + } + + exports.getFile(function(fileEntry) { + var blob_ = fileEntry.file_.blob_; + + if (!blob_) { + blob_ = new Blob([data], {type: data.type}); + } 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], + {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], + enc = args[1], + startPos = args[2], + endPos = args[3]; + + readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsDataURL = function(successCallback, errorCallback, args) { + var fileName = args[0], + startPos = args[1], + endPos = args[2]; + + readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsBinaryString = function(successCallback, errorCallback, args) { + var fileName = args[0], + startPos = args[1], + endPos = args[2]; + + readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsArrayBuffer = function(successCallback, errorCallback, args) { + var fileName = args[0], + startPos = args[1], + 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]; // jshint ignore: line + var name = args[2]; // jshint ignore: 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)) { + 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(); + xhr.open("GET", path, true); + xhr.onreadystatechange = function () { + if (xhr.status === 200 && xhr.readyState === 4) { + exports.requestFileSystem(function(fs) { + fs.name = location.hostname; + + //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; + successCallback(entry); + } + }; + fileWriter.onerror = function () { + if (errorCallback) { + errorCallback(FileError.NOT_READABLE_ERR); + } + }; + fileWriter.write(new Blob([xhr.response])); + }, errorCallback); + } + }; + + 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(); + + 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; + + // 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(), + 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() { + successCallback(request.result); + }; + }; + + 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 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) { + 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); + }; + + tx.objectStore(FILE_STORE_).put(entry, storagePath); + }; + + // 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/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js b/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js new file mode 100644 index 0000000..946304b --- /dev/null +++ b/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js @@ -0,0 +1,805 @@ +/* + * + * 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 IDBKeyRange */ + +var LocalFileSystem = require('./LocalFileSystem'), + FileSystem = require('./FileSystem'), + FileEntry = require('./FileEntry'), + FileError = require('./FileError'), + DirectoryEntry = require('./DirectoryEntry'), + File = require('./File'); + +/* +QUIRKS: + Does not fail when removing non-empty directories + Does not support metadata for directories + Does not support requestAllFileSystems + Does not support resolveLocalFileSystemURI + Methods copyTo and moveTo do not support directories + + Heavily based on https://github.com/ebidel/idb.filesystem.js + */ + + +(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 DIR_OPEN_BOUND = String.fromCharCode(DIR_SEPARATOR.charCodeAt(0) + 1); + + var pathsPrefix = { + // Read-only directory where the application is installed. + applicationDirectory: location.origin + "/", + // 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/', + }; + +/*** Exported functionality ***/ + + exports.requestFileSystem = function(successCallback, errorCallback, args) { + var type = args[0]; + //var size = args[1]; + + 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, '_'); + + var root = new DirectoryEntry('', DIR_SEPARATOR); + fs_ = new FileSystem(name, root); + + idb_.open(storageName, function() { + successCallback(fs_); + }, errorCallback); + }; + + 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 (!successCallback) { + throw Error('Expected successCallback argument.'); + } + + var path = resolveToFullPath_(fullPath); + + idb_.getAllEntries(path.fullPath, path.storagePath, function(entries) { + successCallback(entries); + }, errorCallback); + }; + + 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.INVALID_MODIFICATION_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; + }, errorCallback, [fullPath, null]); + }; + + exports.write = function(successCallback, errorCallback, args) { + var fileName = args[0], + data = args[1], + position = args[2]; + //isBinary = args[3]; + + if (!data) { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + return; + } + + exports.getFile(function(fileEntry) { + var blob_ = fileEntry.file_.blob_; + + if (!blob_) { + blob_ = new Blob([data], {type: data.type}); + } else { + // Calc the head and tail fragments + var head = blob_.slice(0, position); + var tail = blob_.slice(position + 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], + {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 = data.lastModifiedDate || 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.byteLength); + }, errorCallback); + }, errorCallback, [fileName, null]); + }; + + exports.readAsText = function(successCallback, errorCallback, args) { + var fileName = args[0], + enc = args[1], + startPos = args[2], + endPos = args[3]; + + readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsDataURL = function(successCallback, errorCallback, args) { + var fileName = args[0], + startPos = args[1], + endPos = args[2]; + + readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsBinaryString = function(successCallback, errorCallback, args) { + var fileName = args[0], + startPos = args[1], + endPos = args[2]; + + readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsArrayBuffer = function(successCallback, errorCallback, args) { + var fileName = args[0], + startPos = args[1], + endPos = args[2]; + + readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.removeRecursively = exports.remove = function(successCallback, errorCallback, args) { + var fullPath = args[0]; + + // 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(); + }, errorCallback); + }; + + 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.INVALID_MODIFICATION_ERR); + } + } else 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); + } else 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); + } + } + } else 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); + } + } else 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.INVALID_MODIFICATION_ERR); + } + } else { + // 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) { + var fullPath = args[0]; + + if (fullPath === DIR_SEPARATOR) { + successCallback(fs_.root); + return; + } + + var pathArr = fullPath.split(DIR_SEPARATOR); + pathArr.pop(); + var namesa = pathArr.pop(); + var path = pathArr.join(DIR_SEPARATOR); + + exports.getDirectory(successCallback, errorCallback, [path, namesa, {create: false}]); + }; + + exports.copyTo = function(successCallback, errorCallback, args) { + var srcPath = args[0]; + var parentFullPath = args[1]; + var name = args[2]; + + // Read src file + exports.getFile(function(srcFileEntry) { + + // 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}]); + + }, errorCallback, [srcPath, null]); + }; + + exports.moveTo = function(successCallback, errorCallback, args) { + var srcPath = args[0]; + //var parentFullPath = args[1]; + //var name = args[2]; + + 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)) { + path = decodeURI(path); + } + + if (path.indexOf(pathsPrefix.dataDirectory) === 0) { + path = path.substring(pathsPrefix.dataDirectory.length - 1); + + exports.requestFileSystem(function(fs) { + fs.root.getFile(path, {create: false}, successCallback, function() { + fs.root.getDirectory(path, {create: false}, successCallback, errorCallback); + }); + }, errorCallback, [LocalFileSystem.PERSISTENT]); + } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) { + path = path.substring(pathsPrefix.cacheDirectory.length - 1); + + exports.requestFileSystem(function(fs) { + fs.root.getFile(path, {create: false}, successCallback, function() { + fs.root.getDirectory(path, {create: false}, successCallback, errorCallback); + }); + }, errorCallback, [LocalFileSystem.TEMPORARY]); + } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) { + path = path.substring(pathsPrefix.applicationDirectory.length); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", path, true); + xhr.onreadystatechange = function () { + if (xhr.status === 200 && xhr.readyState === 4) { + exports.requestFileSystem(function(fs) { + fs.name = location.hostname; + 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; + successCallback(entry); + } + }; + fileWriter.onerror = function () { + if (errorCallback) { + errorCallback(FileError.NOT_READABLE_ERR); + } + }; + fileWriter.write(new Blob([xhr.response])); + }, errorCallback); + } + }; + + 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(); + + 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; + + // 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; + } + } + + // 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); + } + + return { + storagePath: prefix + fullPath, + 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(), + 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); + var range = IDBKeyRange.bound(fullPath, fullPath + DIR_OPEN_BOUND, + false, true); + var request = tx.objectStore(FILE_STORE_).get(range); + + tx.onabort = errorCallback || onError; + tx.oncomplete = function(e) { + successCallback(request.result); + }; + }; + + 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_OPEN_BOUND, false, true); + + var tx = this.db.transaction([FILE_STORE_], 'readonly'); + tx.onabort = errorCallback || onError; + tx.oncomplete = function(e) { + results = results.filter(function(val) { + var valPartsLen = val.fullPath.split(DIR_SEPARATOR).length; + var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length; + + if (fullPath === DIR_SEPARATOR && valPartsLen < fullPathPartsLen + 1) { + // Hack to filter out entries in the root folder. This is inefficient + // because reading the entires of fs.root (e.g. '/') returns ALL + // results in the database, then filters out the entries not in '/'. + return val; + } else if (fullPath !== DIR_SEPARATOR && + 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; + } + }); + + 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) { + if (!this.db) { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + return; + } + + var tx = this.db.transaction([FILE_STORE_], 'readwrite'); + tx.oncomplete = successCallback; + tx.onabort = errorCallback || onError; + + //var request = tx.objectStore(FILE_STORE_).delete(fullPath); + var range = IDBKeyRange.bound( + fullPath, fullPath + DIR_OPEN_BOUND, false, true); + tx.objectStore(FILE_STORE_)['delete'](range); + }; + + idb_.put = function(entry, storagePath, successCallback, errorCallback) { + 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(e) { + // TODO: Error is thrown if we pass the request event back instead. + successCallback(entry); + }; + + tx.objectStore(FILE_STORE_).put(entry, storagePath); + }; + + // 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); + } + +// Clean up. +// TODO: Is there a place for this? +// global.addEventListener('beforeunload', function(e) { +// idb_.db && idb_.db.close(); +// }, false); + +})(module.exports, window); + +require("cordova/exec/proxy").add("File", module.exports); diff --git a/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h b/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h new file mode 100644 index 0000000..e09e225 --- /dev/null +++ b/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 { +} + +- (id) initWithName:(NSString *)name; + +@end diff --git a/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m b/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m new file mode 100644 index 0000000..8486b7b --- /dev/null +++ b/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 +#import +#import +#import +#import + +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:// + and represents them internally as URLs of the form + cdvfile://localhost/assets-library/ + */ + +- (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 *)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/plugins/cordova-plugin-file/src/ios/CDVFile.h b/plugins/cordova-plugin-file/src/ios/CDVFile.h new file mode 100644 index 0000000..987c66b --- /dev/null +++ b/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 +#import + +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 *)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 *)filesystemForURL:(CDVFilesystemURL *)localURL; + +/* Native Registration API */ +- (void)registerFilesystem:(NSObject *)fs; +- (NSObject *)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/plugins/cordova-plugin-file/src/ios/CDVFile.m b/plugins/cordova-plugin-file/src/ios/CDVFile.m new file mode 100644 index 0000000..59e7d64 --- /dev/null +++ b/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 +#import "CDVFile.h" +#import "CDVLocalFilesystem.h" +#import "CDVAssetLibraryFilesystem.h" +#import + +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 *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 *)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 *)fileSystemByName:(NSString *)fsName +{ + if (self.fileSystems != nil) { + for (NSObject *fs in self.fileSystems) { + if ([fs.name isEqualToString:fsName]) { + return fs; + } + } + } + return nil; +} + +- (NSObject *)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 *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* 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 *fs = [self fileSystemByName:fsName]; + return [fs makeEntryForPath:fullPath isDirectory:isDir]; +} + +- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL +{ + NSObject *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 *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 *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 *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 *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 *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 *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 *srcFs = [self filesystemForURL:srcURL]; + NSObject *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 *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 *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 *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 *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 *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 *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 *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 *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 *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/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h b/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h new file mode 100644 index 0000000..a0186c8 --- /dev/null +++ b/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 { + NSString *_name; + NSString *_fsRoot; +} + +- (id) initWithName:(NSString *)name root:(NSString *)fsRoot; ++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath; + +@property (nonatomic,strong) NSString *fsRoot; + +@end diff --git a/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m b/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m new file mode 100644 index 0000000..35c8db1 --- /dev/null +++ b/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m @@ -0,0 +1,734 @@ +/* + 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 +#import +#import + +@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 *)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/plugins/cordova-plugin-file/src/osx/CDVFile.h b/plugins/cordova-plugin-file/src/osx/CDVFile.h new file mode 100644 index 0000000..13a9360 --- /dev/null +++ b/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 +#import + +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/ *)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 *)filesystemForURL:(CDVFilesystemURL *)localURL; + +/* Native Registration API */ +- (void)registerFilesystem:(NSObject *)fs; +- (NSObject *)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/) +*/ +@property (nonatomic, strong) NSString* appSupportPath; + +/** +* local path of the 'persistent' file system (~/Library/Application Support//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/) +*/ +@property (nonatomic, strong) NSString* appCachePath; + +/** + * registered file systems + */ +@property (nonatomic, strong) NSMutableArray* fileSystems; + +@property BOOL userHasAllowed; + +@end diff --git a/plugins/cordova-plugin-file/src/osx/CDVFile.m b/plugins/cordova-plugin-file/src/osx/CDVFile.m new file mode 100644 index 0000000..75a6001 --- /dev/null +++ b/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 + +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 *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 *)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 *)fileSystemByName:(NSString *)fsName +{ + if (self.fileSystems != nil) { + for (NSObject *fs in self.fileSystems) { + if ([fs.name isEqualToString:fsName]) { + return fs; + } + } + } + return nil; + +} + +- (NSObject *)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/ + self.appSupportPath = [self getSupportDirectoryFor:NSApplicationSupportDirectory pathComponents:nil]; + + // ~/Library/Application Support//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//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 *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* 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 *fs = [self fileSystemByName:fsName]; + return [fs makeEntryForPath:fullPath isDirectory:isDir]; +} + +- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL +{ + NSObject *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 *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 *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 *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 *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 *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 *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 *srcFs = [self filesystemForURL:srcURL]; + NSObject *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 *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 *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 *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 *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 *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 *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 *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 *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 *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/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h b/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h new file mode 100644 index 0000000..a0186c8 --- /dev/null +++ b/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 { + NSString *_name; + NSString *_fsRoot; +} + +- (id) initWithName:(NSString *)name root:(NSString *)fsRoot; ++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath; + +@property (nonatomic,strong) NSString *fsRoot; + +@end diff --git a/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m b/plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m new file mode 100644 index 0000000..016e399 --- /dev/null +++ b/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 + +@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 *)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/plugins/cordova-plugin-file/src/ubuntu/file.cpp b/plugins/cordova-plugin-file/src/ubuntu/file.cpp new file mode 100644 index 0000000..395ab2d --- /dev/null +++ b/plugins/cordova-plugin-file/src/ubuntu/file.cpp @@ -0,0 +1,912 @@ +/* + * 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. + */ + +#include "file.h" + +#include + +namespace { + class FileError { + public: + static const QString kEncodingErr; + static const QString kTypeMismatchErr; + static const QString kNotFoundErr; + static const QString kSecurityErr; + static const QString kAbortErr; + static const QString kNotReadableErr; + static const QString kNoModificationAllowedErr; + static const QString kInvalidStateErr; + static const QString kSyntaxErr; + static const QString kInvalidModificationErr; + static const QString kQuotaExceededErr; + static const QString kPathExistsErr; + }; + + bool checkFileName(const QString &name) { + if (name.contains(":")){ + return false; + } + return true; + } +}; + +const QString FileError::kEncodingErr("FileError.ENCODING_ERR"); +const QString FileError::kTypeMismatchErr("FileError.TYPE_MISMATCH_ERR"); +const QString FileError::kNotFoundErr("FileError.NOT_FOUND_ERR"); +const QString FileError::kSecurityErr("FileError.SECURITY_ERR"); +const QString FileError::kAbortErr("FileError.ABORT_ERR"); +const QString FileError::kNotReadableErr("FileError.NOT_READABLE_ERR"); +const QString FileError::kNoModificationAllowedErr("FileError.NO_MODIFICATION_ALLOWED_ERR"); +const QString FileError::kInvalidStateErr("FileError.INVALID_STATE_ERR"); +const QString FileError::kSyntaxErr("FileError.SYNTAX_ERR"); +const QString FileError::kInvalidModificationErr("FileError.INVALID_MODIFICATION_ERR"); +const QString FileError::kQuotaExceededErr("FileError.QUOTA_EXCEEDED_ERR"); +const QString FileError::kPathExistsErr("FileError.PATH_EXISTS_ERR"); + +File::File(Cordova *cordova) : + CPlugin(cordova), + _persistentDir(QString("%1/.local/share/%2/persistent").arg(QDir::homePath()).arg(QCoreApplication::applicationName())) { + QDir::root().mkpath(_persistentDir.absolutePath()); +} + +QVariantMap File::file2map(const QFileInfo &fileInfo) { + QVariantMap res; + + res.insert("name", fileInfo.fileName()); + QPair r = GetRelativePath(fileInfo); + res.insert("fullPath", QString("/") + r.second); + res.insert("filesystemName", r.first); + + res.insert("nativeURL", QString("file://localhost") + fileInfo.absoluteFilePath()); + res.insert("isDirectory", (int)fileInfo.isDir()); + res.insert("isFile", (int)fileInfo.isFile()); + + return res; +} + +QVariantMap File::dir2map(const QDir &dir) { + return file2map(QFileInfo(dir.absolutePath())); +} + +QPair File::GetRelativePath(const QFileInfo &fileInfo) { + QString fullPath = fileInfo.isDir() ? QDir::cleanPath(fileInfo.absoluteFilePath()) : fileInfo.absoluteFilePath(); + + QString relativePath1 = _persistentDir.relativeFilePath(fullPath); + QString relativePath2 = QDir::temp().relativeFilePath(fullPath); + + if (!(relativePath1[0] != '.' || relativePath2[0] != '.')) { + if (relativePath1.size() > relativePath2.size()) { + return QPair("temporary", relativePath2); + } else { + return QPair("persistent", relativePath1); + } + } + + if (relativePath1[0] != '.') + return QPair("persistent", relativePath1); + return QPair("temporary", relativePath2); +} + +void File::requestFileSystem(int scId, int ecId, unsigned short type, unsigned long long size) { + QDir dir; + + if (size >= 1000485760){ + this->callback(ecId, FileError::kQuotaExceededErr); + return; + } + + if (type == 0) + dir = QDir::temp(); + else + dir = _persistentDir; + + if (type > 1) { + this->callback(ecId, FileError::kSyntaxErr); + return; + } else { + QVariantMap res; + res.insert("root", dir2map(dir)); + if (type == 0) + res.insert("name", "temporary"); + else + res.insert("name", "persistent"); + + this->cb(scId, res); + } +} + +QPair File::resolveURI(int ecId, const QString &uri) { + QPair result; + + result.first = false; + + QUrl url = QUrl::fromUserInput(uri); + + if (url.scheme() == "file" && url.isValid()) { + result.first = true; + result.second = QFileInfo(url.path()); + return result; + } + + if (url.scheme() != "cdvfile") { + if (ecId) + this->callback(ecId, FileError::kTypeMismatchErr); + return result; + } + + QString path = url.path().replace("//", "/"); + //NOTE: colon is not safe in url, it is not a valid path in Win and Mac, simple disable it here. + if (path.contains(":") || !url.isValid()){ + if (ecId) + this->callback(ecId, FileError::kEncodingErr); + return result; + } + if (!path.startsWith("/persistent/") && !path.startsWith("/temporary/")) { + if (ecId) + this->callback(ecId, FileError::kEncodingErr); + return result; + } + + result.first = true; + if (path.startsWith("/persistent/")) { + QString relativePath = path.mid(QString("/persistent/").size()); + result.second = QFileInfo(_persistentDir.filePath(relativePath)); + } else { + QString relativePath = path.mid(QString("/temporary/").size()); + result.second = QFileInfo(QDir::temp().filePath(relativePath)); + } + return result; +} + +QPair File::resolveURI(const QString &uri) { + return resolveURI(0, uri); +} + + +void File::_getLocalFilesystemPath(int scId, int ecId, const QString& uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + this->cb(scId, f1.second.absoluteFilePath()); +} + +void File::resolveLocalFileSystemURI(int scId, int ecId, const QString &uri) { + if (uri[0] == '/' || uri[0] == '.') { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFileInfo fileInfo = f1.second; + if (!fileInfo.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + this->cb(scId, file2map(fileInfo)); +} + +void File::getFile(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options) { + QPair f1 = resolveURI(ecId, parentPath + "/" + rpath); + if (!f1.first) + return; + + bool create = options.value("create").toBool(); + bool exclusive = options.value("exclusive").toBool(); + QFile file(f1.second.absoluteFilePath()); + + // if create is false and the path represents a directory, return error + QFileInfo fileInfo = f1.second; + if ((!create) && fileInfo.isDir()) { + this->callback(ecId, FileError::kTypeMismatchErr); + return; + } + + // if file does exist, and create is true and exclusive is true, return error + if (file.exists()) { + if (create && exclusive) { + this->callback(ecId, FileError::kPathExistsErr); + return; + } + } + else { + // if file does not exist and create is false, return error + if (!create) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + file.open(QIODevice::WriteOnly); + file.close(); + + // Check if creation was successfull + if (!file.exists()) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + } + + this->cb(scId, file2map(QFileInfo(file))); +} + +void File::getDirectory(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options) { + QPair f1 = resolveURI(ecId, parentPath + "/" + rpath); + if (!f1.first) + return; + + bool create = options.value("create").toBool(); + bool exclusive = options.value("exclusive").toBool(); + QDir dir(f1.second.absoluteFilePath()); + + QFileInfo &fileInfo = f1.second; + if ((!create) && fileInfo.isFile()) { + this->callback(ecId, FileError::kTypeMismatchErr); + return; + } + + if (dir.exists()) { + if (create && exclusive) { + this->callback(ecId, FileError::kPathExistsErr); + return; + } + } + else { + if (!create) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + QString folderName = dir.dirName(); + dir.cdUp(); + dir.mkdir(folderName); + dir.cd(folderName); + + if (!dir.exists()) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + } + + this->cb(scId, dir2map(dir)); +} + +void File::removeRecursively(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QDir dir(f1.second.absoluteFilePath()); + if (File::rmDir(dir)) + this->cb(scId); + else + this->callback(ecId, FileError::kNoModificationAllowedErr); +} + +void File::write(int scId, int ecId, const QString &uri, const QString &_data, unsigned long long position, bool binary) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + file.open(QIODevice::WriteOnly); + file.close(); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + QFileInfo fileInfo(file); + if (!file.open(QIODevice::ReadWrite)) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + + if (!binary) { + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + textStream.setAutoDetectUnicode(true); + + if (!textStream.seek(position)) { + file.close(); + fileInfo.refresh(); + + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + textStream << _data; + textStream.flush(); + } else { + QByteArray data(_data.toUtf8()); + if (!file.seek(position)) { + file.close(); + fileInfo.refresh(); + + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + file.write(data.data(), data.length()); + } + + file.flush(); + file.close(); + fileInfo.refresh(); + + this->cb(scId, fileInfo.size() - position); +} + +void File::truncate(int scId, int ecId, const QString &uri, unsigned long long size) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.resize(size)) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + + this->cb(scId, size); +} + +void File::getParent(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QDir dir(f1.second.absoluteFilePath()); + + //can't cdup more than app's root + // Try to change into upper directory + if (dir != _persistentDir && dir != QDir::temp()){ + if (!dir.cdUp()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + } + this->cb(scId, dir2map(dir)); +} + +void File::remove(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + if (!f1.first) + return; + + QFileInfo &fileInfo = f1.second; + //TODO: fix + if (!fileInfo.exists() || (fileInfo.absoluteFilePath() == _persistentDir.absolutePath()) || (QDir::temp() == fileInfo.absoluteFilePath())) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + + if (fileInfo.isDir()) { + QDir dir(fileInfo.absoluteFilePath()); + if (dir.rmdir(dir.absolutePath())) { + this->cb(scId); + return; + } + } else { + QFile file(fileInfo.absoluteFilePath()); + if (file.remove()) { + this->cb(scId); + return; + } + } + + this->callback(ecId, FileError::kInvalidModificationErr); +} + +void File::getFileMetadata(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QFileInfo &fileInfo = f1.second; + + if (!fileInfo.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + } else { + QMimeType mime = _db.mimeTypeForFile(fileInfo.fileName()); + + QString args = QString("{name: %1, fullPath: %2, type: %3, lastModifiedDate: new Date(%4), size: %5}") + .arg(CordovaInternal::format(fileInfo.fileName())).arg(CordovaInternal::format(fileInfo.absoluteFilePath())) + .arg(CordovaInternal::format(mime.name())).arg(fileInfo.lastModified().toMSecsSinceEpoch()) + .arg(fileInfo.size()); + + this->callback(scId, args); + } +} + +void File::getMetadata(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QFileInfo &fileInfo = f1.second; + + if (!fileInfo.exists()) + this->callback(ecId, FileError::kNotFoundErr); + else { + QVariantMap obj; + obj.insert("modificationTime", fileInfo.lastModified().toMSecsSinceEpoch()); + obj.insert("size", fileInfo.isDir() ? 0 : fileInfo.size()); + this->cb(scId, obj); + } +} + +void File::readEntries(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QDir dir(f1.second.absoluteFilePath()); + QString entriesList; + + if (!dir.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + for (const QFileInfo &fileInfo: dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { + entriesList += CordovaInternal::format(file2map(fileInfo)) + ","; + } + // Remove trailing comma + if (entriesList.size() > 0) + entriesList.remove(entriesList.size() - 1, 1); + + entriesList = "new Array(" + entriesList + ")"; + + this->callback(scId, entriesList); +} + +void File::readAsText(int scId, int ecId, const QString &uri, const QString &/*encoding*/, int sliceStart, int sliceEnd) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + + QByteArray content = file.readAll(); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + //FIXME: encoding + content = content.mid(sliceStart, sliceEnd - sliceStart); + + this->cb(scId, content); +} + +void File::readAsArrayBuffer(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { + const QString str2array("\ + (function strToArray(str) { \ + var res = new Uint8Array(str.length); \ + for (var i = 0; i < str.length; i++) { \ + res[i] = str.charCodeAt(i); \ + } \ + return res; \ + })(\"%1\")"); + + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + QString res; + QByteArray content = file.readAll(); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + content = content.mid(sliceStart, sliceEnd - sliceStart); + + res.reserve(content.length() * 6); + for (uchar c: content) { + res += "\\x"; + res += QString::number(c, 16).rightJustified(2, '0').toUpper(); + } + + this->callback(scId, str2array.arg(res)); +} + +void File::readAsBinaryString(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + QString res; + QByteArray content = file.readAll(); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + content = content.mid(sliceStart, sliceEnd - sliceStart); + + res.reserve(content.length() * 6); + for (uchar c: content) { + res += "\\x"; + res += QString::number(c, 16).rightJustified(2, '0').toUpper(); + } + this->callback(scId, "\"" + res + "\""); +} + +void File::readAsDataURL(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + QFileInfo &fileInfo = f1.second; + + if (!file.exists()) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + + QByteArray content = file.readAll(); + QString contentType(_db.mimeTypeForFile(fileInfo.fileName()).name()); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + content = content.mid(sliceStart, sliceEnd - sliceStart); + + this->cb(scId, QString("data:%1;base64,").arg(contentType) + content.toBase64()); +} + +bool File::rmDir(const QDir &dir) { + if (dir == _persistentDir || dir == QDir::temp()) {//can't remove root dir + return false; + } + bool result = true; + if (dir.exists()) { + // Iterate over entries and remove them + Q_FOREACH(const QFileInfo &fileInfo, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { + if (fileInfo.isDir()) { + result = rmDir(fileInfo.absoluteFilePath()); + } + else { + result = QFile::remove(fileInfo.absoluteFilePath()); + } + + if (!result) { + return result; + } + } + + // Finally remove the current dir + return dir.rmdir(dir.absolutePath()); + } + return result; +} + +bool File::copyFile(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { + QPair destDir = resolveURI(ecId, destinationUri); + QPair sourceFile = resolveURI(ecId, sourceUri); + + if (!destDir.first || !sourceFile.first) + return false; + + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return false; + } + + if (destDir.second.isFile()) { + this->callback(ecId, FileError::kInvalidModificationErr); + return false; + } + + if (!destDir.second.isDir()) { + this->callback(ecId, FileError::kNotFoundErr); + return false; + } + + QFileInfo &fileInfo = sourceFile.second; + QString fileName((newName.isEmpty()) ? fileInfo.fileName() : newName); + QString destinationFile(QDir(destDir.second.absoluteFilePath()).filePath(fileName)); + if (QFile::copy(fileInfo.absoluteFilePath(), destinationFile)){ + this->cb(scId, file2map(QFileInfo(destinationFile))); + return true; + } + this->callback(ecId, FileError::kInvalidModificationErr); + return false; +} + +void File::copyDir(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { + QPair destDir = resolveURI(ecId, destinationUri); + QPair sourceDir = resolveURI(ecId, sourceUri); + + if (!destDir.first || !sourceDir.first) + return; + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QString targetName = ((newName.isEmpty()) ? sourceDir.second.fileName() : newName); + QString target(QDir(destDir.second.absoluteFilePath()).filePath(targetName)); + + if (QFileInfo(target).isFile()){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + // check: copy directory into itself + if (QDir(sourceDir.second.absoluteFilePath()).relativeFilePath(target)[0] != '.'){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (!QDir(target).exists()){ + QDir(destDir.second.absoluteFilePath()).mkdir(target);; + } else{ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (copyFolder(sourceDir.second.absoluteFilePath(), target)){ + this->cb(scId, dir2map(QDir(target))); + return; + } + this->callback(ecId, FileError::kInvalidModificationErr); + return; +} + +void File::copyTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName) { + QPair f1 = resolveURI(ecId, source); + + if (!f1.first) + return; + + if (f1.second.isDir()) + copyDir(scId, ecId, source, destinationDir, newName); + else + copyFile(scId, ecId, source, destinationDir, newName); +} + +void File::moveFile(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { + QPair sourceFile = resolveURI(ecId, sourceUri); + QPair destDir = resolveURI(ecId, destinationUri); + + if (!destDir.first || !sourceFile.first) + return; + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QString fileName = ((newName.isEmpty()) ? sourceFile.second.fileName() : newName); + QString target = QDir(destDir.second.absoluteFilePath()).filePath(fileName); + + if (sourceFile.second == QFileInfo(target)) { + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (!destDir.second.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + if (!destDir.second.isDir()){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (QFileInfo(target).exists()) { + if (!QFile::remove(target)) { + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + } + + QFile::rename(sourceFile.second.absoluteFilePath(), target); + this->cb(scId, file2map(QFileInfo(target))); +} + +void File::moveDir(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName){ + QPair sourceDir = resolveURI(ecId, sourceUri); + QPair destDir = resolveURI(ecId, destinationUri); + + if (!destDir.first || !sourceDir.first) + return; + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QString fileName = ((newName.isEmpty()) ? sourceDir.second.fileName() : newName); + QString target = QDir(destDir.second.absoluteFilePath()).filePath(fileName); + + if (!destDir.second.exists()){ + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (destDir.second.isFile()){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + // check: copy directory into itself + if (QDir(sourceDir.second.absoluteFilePath()).relativeFilePath(target)[0] != '.'){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (QFileInfo(target).exists() && !QDir(destDir.second.absoluteFilePath()).rmdir(fileName)) { + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (copyFolder(sourceDir.second.absoluteFilePath(), target)) { + rmDir(sourceDir.second.absoluteFilePath()); + this->cb(scId, file2map(QFileInfo(target))); + } else { + this->callback(ecId, FileError::kNoModificationAllowedErr); + } +} + +void File::moveTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName) { + QPair f1 = resolveURI(ecId, source); + + if (!f1.first) + return; + + if (f1.second.isDir()) + moveDir(scId, ecId, source, destinationDir, newName); + else + moveFile(scId, ecId, source, destinationDir, newName); +} + +bool File::copyFolder(const QString& sourceFolder, const QString& destFolder) { + QDir sourceDir(sourceFolder); + if (!sourceDir.exists()) + return false; + QDir destDir(destFolder); + if (!destDir.exists()){ + destDir.mkdir(destFolder); + } + QStringList files = sourceDir.entryList(QDir::Files); + for (int i = 0; i< files.count(); i++) + { + QString srcName = sourceFolder + "/" + files[i]; + QString destName = destFolder + "/" + files[i]; + QFile::copy(srcName, destName); + } + files.clear(); + files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + for (int i = 0; i< files.count(); i++) + { + QString srcName = sourceFolder + "/" + files[i]; + QString destName = destFolder + "/" + files[i]; + copyFolder(srcName, destName); + } + return true; +} diff --git a/plugins/cordova-plugin-file/src/ubuntu/file.h b/plugins/cordova-plugin-file/src/ubuntu/file.h new file mode 100644 index 0000000..de27762 --- /dev/null +++ b/plugins/cordova-plugin-file/src/ubuntu/file.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef FILEAPI_H_SDASDASDAS +#define FILEAPI_H_SDASDASDAS + +#include +#include + +#include +#include + +class File: public CPlugin { + Q_OBJECT +public: + explicit File(Cordova *cordova); + + virtual const QString fullName() override { + return File::fullID(); + } + + virtual const QString shortName() override { + return "File"; + } + + static const QString fullID() { + return "File"; + } + QPair resolveURI(const QString &uri); + QPair resolveURI(int ecId, const QString &uri); + QVariantMap file2map(const QFileInfo &dir); + +public slots: + void requestFileSystem(int scId, int ecId, unsigned short type, unsigned long long size); + void resolveLocalFileSystemURI(int scId, int ecId, const QString&); + void getDirectory(int scId, int ecId, const QString&, const QString&, const QVariantMap&); + void getFile(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options); + void readEntries(int scId, int ecId, const QString &uri); + void getParent(int scId, int ecId, const QString &uri); + void copyTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName); + void moveTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName); + void getFileMetadata(int scId, int ecId, const QString &); + void getMetadata(int scId, int ecId, const QString &); + void remove(int scId, int ecId, const QString &); + void removeRecursively(int scId, int ecId, const QString&); + void write(int scId, int ecId, const QString&, const QString&, unsigned long long position, bool binary); + void readAsText(int scId, int ecId, const QString&, const QString &encoding, int sliceStart, int sliceEnd); + void readAsDataURL(int scId, int ecId, const QString&, int sliceStart, int sliceEnd); + void readAsArrayBuffer(int scId, int ecId, const QString&, int sliceStart, int sliceEnd); + void readAsBinaryString(int scId, int ecId, const QString&, int sliceStart, int sliceEnd); + void truncate(int scId, int ecId, const QString&, unsigned long long size); + + void _getLocalFilesystemPath(int scId, int ecId, const QString&); +private: + void moveFile(int scId, int ecId,const QString&, const QString&, const QString&); + void moveDir(int scId, int ecId,const QString&, const QString&, const QString&); + bool copyFile(int scId, int ecId, const QString&, const QString&, const QString&); + void copyDir(int scId, int ecId, const QString&, const QString&, const QString&); + bool rmDir(const QDir &dir); + bool copyFolder(const QString&, const QString&); + + QPair GetRelativePath(const QFileInfo &fileInfo); + QVariantMap dir2map(const QDir &dir); + + QMimeDatabase _db; + const QDir _persistentDir; + QNetworkAccessManager _manager; +}; + +#endif diff --git a/plugins/cordova-plugin-file/src/windows/FileProxy.js b/plugins/cordova-plugin-file/src/windows/FileProxy.js new file mode 100644 index 0000000..fa56a53 --- /dev/null +++ b/plugins/cordova-plugin-file/src/windows/FileProxy.js @@ -0,0 +1,1209 @@ +/* + * + * 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'), + FileError = require('./FileError'), + Flags = require('./Flags'), + FileSystem = require('./FileSystem'), + LocalFileSystem = require('./LocalFileSystem'), + 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); +} + +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?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) { + 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?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?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) { + 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) { + 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], + enc = args[1], + startPos = args[2], + 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, + 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], + startPos = args[1], + 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), + 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 }); + var xhr = new XMLHttpRequest(); + 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, + 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), + resBuffer = new ArrayBuffer(endPos - startPos), + 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) { + 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], + data = args[1], + position = args[2], + 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) { + 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/plugins/cordova-plugin-file/src/wp/File.cs b/plugins/cordova-plugin-file/src/wp/File.cs new file mode 100644 index 0000000..941c9c8 --- /dev/null +++ b/plugins/cordova-plugin-file/src/wp/File.cs @@ -0,0 +1,1800 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.IsolatedStorage; +using System.Runtime.Serialization; +using System.Security; +using System.Text; +using System.Windows; +using System.Windows.Resources; +using WPCordovaClassLib.Cordova.JSON; + +namespace WPCordovaClassLib.Cordova.Commands +{ + /// + /// Provides access to isolated storage + /// + public class File : BaseCommand + { + // Error codes + public const int NOT_FOUND_ERR = 1; + public const int SECURITY_ERR = 2; + public const int ABORT_ERR = 3; + public const int NOT_READABLE_ERR = 4; + public const int ENCODING_ERR = 5; + public const int NO_MODIFICATION_ALLOWED_ERR = 6; + public const int INVALID_STATE_ERR = 7; + public const int SYNTAX_ERR = 8; + public const int INVALID_MODIFICATION_ERR = 9; + public const int QUOTA_EXCEEDED_ERR = 10; + public const int TYPE_MISMATCH_ERR = 11; + public const int PATH_EXISTS_ERR = 12; + + // File system options + public const int TEMPORARY = 0; + public const int PERSISTENT = 1; + public const int RESOURCE = 2; + public const int APPLICATION = 3; + + /// + /// Temporary directory name + /// + private readonly string TMP_DIRECTORY_NAME = "tmp"; + + /// + /// Represents error code for callback + /// + [DataContract] + public class ErrorCode + { + /// + /// Error code + /// + [DataMember(IsRequired = true, Name = "code")] + public int Code { get; set; } + + /// + /// Creates ErrorCode object + /// + public ErrorCode(int code) + { + this.Code = code; + } + } + + /// + /// Represents File action options. + /// + [DataContract] + public class FileOptions + { + /// + /// File path + /// + /// + private string _fileName; + [DataMember(Name = "fileName")] + public string FilePath + { + get + { + return this._fileName; + } + + set + { + int index = value.IndexOfAny(new char[] { '#', '?' }); + this._fileName = index > -1 ? value.Substring(0, index) : value; + } + } + + /// + /// Full entryPath + /// + [DataMember(Name = "fullPath")] + public string FullPath { get; set; } + + /// + /// Directory name + /// + [DataMember(Name = "dirName")] + public string DirectoryName { get; set; } + + /// + /// Path to create file/directory + /// + [DataMember(Name = "path")] + public string Path { get; set; } + + /// + /// The encoding to use to encode the file's content. Default is UTF8. + /// + [DataMember(Name = "encoding")] + public string Encoding { get; set; } + + /// + /// Uri to get file + /// + /// + private string _uri; + [DataMember(Name = "uri")] + public string Uri + { + get + { + return this._uri; + } + + set + { + int index = value.IndexOfAny(new char[] { '#', '?' }); + this._uri = index > -1 ? value.Substring(0, index) : value; + } + } + + /// + /// Size to truncate file + /// + [DataMember(Name = "size")] + public long Size { get; set; } + + /// + /// Data to write in file + /// + [DataMember(Name = "data")] + public string Data { get; set; } + + /// + /// Position the writing starts with + /// + [DataMember(Name = "position")] + public int Position { get; set; } + + /// + /// Type of file system requested + /// + [DataMember(Name = "type")] + public int FileSystemType { get; set; } + + /// + /// New file/directory name + /// + [DataMember(Name = "newName")] + public string NewName { get; set; } + + /// + /// Destination directory to copy/move file/directory + /// + [DataMember(Name = "parent")] + public string Parent { get; set; } + + /// + /// Options for getFile/getDirectory methods + /// + [DataMember(Name = "options")] + public CreatingOptions CreatingOpt { get; set; } + + /// + /// Creates options object with default parameters + /// + public FileOptions() + { + this.SetDefaultValues(new StreamingContext()); + } + + /// + /// Initializes default values for class fields. + /// Implemented in separate method because default constructor is not invoked during deserialization. + /// + /// + [OnDeserializing()] + public void SetDefaultValues(StreamingContext context) + { + this.Encoding = "UTF-8"; + this.FilePath = ""; + this.FileSystemType = -1; + } + } + + /// + /// Stores image info + /// + [DataContract] + public class FileMetadata + { + [DataMember(Name = "fileName")] + public string FileName { get; set; } + + [DataMember(Name = "fullPath")] + public string FullPath { get; set; } + + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "lastModifiedDate")] + public string LastModifiedDate { get; set; } + + [DataMember(Name = "size")] + public long Size { get; set; } + + public FileMetadata(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + throw new FileNotFoundException("File doesn't exist"); + } + + this.FullPath = filePath; + this.Size = 0; + this.FileName = string.Empty; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool IsFile = isoFile.FileExists(filePath); + bool IsDirectory = isoFile.DirectoryExists(filePath); + + if (!IsDirectory) + { + if (!IsFile) // special case, if isoFile cannot find it, it might still be part of the app-package + { + // attempt to get it from the resources + + Uri fileUri = new Uri(filePath, UriKind.Relative); + StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri); + if (streamInfo != null) + { + this.Size = streamInfo.Stream.Length; + this.FileName = filePath.Substring(filePath.LastIndexOf("/") + 1); + } + else + { + throw new FileNotFoundException("File doesn't exist"); + } + } + else + { + using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, isoFile)) + { + this.Size = stream.Length; + } + + this.FileName = System.IO.Path.GetFileName(filePath); + this.LastModifiedDate = isoFile.GetLastWriteTime(filePath).DateTime.ToString(); + } + } + + this.Type = MimeTypeMapper.GetMimeType(this.FileName); + } + } + } + + /// + /// Represents file or directory modification metadata + /// + [DataContract] + public class ModificationMetadata + { + /// + /// Modification time + /// + [DataMember] + public string modificationTime { get; set; } + } + + /// + /// Represents file or directory entry + /// + [DataContract] + public class FileEntry + { + + /// + /// File type + /// + [DataMember(Name = "isFile")] + public bool IsFile { get; set; } + + /// + /// Directory type + /// + [DataMember(Name = "isDirectory")] + public bool IsDirectory { get; set; } + + /// + /// File/directory name + /// + [DataMember(Name = "name")] + public string Name { get; set; } + + /// + /// Full path to file/directory + /// + [DataMember(Name = "fullPath")] + public string FullPath { get; set; } + + /// + /// URI encoded fullpath + /// + [DataMember(Name = "nativeURL")] + public string NativeURL + { + set { } + get + { + string escaped = Uri.EscapeUriString(this.FullPath); + escaped = escaped.Replace("//", "/"); + if (escaped.StartsWith("/")) + { + escaped = escaped.Insert(0, "/"); + } + return escaped; + } + } + + public bool IsResource { get; set; } + + public static FileEntry GetEntry(string filePath, bool bIsRes=false) + { + FileEntry entry = null; + try + { + entry = new FileEntry(filePath, bIsRes); + + } + catch (Exception ex) + { + Debug.WriteLine("Exception in GetEntry for filePath :: " + filePath + " " + ex.Message); + } + return entry; + } + + /// + /// Creates object and sets necessary properties + /// + /// + public FileEntry(string filePath, bool bIsRes = false) + { + if (string.IsNullOrEmpty(filePath)) + { + throw new ArgumentException(); + } + + if(filePath.Contains(" ")) + { + Debug.WriteLine("FilePath with spaces :: " + filePath); + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + IsResource = bIsRes; + IsFile = isoFile.FileExists(filePath); + IsDirectory = isoFile.DirectoryExists(filePath); + if (IsFile) + { + this.Name = Path.GetFileName(filePath); + } + else if (IsDirectory) + { + this.Name = this.GetDirectoryName(filePath); + if (string.IsNullOrEmpty(Name)) + { + this.Name = "/"; + } + } + else + { + if (IsResource) + { + this.Name = Path.GetFileName(filePath); + } + else + { + throw new FileNotFoundException(); + } + } + + try + { + this.FullPath = filePath.Replace('\\', '/'); // new Uri(filePath).LocalPath; + } + catch (Exception) + { + this.FullPath = filePath; + } + } + } + + /// + /// Extracts directory name from path string + /// Path should refer to a directory, for example \foo\ or /foo. + /// + /// + /// + private string GetDirectoryName(string path) + { + if (String.IsNullOrEmpty(path)) + { + return path; + } + + string[] split = path.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + if (split.Length < 1) + { + return null; + } + else + { + return split[split.Length - 1]; + } + } + } + + + /// + /// Represents info about requested file system + /// + [DataContract] + public class FileSystemInfo + { + /// + /// file system type + /// + [DataMember(Name = "name", IsRequired = true)] + public string Name { get; set; } + + /// + /// Root directory entry + /// + [DataMember(Name = "root", EmitDefaultValue = false)] + public FileEntry Root { get; set; } + + /// + /// Creates class instance + /// + /// + /// Root directory + public FileSystemInfo(string name, FileEntry rootEntry = null) + { + Name = name; + Root = rootEntry; + } + } + + [DataContract] + public class CreatingOptions + { + /// + /// Create file/directory if is doesn't exist + /// + [DataMember(Name = "create")] + public bool Create { get; set; } + + /// + /// Generate an exception if create=true and file/directory already exists + /// + [DataMember(Name = "exclusive")] + public bool Exclusive { get; set; } + + + } + + // returns null value if it fails. + private string[] getOptionStrings(string options) + { + string[] optStings = null; + try + { + optStings = JSON.JsonHelper.Deserialize(options); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), CurrentCommandCallbackId); + } + return optStings; + } + + /// + /// Gets amount of free space available for Isolated Storage + /// + /// No options is needed for this method + public void getFreeDiskSpace(string options) + { + string callbackId = getOptionStrings(options)[0]; + + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isoFile.AvailableFreeSpace), callbackId); + } + } + catch (IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + /// + /// Check if file exists + /// + /// File path + public void testFileExists(string options) + { + IsDirectoryOrFileExist(options, false); + } + + /// + /// Check if directory exists + /// + /// directory name + public void testDirectoryExists(string options) + { + IsDirectoryOrFileExist(options, true); + } + + /// + /// Check if file or directory exist + /// + /// File path/Directory name + /// Flag to recognize what we should check + public void IsDirectoryOrFileExist(string options, bool isDirectory) + { + string[] args = getOptionStrings(options); + string callbackId = args[1]; + FileOptions fileOptions = JSON.JsonHelper.Deserialize(args[0]); + string filePath = args[0]; + + if (fileOptions == null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId); + } + + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool isExist; + if (isDirectory) + { + isExist = isoFile.DirectoryExists(fileOptions.DirectoryName); + } + else + { + isExist = isoFile.FileExists(fileOptions.FilePath); + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isExist), callbackId); + } + } + catch (IsolatedStorageException) // default handler throws INVALID_MODIFICATION_ERR + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + } + + } + + public void readAsDataURL(string options) + { + string[] optStrings = getOptionStrings(options); + string filePath = optStrings[0]; + int startPos = int.Parse(optStrings[1]); + int endPos = int.Parse(optStrings[2]); + string callbackId = optStrings[3]; + + if (filePath != null) + { + try + { + string base64URL = null; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(filePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + string mimeType = MimeTypeMapper.GetMimeType(filePath); + + using (IsolatedStorageFileStream stream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read)) + { + string base64String = GetFileContent(stream); + base64URL = "data:" + mimeType + ";base64," + base64String; + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, base64URL), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + } + + private byte[] readFileBytes(string filePath,int startPos,int endPos, IsolatedStorageFile isoFile) + { + byte[] buffer; + using (IsolatedStorageFileStream reader = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read)) + { + if (startPos < 0) + { + startPos = Math.Max((int)reader.Length + startPos, 0); + } + else if (startPos > 0) + { + startPos = Math.Min((int)reader.Length, startPos); + } + if (endPos > 0) + { + endPos = Math.Min((int)reader.Length, endPos); + } + else if (endPos < 0) + { + endPos = Math.Max(endPos + (int)reader.Length, 0); + } + + buffer = new byte[endPos - startPos]; + reader.Seek(startPos, SeekOrigin.Begin); + reader.Read(buffer, 0, buffer.Length); + } + + return buffer; + } + + public void readAsArrayBuffer(string options) + { + string[] optStrings = getOptionStrings(options); + string filePath = optStrings[0]; + int startPos = int.Parse(optStrings[1]); + int endPos = int.Parse(optStrings[2]); + string callbackId = optStrings[3]; + + try + { + byte[] buffer; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(filePath)) + { + readResourceAsText(options); + return; + } + buffer = readFileBytes(filePath, startPos, endPos, isoFile); + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, buffer), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + public void readAsBinaryString(string options) + { + string[] optStrings = getOptionStrings(options); + string filePath = optStrings[0]; + int startPos = int.Parse(optStrings[1]); + int endPos = int.Parse(optStrings[2]); + string callbackId = optStrings[3]; + + try + { + string result; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(filePath)) + { + readResourceAsText(options); + return; + } + + byte[] buffer = readFileBytes(filePath, startPos, endPos, isoFile); + result = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(buffer, 0, buffer.Length); + + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, result), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + public void readAsText(string options) + { + string[] optStrings = getOptionStrings(options); + string filePath = optStrings[0]; + string encStr = optStrings[1]; + int startPos = int.Parse(optStrings[2]); + int endPos = int.Parse(optStrings[3]); + string callbackId = optStrings[4]; + + try + { + string text = ""; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(filePath)) + { + readResourceAsText(options); + return; + } + Encoding encoding = Encoding.GetEncoding(encStr); + + byte[] buffer = this.readFileBytes(filePath, startPos, endPos, isoFile); + text = encoding.GetString(buffer, 0, buffer.Length); + } + + // JIRA: https://issues.apache.org/jira/browse/CB-8792 + // Need to perform additional serialization here because NativeExecution is always trying + // to do JSON.parse() on command result. This leads to issue when trying to read JSON files + var resultText = JsonHelper.Serialize(text); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, resultText), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + /// + /// Reads application resource as a text + /// + /// Path to a resource + public void readResourceAsText(string options) + { + string[] optStrings = getOptionStrings(options); + string pathToResource = optStrings[0]; + string encStr = optStrings[1]; + int start = int.Parse(optStrings[2]); + int endMarker = int.Parse(optStrings[3]); + string callbackId = optStrings[4]; + + try + { + if (pathToResource.StartsWith("/")) + { + pathToResource = pathToResource.Remove(0, 1); + } + + var resource = Application.GetResourceStream(new Uri(pathToResource, UriKind.Relative)); + + if (resource == null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + + string text; + StreamReader streamReader = new StreamReader(resource.Stream); + text = streamReader.ReadToEnd(); + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + public void truncate(string options) + { + string[] optStrings = getOptionStrings(options); + + string filePath = optStrings[0]; + int size = int.Parse(optStrings[1]); + string callbackId = optStrings[2]; + + try + { + long streamLength = 0; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(filePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + + using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile)) + { + if (0 <= size && size <= stream.Length) + { + stream.SetLength(size); + } + streamLength = stream.Length; + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, streamLength), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + //write:[filePath,data,position,isBinary,callbackId] + public void write(string options) + { + string[] optStrings = getOptionStrings(options); + + string filePath = optStrings[0]; + string data = optStrings[1]; + int position = int.Parse(optStrings[2]); + bool isBinary = bool.Parse(optStrings[3]); + string callbackId = optStrings[4]; + + try + { + if (string.IsNullOrEmpty(data)) + { + Debug.WriteLine("Expected some data to be send in the write command to {0}", filePath); + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId); + return; + } + + byte[] dataToWrite = isBinary ? JSON.JsonHelper.Deserialize(data) : + System.Text.Encoding.UTF8.GetBytes(data); + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + // create the file if not exists + if (!isoFile.FileExists(filePath)) + { + var file = isoFile.CreateFile(filePath); + file.Close(); + } + + using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile)) + { + if (0 <= position && position <= stream.Length) + { + stream.SetLength(position); + } + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Seek(0, SeekOrigin.End); + writer.Write(dataToWrite); + } + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, dataToWrite.Length), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + /// + /// Look up metadata about this entry. + /// + /// filePath to entry + public void getMetadata(string options) + { + string[] optStings = getOptionStrings(options); + string filePath = optStings[0]; + string callbackId = optStings[1]; + + if (filePath != null) + { + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.FileExists(filePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, + new ModificationMetadata() { modificationTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString() }), callbackId); + } + else if (isoFile.DirectoryExists(filePath)) + { + string modTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString(); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new ModificationMetadata() { modificationTime = modTime }), callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + + } + } + catch (IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + + } + + + /// + /// Returns a File that represents the current state of the file that this FileEntry represents. + /// + /// filePath to entry + /// + public void getFileMetadata(string options) + { + string[] optStings = getOptionStrings(options); + string filePath = optStings[0]; + string callbackId = optStings[1]; + + if (!string.IsNullOrEmpty(filePath)) + { + try + { + FileMetadata metaData = new FileMetadata(filePath); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, metaData), callbackId); + } + catch (IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); + } + } + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + } + + /// + /// Look up the parent DirectoryEntry containing this Entry. + /// If this Entry is the root of IsolatedStorage, its parent is itself. + /// + /// + public void getParent(string options) + { + string[] optStings = getOptionStrings(options); + string filePath = optStings[0]; + string callbackId = optStings[1]; + + if (filePath != null) + { + try + { + if (string.IsNullOrEmpty(filePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + FileEntry entry; + + if (isoFile.FileExists(filePath) || isoFile.DirectoryExists(filePath)) + { + + + string path = this.GetParentDirectory(filePath); + entry = FileEntry.GetEntry(path); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry),callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); + } + + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); + } + } + } + } + + public void remove(string options) + { + string[] args = getOptionStrings(options); + string filePath = args[0]; + string callbackId = args[1]; + + if (filePath != null) + { + try + { + if (filePath == "/" || filePath == "" || filePath == @"\") + { + throw new Exception("Cannot delete root file system") ; + } + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.FileExists(filePath)) + { + isoFile.DeleteFile(filePath); + } + else + { + if (isoFile.DirectoryExists(filePath)) + { + isoFile.DeleteDirectory(filePath); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); + return; + } + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK),callbackId); + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); + } + } + } + } + + public void removeRecursively(string options) + { + string[] args = getOptionStrings(options); + string filePath = args[0]; + string callbackId = args[1]; + + if (filePath != null) + { + if (string.IsNullOrEmpty(filePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId); + } + else + { + if (removeDirRecursively(filePath, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId); + } + } + } + } + + public void readEntries(string options) + { + string[] args = getOptionStrings(options); + string filePath = args[0]; + string callbackId = args[1]; + + if (filePath != null) + { + try + { + if (string.IsNullOrEmpty(filePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.DirectoryExists(filePath)) + { + string path = File.AddSlashToDirectory(filePath); + List entries = new List(); + string[] files = isoFile.GetFileNames(path + "*"); + string[] dirs = isoFile.GetDirectoryNames(path + "*"); + foreach (string file in files) + { + entries.Add(FileEntry.GetEntry(path + file)); + } + foreach (string dir in dirs) + { + entries.Add(FileEntry.GetEntry(path + dir + "/")); + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entries),callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); + } + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); + } + } + } + } + + public void requestFileSystem(string options) + { + // TODO: try/catch + string[] optVals = getOptionStrings(options); + //FileOptions fileOptions = new FileOptions(); + int fileSystemType = int.Parse(optVals[0]); + double size = double.Parse(optVals[1]); + string callbackId = optVals[2]; + + + IsolatedStorageFile.GetUserStoreForApplication(); + + if (size > (10 * 1024 * 1024)) // 10 MB, compier will clean this up! + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId); + return; + } + + try + { + if (size != 0) + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + long availableSize = isoFile.AvailableFreeSpace; + if (size > availableSize) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId); + return; + } + } + } + + if (fileSystemType == PERSISTENT) + { + // TODO: this should be in it's own folder to prevent overwriting of the app assets, which are also in ISO + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("persistent", FileEntry.GetEntry("/"))), callbackId); + } + else if (fileSystemType == TEMPORARY) + { + using (IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoStorage.FileExists(TMP_DIRECTORY_NAME)) + { + isoStorage.CreateDirectory(TMP_DIRECTORY_NAME); + } + } + + string tmpFolder = "/" + TMP_DIRECTORY_NAME + "/"; + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("temporary", FileEntry.GetEntry(tmpFolder))), callbackId); + } + else if (fileSystemType == RESOURCE) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("resource")), callbackId); + } + else if (fileSystemType == APPLICATION) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("application")), callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); + } + + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); + } + } + } + + public void resolveLocalFileSystemURI(string options) + { + + string[] optVals = getOptionStrings(options); + string uri = optVals[0].Split('?')[0]; + string callbackId = optVals[1]; + + if (uri != null) + { + // a single '/' is valid, however, '/someDir' is not, but '/tmp//somedir' and '///someDir' are valid + if (uri.StartsWith("/") && uri.IndexOf("//") < 0 && uri != "/") + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); + return; + } + try + { + // fix encoded spaces + string path = Uri.UnescapeDataString(uri); + + FileEntry uriEntry = FileEntry.GetEntry(path); + if (uriEntry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uriEntry), callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); + } + } + } + } + + public void copyTo(string options) + { + TransferTo(options, false); + } + + public void moveTo(string options) + { + TransferTo(options, true); + } + + public void getFile(string options) + { + GetFileOrDirectory(options, false); + } + + public void getDirectory(string options) + { + GetFileOrDirectory(options, true); + } + + #region internal functionality + + /// + /// Retrieves the parent directory name of the specified path, + /// + /// Path + /// Parent directory name + private string GetParentDirectory(string path) + { + if (String.IsNullOrEmpty(path) || path == "/") + { + return "/"; + } + + if (path.EndsWith(@"/") || path.EndsWith(@"\")) + { + return this.GetParentDirectory(Path.GetDirectoryName(path)); + } + + string result = Path.GetDirectoryName(path); + if (result == null) + { + result = "/"; + } + + return result; + } + + private bool removeDirRecursively(string fullPath,string callbackId) + { + try + { + if (fullPath == "/") + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); + return false; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.DirectoryExists(fullPath)) + { + string tempPath = File.AddSlashToDirectory(fullPath); + string[] files = isoFile.GetFileNames(tempPath + "*"); + if (files.Length > 0) + { + foreach (string file in files) + { + isoFile.DeleteFile(tempPath + file); + } + } + string[] dirs = isoFile.GetDirectoryNames(tempPath + "*"); + if (dirs.Length > 0) + { + foreach (string dir in dirs) + { + if (!removeDirRecursively(tempPath + dir, callbackId)) + { + return false; + } + } + } + isoFile.DeleteDirectory(fullPath); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); + } + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); + return false; + } + } + return true; + } + + private bool CanonicalCompare(string pathA, string pathB) + { + string a = pathA.Replace("//", "/"); + string b = pathB.Replace("//", "/"); + + return a.Equals(b, StringComparison.OrdinalIgnoreCase); + } + + /* + * copyTo:["fullPath","parent", "newName"], + * moveTo:["fullPath","parent", "newName"], + */ + private void TransferTo(string options, bool move) + { + // TODO: try/catch + string[] optStrings = getOptionStrings(options); + string fullPath = optStrings[0]; + string parent = optStrings[1]; + string newFileName = optStrings[2]; + string callbackId = optStrings[3]; + + char[] invalids = Path.GetInvalidPathChars(); + + if (newFileName.IndexOfAny(invalids) > -1 || newFileName.IndexOf(":") > -1 ) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); + return; + } + + try + { + if ((parent == null) || (string.IsNullOrEmpty(parent)) || (string.IsNullOrEmpty(fullPath))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + + string parentPath = File.AddSlashToDirectory(parent); + string currentPath = fullPath; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool isFileExist = isoFile.FileExists(currentPath); + bool isDirectoryExist = isoFile.DirectoryExists(currentPath); + bool isParentExist = isoFile.DirectoryExists(parentPath); + + if ( ( !isFileExist && !isDirectoryExist ) || !isParentExist ) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + string newName; + string newPath; + if (isFileExist) + { + newName = (string.IsNullOrEmpty(newFileName)) + ? Path.GetFileName(currentPath) + : newFileName; + + newPath = Path.Combine(parentPath, newName); + + // sanity check .. + // cannot copy file onto itself + if (CanonicalCompare(newPath,currentPath)) //(parent + newFileName)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId); + return; + } + else if (isoFile.DirectoryExists(newPath)) + { + // there is already a folder with the same name, operation is not allowed + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId); + return; + } + else if (isoFile.FileExists(newPath)) + { // remove destination file if exists, in other case there will be exception + isoFile.DeleteFile(newPath); + } + + if (move) + { + isoFile.MoveFile(currentPath, newPath); + } + else + { + isoFile.CopyFile(currentPath, newPath, true); + } + } + else + { + newName = (string.IsNullOrEmpty(newFileName)) + ? currentPath + : newFileName; + + newPath = Path.Combine(parentPath, newName); + + if (move) + { + // remove destination directory if exists, in other case there will be exception + // target directory should be empty + if (!newPath.Equals(currentPath) && isoFile.DirectoryExists(newPath)) + { + isoFile.DeleteDirectory(newPath); + } + + isoFile.MoveDirectory(currentPath, newPath); + } + else + { + CopyDirectory(currentPath, newPath, isoFile); + } + } + FileEntry entry = FileEntry.GetEntry(newPath); + if (entry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + } + + } + catch (Exception ex) + { + if (!this.HandleException(ex, callbackId)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); + } + } + } + + private bool HandleException(Exception ex, string cbId="") + { + bool handled = false; + string callbackId = String.IsNullOrEmpty(cbId) ? this.CurrentCommandCallbackId : cbId; + if (ex is SecurityException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR), callbackId); + handled = true; + } + else if (ex is FileNotFoundException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + handled = true; + } + else if (ex is ArgumentException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); + handled = true; + } + else if (ex is IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId); + handled = true; + } + else if (ex is DirectoryNotFoundException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + handled = true; + } + return handled; + } + + private void CopyDirectory(string sourceDir, string destDir, IsolatedStorageFile isoFile) + { + string path = File.AddSlashToDirectory(sourceDir); + + bool bExists = isoFile.DirectoryExists(destDir); + + if (!bExists) + { + isoFile.CreateDirectory(destDir); + } + + destDir = File.AddSlashToDirectory(destDir); + + string[] files = isoFile.GetFileNames(path + "*"); + + if (files.Length > 0) + { + foreach (string file in files) + { + isoFile.CopyFile(path + file, destDir + file,true); + } + } + string[] dirs = isoFile.GetDirectoryNames(path + "*"); + if (dirs.Length > 0) + { + foreach (string dir in dirs) + { + CopyDirectory(path + dir, destDir + dir, isoFile); + } + } + } + + private string RemoveExtraSlash(string path) { + if (path.StartsWith("//")) { + path = path.Remove(0, 1); + path = RemoveExtraSlash(path); + } + return path; + } + + private string ResolvePath(string parentPath, string path) + { + string absolutePath = null; + + if (path.Contains("..")) + { + if (parentPath.Length > 1 && parentPath.StartsWith("/") && parentPath !="/") + { + parentPath = RemoveExtraSlash(parentPath); + } + + string fullPath = Path.GetFullPath(Path.Combine(parentPath, path)); + absolutePath = fullPath.Replace(Path.GetPathRoot(fullPath), @"//"); + } + else + { + absolutePath = Path.Combine(parentPath + "/", path); + } + return absolutePath; + } + + private void GetFileOrDirectory(string options, bool getDirectory) + { + FileOptions fOptions = new FileOptions(); + string[] args = getOptionStrings(options); + + fOptions.FullPath = args[0]; + fOptions.Path = args[1]; + + string callbackId = args[3]; + + try + { + fOptions.CreatingOpt = JSON.JsonHelper.Deserialize(args[2]); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId); + return; + } + + try + { + if ((string.IsNullOrEmpty(fOptions.Path)) || (string.IsNullOrEmpty(fOptions.FullPath))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + + string path; + + if (fOptions.Path.Split(':').Length > 2) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); + return; + } + + try + { + path = ResolvePath(fOptions.FullPath, fOptions.Path); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool isFile = isoFile.FileExists(path); + bool isDirectory = isoFile.DirectoryExists(path); + bool create = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Create; + bool exclusive = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Exclusive; + if (create) + { + if (exclusive && (isoFile.FileExists(path) || isoFile.DirectoryExists(path))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, PATH_EXISTS_ERR), callbackId); + return; + } + + // need to make sure the parent exists + // it is an error to create a directory whose immediate parent does not yet exist + // see issue: https://issues.apache.org/jira/browse/CB-339 + string[] pathParts = path.Split('/'); + string builtPath = pathParts[0]; + for (int n = 1; n < pathParts.Length - 1; n++) + { + builtPath += "/" + pathParts[n]; + if (!isoFile.DirectoryExists(builtPath)) + { + Debug.WriteLine(String.Format("Error :: Parent folder \"{0}\" does not exist, when attempting to create \"{1}\"",builtPath,path)); + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + return; + } + } + + if ((getDirectory) && (!isDirectory)) + { + isoFile.CreateDirectory(path); + } + else + { + if ((!getDirectory) && (!isFile)) + { + + IsolatedStorageFileStream fileStream = isoFile.CreateFile(path); + fileStream.Close(); + } + } + } + else // (not create) + { + if ((!isFile) && (!isDirectory)) + { + if (path.IndexOf("//www") == 0) + { + Uri fileUri = new Uri(path.Remove(0,2), UriKind.Relative); + StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri); + if (streamInfo != null) + { + FileEntry _entry = FileEntry.GetEntry(fileUri.OriginalString,true); + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, _entry), callbackId); + + //using (BinaryReader br = new BinaryReader(streamInfo.Stream)) + //{ + // byte[] data = br.ReadBytes((int)streamInfo.Stream.Length); + + //} + + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + + + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + return; + } + if (((getDirectory) && (!isDirectory)) || ((!getDirectory) && (!isFile))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, TYPE_MISMATCH_ERR), callbackId); + return; + } + } + FileEntry entry = FileEntry.GetEntry(path); + if (entry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); + } + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); + } + } + } + + private static string AddSlashToDirectory(string dirPath) + { + if (dirPath.EndsWith("/")) + { + return dirPath; + } + else + { + return dirPath + "/"; + } + } + + /// + /// Returns file content in a form of base64 string + /// + /// File stream + /// Base64 representation of the file + private string GetFileContent(Stream stream) + { + int streamLength = (int)stream.Length; + byte[] fileData = new byte[streamLength + 1]; + stream.Read(fileData, 0, streamLength); + stream.Close(); + return Convert.ToBase64String(fileData); + } + + #endregion + + } +} diff --git a/plugins/cordova-plugin-file/tests/plugin.xml b/plugins/cordova-plugin-file/tests/plugin.xml new file mode 100644 index 0000000..29e531f --- /dev/null +++ b/plugins/cordova-plugin-file/tests/plugin.xml @@ -0,0 +1,44 @@ + + + + + + Cordova File Plugin Tests + Apache 2.0 + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java b/plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java new file mode 100644 index 0000000..9eba0d0 --- /dev/null +++ b/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/plugins/cordova-plugin-file/tests/tests.js b/plugins/cordova-plugin-file/tests/tests.js new file mode 100644 index 0000000..acdcfa0 --- /dev/null +++ b/plugins/cordova-plugin-file/tests/tests.js @@ -0,0 +1,4012 @@ +/* + * + * 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. + * + */ + +/* jshint jasmine: true */ +/* global WebKitBlobBuilder */ + +exports.defineAutoTests = function () { + 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"); + + 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' + }, + root, + temp_root, + 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(""), + b = currentPath.split("/").join("").split("\\").join(""), + pass = a === b; + return { + pass : pass, + message : 'Expected paths to match : ' + path + ' should be ' + currentPath + }; + } + }; + }, + toFailWithMessage : function () { + return { + compare : function (error, message) { + 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) { + root = fileSystem.root; + // set in file.tests.js + persistent_root = root; + window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) { + 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 () { + 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); + }); + 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')); + }); + 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')); + }); + 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); + 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); + }); + 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) 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); + } + 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) { + var fileName = "de.create.file", + 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", + 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"), + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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", + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.ENCODING_ERR); + 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(); + 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); + 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", + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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", + filePath = joinURL(root.fullPath, fileName), + 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", + filePath = joinURL(root.fullPath, fileName), + 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", + 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", + existingFile; + + function fail(error) { + expect(error).toBeDefined(); + if (isChrome) { + /*INVALID_MODIFICATION_ERR (code: 9) 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); + } + // 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", + filePath = joinURL(root.fullPath, fileName), + 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); + }, + 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", + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.ENCODING_ERR); + 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", + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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", + 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", + dirPath = joinURL(root.fullPath, dirName), + 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", + dirPath = joinURL(root.fullPath, dirName), + 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", + dirPath = joinURL(root.fullPath, dirName), + 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", + existingDir, + fail = function (error) { + expect(error).toBeDefined(); + if (isChrome) { + /*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 (Chrome).*/ + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + } else { + expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); + } + // 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", + dirPath = joinURL(root.fullPath, dirName), + 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", + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.ENCODING_ERR); + 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", + existingFile, + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); + // 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", + existingDir, + fail = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); + // 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", + subDirName = "dir", + dirExists = function (error) { + expect(error).toBeDefined(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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) 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); + } + 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, + 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', + 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); + } + + // 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, + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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(); + expect(err).toBeFileError(FileError.NOT_FOUND_ERR); + 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(); + expect(typeof File).toBe('function'); + }); + it("file.spec.40 should be define File attributes", function () { + var file = new File(); + 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", + 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", + testFile, + 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); + } + + // 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", + newFile, + 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); + } + + // 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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", + fullPath = joinURL(root.fullPath, fileName), + 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", + 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", + 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, + 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", + rootPath = root.fullPath, + 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", + dirName_2 = "num 2", + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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) { + 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); + // 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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", + fileName = "re ove.txt", + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + 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) 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); + } + done(); + }); + }); + it("file.spec.57 copyTo: file", function (done) { + var file1 = "entry copy.file1", + file2 = "entry copy.file2", + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + // 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", + srcDir = "entry.copy.srcDir", + dstDir = "entry.copy.dstDir", + dstPath = joinURL(root.fullPath, dstDir), + 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", + srcDir = "entry.copy srcDirSame", + dstDir = "entry.copy srcDirSame-backup", + dstPath = joinURL(root.fullPath, dstDir), + 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", + srcDir = "entry.copy.dos.srcDir", + srcPath = joinURL(root.fullPath, srcDir), + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + 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", + dstDir = "entry.copy.dis.dstDir", + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + 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", + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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", + 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); + // 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", + file2 = "entry.move.fsp.file2", + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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", + dir = "entry.move.fnp.dir", + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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", + srcDir = "entry.move.dsp.srcDir", + dstDir = "entry.move.dsp.dstDir", + dstPath = joinURL(root.fullPath, dstDir), + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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", + srcDir = "entry.move.dsp.srcDir", + dstDir = "entry.move.dsp.srcDir-backup", + dstPath = joinURL(root.fullPath, dstDir), + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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", + srcDir = "entry.move.dnp.srcDir", + dstDir = "entry.move.dnp.dstDir", + dstPath = joinURL(root.fullPath, dstDir), + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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); + 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", + srcDir = "entry.move.dos.srcDir", + srcPath = joinURL(root.fullPath, srcDir), + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + // 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", + dstDir = "entry.move.dis.dstDir", + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + // 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", + 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", + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + //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", + dstDir = "entry.move.fod.dstDir", + subDir = "subDir", + dirPath = joinURL(joinURL(root.fullPath, dstDir), subDir), + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + // 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", + srcDir = "entry.move.dof.srcDir", + dirPath = joinURL(root.fullPath, srcDir), + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + // 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", + srcDir = "entry.copy.dof.srcDir", + dirPath = joinURL(root.fullPath, srcDir), + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + //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", + dstDir = "entry.move.dod.dstDir", + subDir = "subDir", + srcPath = joinURL(root.fullPath, srcDir), + 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(); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + // 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", + file2 = "entry.move.frf.file2", + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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", + srcDir = "entry.move.drd.srcDir", + dstDir = "entry.move.drd.dstDir", + dstPath = joinURL(root.fullPath, dstDir), + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + // 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) { + + var file1 = "entry.move.dnf.file1", + dstDir = "entry.move.dnf.dstDir", + 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(); + 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); + } else { + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + } + // 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", + 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); + // 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(); + 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", + verifier = function (evt) { + expect(evt).toBeDefined(); + expect(evt.target.error).toBeFileError(FileError.NOT_FOUND_ERR); + done(); + }; + root.getFile(fileName, { + create : true + }, function (entry) { + entry.file(function (file) { + deleteEntry(fileName, function () { + //Create FileReader + var reader = new FileReader(); + 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]); + } 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(); + reader.onloadend = verifier; + reader.readAsText(blob); + }); + function writeDummyFile(writeBinary, callback, done, fileContents) { + var fileName = "dummy.txt", + fileEntry = null, + // use default string if file data is not provided + fileData = fileContents !== undefined ? fileContents : + '\u20AC\xEB - There is an exception to every rule. Except this one.', + 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(); + 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:;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:;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; + + 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); + 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; + + 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 + content = "There is an exception to every rule.", // for checkin file length + exception = " Except this one.", + 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(); + 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 + content = "There is an exception to every rule.", // for checkin file length + exception = " Except this one.", + 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(); + 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 + content = "This is our sentence.", // for checking file length + exception = "newer sentence.", + 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(); + 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 (isChrome) { + /* Chrome (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 + content = "This is our sentence.", // for checking file length + exception = "new.", + 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(); + 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 + content = '\n\nData\n\n', // for testing file length + 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 + content = '{ "name": "Guy Incognito", "email": "here@there.com" }', // for testing file length + 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 + content = "There is an exception to every rule. Except this one.", // for testing file length + 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", + 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 + data = new ArrayBuffer(32), + dataView = new Int8Array(data), // for verifying file length + 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 + data = new ArrayBuffer(32), + dataView = new Int8Array(data), + blob, // for verifying file length + length = 32; + for (var i = 0; i < dataView.length; i++) { + dataView[i] = i; + } + try { + // Mobile Safari: Use Blob constructor + blob = new Blob([data], { + "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', + outputFileName = 'verify.txt', + dummyFileText = 'This text should be written to two files', + verifier = function (outputFileWriter) { + expect(outputFileWriter.length).toBe(dummyFileText.length); + expect(outputFileWriter.position).toBe(dummyFileText.length); + deleteFile(outputFileName, done); + }, + writeFile = function (fileName, fileData, win) { + var theWriter, + 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)); + }, + 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', + outputFileName = 'verify2.txt', + dummyFileText = 'This text should be written to two files', + verifier = function (outputFileWriter) { + expect(outputFileWriter.length).toBe(10); + expect(outputFileWriter.position).toBe(10); + deleteFile(outputFileName, done); + }, + writeFile = function (fileName, fileData, win) { + var theWriter, + 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)); + }, + 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", + outputFileName = 'verify.bin', // file content + data = new ArrayBuffer(32), + dataView = new Int8Array(data), + blob, // for verifying file length + length = 32, + verifier = function (outputFileWriter) { + expect(outputFileWriter.length).toBe(length); + expect(outputFileWriter.position).toBe(length); + // cleanup + deleteFile(outputFileName); + done(); + }, + writeFile = function (fileName, fileData, win) { + var theWriter, + 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)); + }, + 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], { + "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) { + 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", + 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(); + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); + 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://"; + if (isChrome) { + pathExpect = 'filesystem:file://'; + } + 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', + fileName = 'nativeEntries.file', + directory, + 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(null, 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) { + 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) { + 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) { + 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", + file2 = "entry.copy.file2a", + sourceEntry, + fullPath = joinURL(root.fullPath, file2), + 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); + }, + 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", + file2 = "entry.copy.file2b", + sourceEntry, + fullPath = joinURL(temp_root.fullPath, file2), + 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); + }, + 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", + file2 = "entry.copy.file2a", + sourceEntry, + fullPath = joinURL(root.fullPath, file2), + 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); + }, + 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", + file2 = "entry.copy.file2b", + sourceEntry, + fullPath = joinURL(temp_root.fullPath, file2), + 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); + }, + 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']; + 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, in config.xml or + // cdvfile: in CSP and in config.xml + resolveLocalFileSystemURL('cdvfile://localhost/' + cdvfileApplicationDirectoryFsRootName + '/www/img/logo.png', function(entry) { + 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(); + 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', + nestedDirName = 'nestedDir131', + 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', + 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', + 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') { + 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", + fullPath = joinURL(temp_root.fullPath, file2), + 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); + }, + transfer = function () { + resolveLocalFileSystemURL(src, function(entry) { + 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) { + 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/'); + }, MEDIUM_TIMEOUT); + it("file.spec.142 assets should be enumerable", function(done) { + resolveLocalFileSystemURL('file:///android_asset/www/fixtures/asset-test', function(entry) { + 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) { + 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) { + expect(entry.isFile).toBe(true); + entry.file(function (file) { + expect(file).toBeDefined(); + var reader = new FileReader(); + 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", + fullPath = joinURL(temp_root.fullPath, file2), + 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); + }, + transfer = function () { + resolveLocalFileSystemURL('file:///android_asset/www/index.html', function(entry) { + 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) { + 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); + 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) { + 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); + if (fsRoots.hasOwnProperty(cordova.platformId)) { + (fsRoots[cordova.platformId].split(',')).forEach(function (fs) { + if (cordova.platformId === 'ios' && fs === 'private') { + 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/plugins/cordova-plugin-file/tests/www/fixtures/asset-test/asset-test.txt b/plugins/cordova-plugin-file/tests/www/fixtures/asset-test/asset-test.txt new file mode 100644 index 0000000..2ee2df0 --- /dev/null +++ b/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/plugins/cordova-plugin-file/www/DirectoryEntry.js b/plugins/cordova-plugin-file/www/DirectoryEntry.js new file mode 100644 index 0000000..62f468a --- /dev/null +++ b/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'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('./Entry'), + FileError = require('./FileError'), + 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/plugins/cordova-plugin-file/www/DirectoryReader.js b/plugins/cordova-plugin-file/www/DirectoryReader.js new file mode 100644 index 0000000..2894c9a --- /dev/null +++ b/plugins/cordova-plugin-file/www/DirectoryReader.js @@ -0,0 +1,73 @@ +/* + * + * 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'), + 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 or + *