aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2019-06-07 01:42:47 +0200
committerRobert Jaros <rjaros@finn.pl>2019-06-07 01:42:47 +0200
commitd40abae23a4700447b000c55a177bfdb703fef44 (patch)
tree862aa26a0f64cce1d431fc23b697de20e7a2a48c
parentf9398d1ae7da496d3a290d7b225225d70a4bb174 (diff)
downloadkvision-d40abae23a4700447b000c55a177bfdb703fef44.tar.gz
kvision-d40abae23a4700447b000c55a177bfdb703fef44.tar.bz2
kvision-d40abae23a4700447b000c55a177bfdb703fef44.zip
Support for Cordova file api.
-rw-r--r--kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt542
-rw-r--r--kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt221
-rw-r--r--kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt47
-rw-r--r--kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt7
4 files changed, 815 insertions, 2 deletions
diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt
new file mode 100644
index 00000000..df176e84
--- /dev/null
+++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt
@@ -0,0 +1,542 @@
+@file:Suppress(
+ "TooManyFunctions", "TooGenericExceptionCaught"
+)
+
+/*
+ * Copyright (c) 2017-present Robert Jaros
+ *
+ * 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.
+ */
+
+package pl.treksoft.kvision.cordova
+
+import org.khronos.webgl.ArrayBuffer
+import org.khronos.webgl.Uint8Array
+import org.khronos.webgl.set
+import org.w3c.files.Blob
+import org.w3c.files.BlobPropertyBag
+import org.w3c.files.FileReader
+import pl.treksoft.kvision.utils.obj
+import kotlin.browser.window
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+/**
+ * Exception class for file errors.
+ */
+class FileException(code: Int) : Exception("Error: $code")
+
+/**
+ * System directories class.
+ */
+external class SystemDirs {
+ val applicationDirectory: String
+ val applicationStorageDirectory: String
+ val dataDirectory: String
+ val cacheDirectory: String
+ val externalApplicationStorageDirectory: String?
+ val externalDataDirectory: String?
+ val externalCacheDirectory: String?
+ val externalRootDirectory: String?
+ val tempDirectory: String?
+ val syncedDataDirectory: String?
+ val documentsDirectory: String?
+ val sharedDirectory: String?
+}
+
+/**
+ * Main object for Cordova file.
+ */
+object File {
+
+ const val NOT_FOUND_ERR = 1
+ const val SECURITY_ERR = 2
+ const val ABORT_ERR = 3
+ const val NOT_READABLE_ERR = 4
+ const val ENCODING_ERR = 5
+ const val NO_MODIFICATION_ALLOWED_ERR = 6
+ const val INVALID_STATE_ERR = 7
+ const val SYNTAX_ERR = 8
+ const val INVALID_MODIFICATION_ERR = 9
+ const val QUOTA_EXCEEDED_ERR = 10
+ const val TYPE_MISMATCH_ERR = 11
+ const val PATH_EXISTS_ERR = 12
+
+ /**
+ * File system types.
+ */
+ enum class FileSystemType {
+ TEMPORARY,
+ PERSISTENT
+ }
+
+ /**
+ * Get system directories.
+ */
+ suspend fun getSystemDirectories(): SystemDirs {
+ return suspendCoroutine { continuation ->
+ addDeviceReadyListener {
+ continuation.resume(window.asDynamic().cordova.file)
+ }
+ }
+ }
+
+ /**
+ * Resolve given path to a file or directory entry.
+ * @param url directory path
+ */
+ @Suppress("UnsafeCastFromDynamic")
+ suspend fun resolveLocalFileSystemURL(url: String): Result<Entry, FileException> {
+ return suspendCoroutine { continuation ->
+ addDeviceReadyListener {
+ window.asDynamic().resolveLocalFileSystemURL(url, { entry ->
+ continuation.resume(Result.Success(entry))
+ }, { error ->
+ continuation.resume(Result.error(FileException(error.code)))
+ })
+ }
+ }
+ }
+
+ /**
+ * Resolve given path to a directory entry.
+ * @param url directory path
+ */
+ @Suppress("UnsafeCastFromDynamic")
+ suspend fun resolveLocalFileSystemURLForDir(url: String): Result<DirectoryEntry, FileException> {
+ return when (val result = resolveLocalFileSystemURL(url)) {
+ is Result.Success -> {
+ if (result.value.isDirectory) {
+ result.map {
+ @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
+ it as DirectoryEntry
+ }
+ } else {
+ Result.error(FileException(TYPE_MISMATCH_ERR))
+ }
+ }
+ is Result.Failure -> result
+ }
+ }
+
+ /**
+ * Resolve given path to a file entry.
+ * @param url file path
+ */
+ @Suppress("UnsafeCastFromDynamic")
+ suspend fun resolveLocalFileSystemURLForFile(url: String): Result<FileEntry, FileException> {
+ return when (val result = resolveLocalFileSystemURL(url)) {
+ is Result.Success -> {
+ if (result.value.isFile) {
+ result.map {
+ @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
+ it as FileEntry
+ }
+ } else {
+ Result.error(FileException(TYPE_MISMATCH_ERR))
+ }
+ }
+ is Result.Failure -> result
+ }
+ }
+
+ /**
+ * Request a file system of a given type.
+ * @param fileSystemType file system type (temporary or persistent)
+ * @param size file system size (quota)
+ */
+ @Suppress("UnsafeCastFromDynamic")
+ suspend fun requestFileSystem(
+ fileSystemType: File.FileSystemType,
+ size: Long = 0
+ ): Result<FileSystem, FileException> {
+ return suspendCoroutine { continuation ->
+ val type = when (fileSystemType) {
+ File.FileSystemType.TEMPORARY -> LocalFileSystem.TEMPORARY
+ File.FileSystemType.PERSISTENT -> LocalFileSystem.PERSISTENT
+ }
+ addDeviceReadyListener {
+ window.asDynamic().requestFileSystem(type, size, { fs ->
+ continuation.resume(Result.Success(fs))
+ }, { error ->
+ continuation.resume(Result.error(FileException(error.code)))
+ })
+ }
+ }
+ }
+
+ internal fun dataURLtoBlob(dataURI: String): Blob {
+ val byteString = window.atob(dataURI.split(',')[1])
+ val mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
+ val ab = ArrayBuffer(byteString.length)
+ val ia = Uint8Array(ab)
+ for (i in 0 until byteString.length) {
+ ia[i] = byteString[i].toByte()
+ }
+ return Blob(arrayOf(ab), BlobPropertyBag(type = mimeString))
+ }
+
+}
+
+/**
+ * Extension function to convert String to a directory entry.
+ */
+suspend fun String.toDirectoryEntry(): Result<DirectoryEntry, FileException> {
+ return File.resolveLocalFileSystemURLForDir(this)
+}
+
+/**
+ * Extension function to convert String to a file entry.
+ */
+suspend fun String.toFileEntry(): Result<FileEntry, FileException> {
+ return File.resolveLocalFileSystemURLForFile(this)
+}
+
+/**
+ * Get file or directory metadata.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun Entry.getMetadata(): Result<Metadata, FileException> {
+ return suspendCoroutine { continuation ->
+ this.getMetadata({ metadata: Metadata ->
+ continuation.resume(Result.success(metadata))
+ } as MetadataCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Get file or directory parent directory entry.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun Entry.getParent(): Result<DirectoryEntry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.getParent({ directoryEntry: DirectoryEntry ->
+ continuation.resume(Result.success(directoryEntry))
+ } as DirectoryEntryCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Remove given file or directory.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun Entry.remove(): Result<Entry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.remove({
+ continuation.resume(Result.success(this))
+ } as VoidCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Move given file or directory to a new location.
+ * @param parent new location parent directory entry
+ * @param newName new location name
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun Entry.moveTo(parent: DirectoryEntry, newName: String? = null): Result<Entry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.moveTo(parent, newName, { entry: Entry ->
+ continuation.resume(Result.success(entry))
+ } as EntryCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Copy given file or directory to a new location.
+ * @param parent new location parent directory entry
+ * @param newName new location name
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun Entry.copyTo(parent: DirectoryEntry, newName: String? = null): Result<Entry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.copyTo(parent, newName, { entry: Entry ->
+ continuation.resume(Result.success(entry))
+ } as EntryCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Create a FileWriter object for a given file entry.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun FileEntry.createWriter(): Result<FileWriter, FileException> {
+ return suspendCoroutine { continuation ->
+ this.createWriter({ fileWriter: FileWriter ->
+ continuation.resume(Result.success(fileWriter))
+ } as FileWriterCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Get a File object for a given file entry.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun FileEntry.file(): Result<org.w3c.files.File, FileException> {
+ return suspendCoroutine { continuation ->
+ this.file({ file: org.w3c.files.File ->
+ continuation.resume(Result.success(file))
+ } as FileCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Get or create a file in a given parent directory.
+ * @param path target file path
+ * @param create Used to indicate that the user wants to create a file if it was not previously there.
+ * @param exclusive Fail if the target path file already exists.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun DirectoryEntry.getFile(
+ path: String,
+ create: Boolean = true,
+ exclusive: Boolean = false
+): Result<FileEntry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.getFile(path, obj {
+ this.create = create
+ this.exclusive = exclusive
+ }, { fileEntry: FileEntry ->
+ continuation.resume(Result.success(fileEntry))
+ } as FileEntryCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Get or create a directory in a given parent directory.
+ * @param path target directory path
+ * @param create Used to indicate that the user wants to create a directory if it was not previously there.
+ * @param exclusive Fail if the target path directory already exists.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun DirectoryEntry.getDirectory(
+ path: String,
+ create: Boolean = true,
+ exclusive: Boolean = false
+): Result<DirectoryEntry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.getDirectory(path, obj {
+ this.create = create
+ this.exclusive = exclusive
+ }, { directoryEntry: DirectoryEntry ->
+ continuation.resume(Result.success(directoryEntry))
+ } as DirectoryEntryCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * Remove given directory recursively.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun DirectoryEntry.removeRecursively(): Result<DirectoryEntry, FileException> {
+ return suspendCoroutine { continuation ->
+ this.removeRecursively({
+ continuation.resume(Result.success(this))
+ } as VoidCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * List directory entries for a given DirectoryReader.
+ */
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic")
+suspend fun DirectoryReader.readEntries(): Result<List<Entry>, FileException> {
+ return suspendCoroutine { continuation ->
+ this.readEntries({ entries: Array<Entry> ->
+ continuation.resume(Result.success(entries.toList()))
+ } as EntriesCallback, { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ } as ErrorCallback)
+ }
+}
+
+/**
+ * List directory entries for a given parent directory entry.
+ */
+suspend fun DirectoryEntry.readEntries(): Result<List<Entry>, FileException> {
+ return this.createReader().readEntries()
+}
+
+/**
+ * Read file content as a plain string.
+ */
+@Suppress("UnsafeCastFromDynamic")
+suspend fun FileEntry.readAsText(): Result<String, FileException> {
+ return this.file().flatMap { file ->
+ suspendCoroutine<Result<String, FileException>> { continuation ->
+ val reader = FileReader()
+ reader.onloadend = { e ->
+ continuation.resume(Result.success(e.target.asDynamic().result))
+ }
+ reader.onerror = { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ }
+ reader.readAsText(file)
+ }
+ }
+}
+
+/**
+ * Read file content as a data url.
+ */
+@Suppress("UnsafeCastFromDynamic")
+suspend fun FileEntry.readAsDataURL(): Result<String, FileException> {
+ return this.file().flatMap { file ->
+ suspendCoroutine<Result<String, FileException>> { continuation ->
+ val reader = FileReader()
+ reader.onloadend = { e ->
+ continuation.resume(Result.success(e.target.asDynamic().result))
+ }
+ reader.onerror = { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ }
+ reader.readAsDataURL(file)
+ }
+ }
+}
+
+/**
+ * Read file content as an array buffer.
+ */
+@Suppress("UnsafeCastFromDynamic")
+suspend fun FileEntry.readAsArrayBuffer(): Result<ArrayBuffer, FileException> {
+ return this.file().flatMap { file ->
+ suspendCoroutine<Result<ArrayBuffer, FileException>> { continuation ->
+ val reader = FileReader()
+ reader.onloadend = { e ->
+ continuation.resume(Result.success(e.target.asDynamic().result))
+ }
+ reader.onerror = { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ }
+ reader.onabort = { _ ->
+ continuation.resume(Result.error(FileException(File.ABORT_ERR)))
+ }
+ reader.readAsArrayBuffer(file)
+ }
+ }
+}
+
+/**
+ * Write file content from a Blob.
+ * @param data a data Blob to be written.
+ */
+@Suppress("UnsafeCastFromDynamic")
+suspend fun FileEntry.write(data: Blob): Result<FileEntry, FileException> {
+ return this.createWriter().flatMap { writer ->
+ suspendCoroutine<Result<FileEntry, FileException>> { continuation ->
+ writer.onwriteend = {
+ continuation.resume(Result.success(this))
+ }
+ writer.onerror = { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ }
+ writer.onabort = { _ ->
+ continuation.resume(Result.error(FileException(File.ABORT_ERR)))
+ }
+ writer.write(data)
+ }
+ }
+}
+
+/**
+ * Write file content from a plain string.
+ * @param data a data string to be written.
+ */
+suspend fun FileEntry.write(data: String): Result<FileEntry, FileException> {
+ return this.write(Blob(arrayOf(data)))
+}
+
+/**
+ * Write file content from a data url.
+ * @param dataUrl a data url to be written.
+ */
+suspend fun FileEntry.writeDataUrL(dataUrl: String): Result<FileEntry, FileException> {
+ return this.write(File.dataURLtoBlob(dataUrl))
+}
+
+/**
+ * Write file content from an array buffer.
+ * @param data an array buffer to be written.
+ */
+suspend fun FileEntry.write(data: ArrayBuffer): Result<FileEntry, FileException> {
+ return this.write(data.toBlob())
+}
+
+/**
+ * Append file content from a Blob.
+ * @param data a data Blob to be appended.
+ */
+@Suppress("UnsafeCastFromDynamic")
+suspend fun FileEntry.append(data: Blob): Result<FileEntry, FileException> {
+ return this.createWriter().flatMap { writer ->
+ try {
+ writer.seek(writer.length)
+ } catch (e: Exception) {
+ console.log("File doesn't exist!")
+ }
+ suspendCoroutine<Result<FileEntry, FileException>> { continuation ->
+ writer.onwriteend = {
+ continuation.resume(Result.success(this))
+ }
+ writer.onerror = { error: dynamic ->
+ continuation.resume(Result.error(FileException(error.code)))
+ }
+ writer.onabort = { _ ->
+ continuation.resume(Result.error(FileException(File.ABORT_ERR)))
+ }
+ writer.write(data)
+ }
+ }
+}
+
+/**
+ * Append file content from a plain string.
+ * @param data a string to be appended.
+ */
+suspend fun FileEntry.append(data: String): Result<FileEntry, FileException> {
+ return this.append(Blob(arrayOf(data)))
+}
+
+/**
+ * Convert array buffer to a blob.
+ */
+fun ArrayBuffer.toBlob(): Blob {
+ return Blob(arrayOf(Uint8Array(this)))
+}
diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt
new file mode 100644
index 00000000..48f4f3d2
--- /dev/null
+++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt
@@ -0,0 +1,221 @@
+@file:Suppress(
+ "INTERFACE_WITH_SUPERCLASS",
+ "OVERRIDING_FINAL_MEMBER",
+ "RETURN_TYPE_MISMATCH_ON_OVERRIDE",
+ "CONFLICTING_OVERLOADS",
+ "EXTERNAL_DELEGATION",
+ "NESTED_CLASS_IN_EXTERNAL_INTERFACE",
+ "unused", "PropertyName", "TooManyFunctions", "VariableNaming", "MaxLineLength"
+)
+
+package pl.treksoft.kvision.cordova
+
+import org.w3c.files.File
+import kotlin.js.Date
+
+external object LocalFileSystem {
+ var TEMPORARY: Number
+ var PERSISTENT: Number
+ fun requestFileSystem(
+ type: Number,
+ size: Number,
+ successCallback: FileSystemCallback,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+
+ fun resolveLocalFileSystemURL(
+ url: String,
+ successCallback: EntryCallback,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+
+ fun webkitRequestFileSystem(
+ type: Number,
+ size: Number,
+ successCallback: FileSystemCallback,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+}
+
+external object LocalFileSystemSync {
+ var TEMPORARY: Number
+ var PERSISTENT: Number
+ fun requestFileSystemSync(type: Number, size: Number): FileSystemSync
+ fun resolveLocalFileSystemSyncURL(url: String): EntrySync
+ fun webkitRequestFileSystemSync(type: Number, size: Number): FileSystemSync
+}
+
+external interface Metadata {
+ var modificationTime: Date
+ var size: Number
+}
+
+external interface Flags {
+ var create: Boolean? get() = definedExternally; set(value) = definedExternally
+ var exclusive: Boolean? get() = definedExternally; set(value) = definedExternally
+}
+
+external interface FileSystem {
+ var name: String
+ var root: DirectoryEntry
+}
+
+external interface Entry {
+ var isFile: Boolean
+ var isDirectory: Boolean
+ fun getMetadata(successCallback: MetadataCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+ var name: String
+ var fullPath: String
+ var filesystem: FileSystem
+ fun moveTo(
+ parent: DirectoryEntry,
+ newName: String? = definedExternally /* null */,
+ successCallback: EntryCallback? = definedExternally /* null */,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+
+ fun copyTo(
+ parent: DirectoryEntry,
+ newName: String? = definedExternally /* null */,
+ successCallback: EntryCallback? = definedExternally /* null */,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+
+ fun toURL(): String
+ fun toInternalURL(): String
+ fun remove(successCallback: VoidCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+ fun getParent(successCallback: DirectoryEntryCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+}
+
+external interface DirectoryEntry : Entry {
+ fun createReader(): DirectoryReader
+ fun getFile(
+ path: String,
+ options: Flags? = definedExternally /* null */,
+ successCallback: FileEntryCallback?,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+
+ fun getDirectory(
+ path: String,
+ options: Flags? = definedExternally /* null */,
+ successCallback: DirectoryEntryCallback?,
+ errorCallback: ErrorCallback? = definedExternally /* null */
+ )
+
+ fun removeRecursively(successCallback: VoidCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+}
+
+external interface DirectoryReader {
+ fun readEntries(successCallback: EntriesCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+}
+
+external interface FileEntry : Entry {
+ fun createWriter(successCallback: FileWriterCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+ fun file(successCallback: FileCallback, errorCallback: ErrorCallback? = definedExternally /* null */)
+}
+
+external interface FileSystemCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun FileSystemCallback.invoke(filesystem: FileSystem) {
+ asDynamic()(filesystem)
+}
+
+external interface EntryCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun EntryCallback.invoke(entry: Entry) {
+ asDynamic()(entry)
+}
+
+external interface FileEntryCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun FileEntryCallback.invoke(entry: FileEntry) {
+ asDynamic()(entry)
+}
+
+external interface DirectoryEntryCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun DirectoryEntryCallback.invoke(entry: DirectoryEntry) {
+ asDynamic()(entry)
+}
+
+external interface EntriesCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun EntriesCallback.invoke(entries: Array<Entry>) {
+ asDynamic()(entries)
+}
+
+external interface MetadataCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun MetadataCallback.invoke(metadata: Metadata) {
+ asDynamic()(metadata)
+}
+
+external interface FileWriterCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun FileWriterCallback.invoke(fileWriter: FileWriter) {
+ asDynamic()(fileWriter)
+}
+
+external interface FileCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun FileCallback.invoke(file: File) {
+ asDynamic()(file)
+}
+
+external interface VoidCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun VoidCallback.invoke() {
+ asDynamic()()
+}
+
+external interface ErrorCallback
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun ErrorCallback.invoke(err: Error) {
+ asDynamic()(err)
+}
+
+external interface FileSystemSync {
+ var name: String
+ var root: DirectoryEntrySync
+}
+
+external interface EntrySync {
+ var isFile: Boolean
+ var isDirectory: Boolean
+ fun getMetadata(): Metadata
+ var name: String
+ var fullPath: String
+ var filesystem: FileSystemSync
+ fun moveTo(parent: DirectoryEntrySync, newName: String? = definedExternally /* null */): EntrySync
+ fun copyTo(parent: DirectoryEntrySync, newName: String? = definedExternally /* null */): EntrySync
+ fun toURL(): String
+ fun remove()
+ fun getParent(): DirectoryEntrySync
+}
+
+external interface DirectoryEntrySync : EntrySync {
+ fun createReader(): DirectoryReaderSync
+ fun getFile(path: String, options: Flags? = definedExternally /* null */): FileEntrySync
+ fun getDirectory(path: String, options: Flags? = definedExternally /* null */): DirectoryEntrySync
+ fun removeRecursively()
+}
+
+external interface DirectoryReaderSync {
+ fun readEntries(): Array<EntrySync>
+}
+
+external interface FileEntrySync : EntrySync {
+ fun createWriter(): FileWriterSync
+ fun file(): File
+}
diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt
new file mode 100644
index 00000000..26215aa8
--- /dev/null
+++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt
@@ -0,0 +1,47 @@
+@file:Suppress(
+ "INTERFACE_WITH_SUPERCLASS",
+ "OVERRIDING_FINAL_MEMBER",
+ "RETURN_TYPE_MISMATCH_ON_OVERRIDE",
+ "CONFLICTING_OVERLOADS",
+ "EXTERNAL_DELEGATION",
+ "NESTED_CLASS_IN_EXTERNAL_INTERFACE",
+ "unused", "PropertyName", "TooManyFunctions", "VariableNaming", "MaxLineLength"
+)
+
+package pl.treksoft.kvision.cordova
+
+import org.w3c.dom.events.EventTarget
+import org.w3c.files.Blob
+import org.w3c.xhr.ProgressEvent
+
+external interface FileSaver : EventTarget {
+ fun abort()
+ var INIT: Number
+ var WRITING: Number
+ var DONE: Number
+ var readyState: Number
+ var error: Error
+ var onwritestart: (event: ProgressEvent) -> Unit
+ var onprogress: (event: ProgressEvent) -> Unit
+ var onwrite: (event: ProgressEvent) -> Unit
+ var onabort: (event: ProgressEvent) -> Unit
+ var onerror: (event: ProgressEvent) -> Unit
+ var onwriteend: (event: ProgressEvent) -> Unit
+
+}
+
+external interface FileWriter : FileSaver {
+ var position: Number
+ var length: Number
+ fun write(data: Blob)
+ fun seek(offset: Number)
+ fun truncate(size: Number)
+}
+
+external interface FileWriterSync {
+ var position: Number
+ var length: Number
+ fun write(data: Blob)
+ fun seek(offset: Number)
+ fun truncate(size: Number)
+}
diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt
index bb125e24..83a1987e 100644
--- a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt
+++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt
@@ -1,3 +1,6 @@
+@file:Suppress(
+ "TooManyFunctions", "TooGenericExceptionCaught"
+)
/*
* Source: https://github.com/kittinunf/Result
*
@@ -31,9 +34,9 @@ inline fun <reified X> Result<*, *>.getAs() = when (this) {
is Result.Failure -> error as? X
}
-fun <V : Any> Result<V, *>.success(f: (V) -> Unit) = fold(f, {})
+inline fun <V : Any> Result<V, *>.success(f: (V) -> Unit) = fold(f, {})
-fun <E : Exception> Result<*, E>.failure(f: (E) -> Unit) = fold({}, f)
+inline fun <E : Exception> Result<*, E>.failure(f: (E) -> Unit) = fold({}, f)
infix fun <V : Any, E : Exception> Result<V, E>.or(fallback: V) = when (this) {
is Result.Success -> this