diff --git a/api/kweb-core.api b/api/kweb-core.api index 0d340042b..9fecdf435 100644 --- a/api/kweb-core.api +++ b/api/kweb-core.api @@ -1203,20 +1203,24 @@ public final class kweb/html/events/KeyboardEvent$Companion { public final class kweb/html/events/MouseEvent { public static final field Companion Lkweb/html/events/MouseEvent$Companion; - public synthetic fun (ILjava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIZFFLkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIZFFLkotlinx/serialization/json/JsonElement;)V - public synthetic fun (Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIZFFLkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (ILjava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIFFFFZFFLkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIFFFFZFFLkotlinx/serialization/json/JsonElement;)V + public synthetic fun (Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIFFFFZFFLkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component10 ()Ljava/lang/Integer; public final fun component11 ()Ljava/lang/Integer; public final fun component12 ()Ljava/lang/String; public final fun component13 ()I public final fun component14 ()I - public final fun component15 ()Z + public final fun component15 ()F public final fun component16 ()F public final fun component17 ()F - public final fun component18 ()Lkotlinx/serialization/json/JsonElement; + public final fun component18 ()F + public final fun component19 ()Z public final fun component2 ()J + public final fun component20 ()F + public final fun component21 ()F + public final fun component22 ()Lkotlinx/serialization/json/JsonElement; public final fun component3 ()Z public final fun component4 ()I public final fun component5 ()I @@ -1224,8 +1228,8 @@ public final class kweb/html/events/MouseEvent { public final fun component7 ()F public final fun component8 ()Z public final fun component9 ()Z - public final fun copy (Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIZFFLkotlinx/serialization/json/JsonElement;)Lkweb/html/events/MouseEvent; - public static synthetic fun copy$default (Lkweb/html/events/MouseEvent;Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIZFFLkotlinx/serialization/json/JsonElement;ILjava/lang/Object;)Lkweb/html/events/MouseEvent; + public final fun copy (Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIFFFFZFFLkotlinx/serialization/json/JsonElement;)Lkweb/html/events/MouseEvent; + public static synthetic fun copy$default (Lkweb/html/events/MouseEvent;Ljava/lang/String;JZIIFFZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;IIFFFFZFFLkotlinx/serialization/json/JsonElement;ILjava/lang/Object;)Lkweb/html/events/MouseEvent; public fun equals (Ljava/lang/Object;)Z public final fun getAltKey ()Z public final fun getButton ()I @@ -1237,6 +1241,10 @@ public final class kweb/html/events/MouseEvent { public final fun getMetaKey ()Z public final fun getMovementX ()Ljava/lang/Integer; public final fun getMovementY ()Ljava/lang/Integer; + public final fun getOffsetX ()F + public final fun getOffsetY ()F + public final fun getPageX ()F + public final fun getPageY ()F public final fun getRegion ()Ljava/lang/String; public final fun getRetrieved ()Lkotlinx/serialization/json/JsonElement; public final fun getScreenX ()I @@ -1737,6 +1745,24 @@ public final class kweb/plugins/fomanticUI/JqueryKt { public static final fun getSUI (Lkweb/plugins/jqueryCore/JQueryReceiver;)Lkweb/plugins/fomanticUI/JQuerySUIReceiver; } +public final class kweb/plugins/image/DynamicImagePlugin : kweb/plugins/KwebPlugin { + public fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/util/Map;)V + public fun (Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;)V + public fun appServerConfigurator (Lio/ktor/server/routing/Routing;)V +} + +public final class kweb/plugins/image/DynamicImagePluginKt { + public static final fun toByteArray (Ljava/awt/image/BufferedImage;Ljava/lang/String;)[B +} + +public final class kweb/plugins/javascript/JavascriptPlugin : kweb/plugins/KwebPlugin { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/util/Set;)V + public fun decorate (Lorg/jsoup/nodes/Document;)V +} + public final class kweb/plugins/jqueryCore/JQueryCorePlugin : kweb/plugins/KwebPlugin { public fun ()V public fun decorate (Lorg/jsoup/nodes/Document;)V diff --git a/docs/src/js.md b/docs/src/js.md index 365004182..d1fd79026 100644 --- a/docs/src/js.md +++ b/docs/src/js.md @@ -48,3 +48,34 @@ Note that the last line of the `jsBody` parameter must be a `return` statement: `callJsFunctionWithResult()` is a suspend function so it must be called inside a [CoroutineScope](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/). You can create one within Kweb's DSL using **elementScope()**. This scope will be cancelled automatically when this part of the DOM is no-longer needed. + +## Including static JavaScript files + +The [JavascriptPlugin](https://docs.kweb.io/api/kweb-core/kweb.plugins.javascript/-javascript-plugin/index.html) can be +used to conveniently add multiple static JavaScript files to your website, just add it to your resources folder as +follows: + +``` +├── src +│ └─── main +│ └─── resources +│ └─── script +│ └── test.js +``` + +Next add the plugin via the [plugins](https://docs.kweb.io/api/kweb-core/kweb/-kweb/index.html) constructor +parameter. + +```kotlin +{{#include ../../src/test/kotlin/kweb/docs/style.kt:JavascriptPlugin}} +``` + +Specify the relative path to the folder inside src/main/resources +and the files to include (either a single file name or a list of file names). + +The files will be served under /kweb_static/js and linked from the websites +HTML head tag, for example: + +```html + +``` diff --git a/src/main/kotlin/kweb/html/events/events.kt b/src/main/kotlin/kweb/html/events/events.kt index 1fb0f6d62..975dc4030 100644 --- a/src/main/kotlin/kweb/html/events/events.kt +++ b/src/main/kotlin/kweb/html/events/events.kt @@ -53,6 +53,10 @@ data class MouseEvent( val region: String? = null, val screenX: Int, val screenY: Int, + val pageX: Float, + val pageY: Float, + val offsetX: Float, + val offsetY: Float, val shiftKey: Boolean, val x: Float = clientX, val y: Float = clientY, diff --git a/src/main/kotlin/kweb/plugins/image/DynamicImagePlugin.kt b/src/main/kotlin/kweb/plugins/image/DynamicImagePlugin.kt new file mode 100644 index 000000000..c451d3519 --- /dev/null +++ b/src/main/kotlin/kweb/plugins/image/DynamicImagePlugin.kt @@ -0,0 +1,48 @@ +package kweb.plugins.image + +import io.ktor.server.response.* +import io.ktor.server.routing.* +import kweb.plugins.KwebPlugin +import java.awt.image.BufferedImage +import java.io.ByteArrayOutputStream +import javax.imageio.ImageIO + +/** + * Add multiple dynamically generated images files to your Kweb app routing using ByteArray generator functions. + * + * @property resourceFolder The relative logical path used for routing to the dynamic images. + * @property fileNames The map of file names to ByteArray providers, called once every time the image is requested. + * + * @author toddharrison + */ +class DynamicImagePlugin( + private val resourceFolder: String, + private val fileNames: Map ByteArray> +): KwebPlugin() { + constructor(resourceFolder: String, fileName: String, byteProvider: suspend () -> ByteArray): + this(resourceFolder, mapOf(fileName to byteProvider)) + + constructor(resourceFolder: String, fileNames: Map BufferedImage>, format: String): + this(resourceFolder, fileNames + .mapValues { (_, value) -> suspend { toByteArray(value(), format) }}) + + constructor(resourceFolder: String, fileName: String, bufferedImageProvider: suspend () -> BufferedImage, format: String): + this(resourceFolder, mapOf(fileName to bufferedImageProvider), format) + + override fun appServerConfigurator(routeHandler: Routing) { + fileNames.forEach { (fileName, byteProvider) -> + routeHandler.get("$resourceFolder/$fileName") { _ -> + context.respondBytes { + byteProvider() + } + } + } + } +} + +fun toByteArray(bufferedImage: BufferedImage, format: String): ByteArray { + val byteArrayOutputStream = ByteArrayOutputStream() + ImageIO.write(bufferedImage, format, byteArrayOutputStream) + return byteArrayOutputStream.toByteArray() +} diff --git a/src/main/kotlin/kweb/plugins/javascript/JavascriptPlugin.kt b/src/main/kotlin/kweb/plugins/javascript/JavascriptPlugin.kt new file mode 100644 index 000000000..c59b83626 --- /dev/null +++ b/src/main/kotlin/kweb/plugins/javascript/JavascriptPlugin.kt @@ -0,0 +1,26 @@ +package kweb.plugins.javascript + +import kweb.plugins.KwebPlugin +import kweb.plugins.staticFiles.ResourceFolder +import kweb.plugins.staticFiles.StaticFilesPlugin +import org.jsoup.nodes.Document + +/** + * Add multiple JavaScript files to your Kweb app from your resources folder. + * + * @property resourceFolder The relative path to the folder in the src/main/resources folder where the .js files are located + * @property fileNames The list of file names located in the resourceFolder which should be added to the website. Only files with suffix .js (case-insensitive) will be linked + * + * @author toddharrison + */ +class JavascriptPlugin(private val resourceFolder: String, private val fileNames: Set) : KwebPlugin(dependsOn = setOf(StaticFilesPlugin(ResourceFolder(resourceFolder), "/kweb_static/js"))) { + constructor(resourceFolder: String, fileName: String) : this(resourceFolder, setOf(fileName)) + + override fun decorate(doc: Document) { + fileNames.filter { f -> f.endsWith(".js", true) }.forEach { + doc.head().appendElement("script") + .attr("type", "text/javascript") + .attr("src", "/kweb_static/js/$it") + } + } +} diff --git a/src/test/kotlin/kweb/docs/style.kt b/src/test/kotlin/kweb/docs/style.kt index 3ff821c24..78e7d5ead 100644 --- a/src/test/kotlin/kweb/docs/style.kt +++ b/src/test/kotlin/kweb/docs/style.kt @@ -5,6 +5,7 @@ import kweb.plugins.css.CSSPlugin // ANCHOR: fomanticUIPlugin import kweb.* import kweb.plugins.fomanticUI.* +import kweb.plugins.javascript.JavascriptPlugin fun main() { Kweb(port = 16097, plugins = listOf(fomanticUIPlugin)) { @@ -34,3 +35,11 @@ Kweb(port = 16097, plugins = listOf(CSSPlugin("css", "test.css"))) { } // ANCHOR_END: CSSPlugin } + +fun main4() { + // ANCHOR: JavascriptPlugin +Kweb(port = 16097, plugins = listOf(JavascriptPlugin("script", "test.js"))) { + // ... +} + // ANCHOR_END: JavascriptPlugin +}