-
-
Save fuzzyweapon/00469c3b02c9424b397deeb14044af17 to your computer and use it in GitHub Desktop.
/* | |
* Copyright 2018 Nicholas Bilyk | |
* | |
* 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 com.acornui.js.file | |
import com.acornui.async.Promise | |
import com.acornui.async.launch | |
import com.acornui.file.FileIoManager | |
import com.acornui.file.FileReader | |
import com.acornui.io.NativeBuffer | |
import com.acornui.js.time.setTimeout | |
import org.w3c.dom.HTMLElement | |
import org.w3c.dom.HTMLInputElement | |
import org.w3c.dom.Node | |
import org.w3c.dom.css.CSSStyleDeclaration | |
import org.w3c.dom.events.Event | |
import org.w3c.files.File | |
import org.w3c.files.get | |
import org.w3c.files.FileReader as JsFileApiReader | |
import kotlin.browser.document | |
import kotlin.browser.window | |
class JsFileIoManager : FileIoManager { | |
// TODO: Test to see if we can lateinit this | |
private var filePicker: HTMLInputElement? = null | |
private var files: List<FileReader>? = null | |
private val clickHandler = { _: Event -> | |
val fileList = filePicker?.files | |
files = if (fileList == null || fileList.length == 0) { | |
null | |
} else { | |
var tempList = mutableListOf<FileReader>() | |
for (i in 0..fileList.length - 1) { | |
tempList.add(JsFileReader(fileList[i] ?: continue)) | |
} | |
tempList | |
} | |
} | |
private fun createFilePicker(multipleFiles: Boolean): HTMLInputElement { | |
val newFilePicker = document.createElement("input") as HTMLInputElement | |
newFilePicker.type = "file" | |
// Required for iOS Safari | |
newFilePicker.setAttribute("style", "width: 0px; height: 0px; overflow: hidden;") | |
newFilePicker.style.visibility = "hidden" | |
newFilePicker.multiple = multipleFiles | |
newFilePicker.id = "test" | |
newFilePicker.onclick = null | |
newFilePicker.onchange = clickHandler | |
return newFilePicker | |
} | |
private fun getFileReaders(extensions: List<String>, defaultPath: String, multipleFiles: Boolean = false): List<FileReader>? { | |
// TODO: Check to see if extensions limits what types of files can be pulled in or if it filters to that + all and mimic functionality | |
// Ensure there is a body to append to in order to preserve browser support when using contains() | |
if (document.body == null) { | |
document.createElement("body").also { document.appendChild(it) } | |
} | |
val body = document.body as HTMLElement | |
filePicker = createFilePicker(multipleFiles).also { body.appendChild(it) } | |
filePicker?.click() | |
body.removeChild(filePicker!!) | |
return files | |
} | |
override fun pickFileForOpen(extensions: List<String>, defaultPath: String): FileReader? { | |
return getFileReaders(extensions, defaultPath)?.get(0) | |
} | |
override fun pickFilesForOpen(extensions: List<String>, defaultPath: String): List<FileReader>? { | |
return getFileReaders(extensions, defaultPath, true) | |
} | |
} | |
class JsFileReader(private val file: File) : FileReader { | |
override val name: String = file.name | |
override val size: Int = file.size | |
override val lastModified: Int = file.lastModified | |
private val reader: JsFileApiReader = JsFileApiReader() | |
private var contents: Promise<String> = getContentsPromise() | |
get() = getContentsPromise() // Just to compile for now. | |
private fun getContentsPromise() = | |
object : Promise<String>() { | |
init { | |
launch { | |
reader.onload = { _: Event -> | |
success(reader.result as String) | |
} | |
reader.onerror = { _: Event -> | |
val error: dynamic = reader.error | |
var msg: String = when(error.code) { | |
error.ENCODING_ERR -> "Encoding error" | |
error.NOT_FOUND_ERR -> "File not found" | |
error.NOT_READABLE_ERR -> "File could not be read" | |
error.SECURITY_ERR -> "File has a security issue" | |
else -> "File cannot be opened due to the following error" | |
} | |
msg = "$msg: ${error.code}" | |
fail(Exception(msg)) | |
} | |
} | |
} | |
} | |
override suspend fun readAsString(): String? { | |
reader.readAsText(file) | |
return contents.await() | |
} | |
override suspend fun readAsBinary(): NativeBuffer<Byte>? { | |
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | |
} | |
override suspend fun saveToFileAsString(extension: String, defaultPath: String, value: String): Boolean { | |
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | |
} | |
override suspend fun saveToFileAsBinary(extension: String, defaultPath: String, value: NativeBuffer<Byte>): Boolean { | |
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | |
} | |
} |
21 - rename to canvasClickHandler
32 - canvas.onmousedown Never use the on___ setters for elements you didn't create. Always use addEventListener. E.g. canvas.addEventListener("mousedown", canvasMouseDownHandler)
16 - why stop propagation?
Yeah, I'm going to remove that self init. That's something I don't like. Removes explicit control if something goes wrong.
21 - will do
32 - will do
16 - I just didn't think there was any need to continue propagating (but this very well could be totally ignorant)
30 - rename to pickFileForRead
add to interface
return a FileReader interface (when you get to it)
3 - don't use lateinit in that way. lateinit is only for if there's an initialization step separate from the constructor.
gist updated
updated - major changes, now the promise object created more closely resembles the kind of interface it is fulfilling (which is better for picking multiple files) and gets rid of the .getornull call on picking one file. Also, files is initiated with a function which we use also as its getter (so we aren't baking in copy/pasting that code in two places). This fixes the multiple picking bug.
if (document.getElementById("filePicker") == null)
JsFileIoManager(canvas) -- That looks scary.