From 8d0a0e318be5ca155b2cc25aefb3be855c85721c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 28 Jan 2021 19:47:49 +0300 Subject: [PATCH 01/12] Kotlin version to 1.4.30-RC --- build.gradle.kts | 2 +- plotlykt-core/api/plotlykt-core.api | 1 + plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt | 6 +++--- settings.gradle.kts | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 59742912..fc5d95fe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ val githubProject by extra("plotly.kt") allprojects { group = "kscience.plotlykt" - version = "0.3.1-dev-4" + version = "0.3.1-dev-5" repositories { mavenLocal() diff --git a/plotlykt-core/api/plotlykt-core.api b/plotlykt-core/api/plotlykt-core.api index 9972aa79..aa91c40e 100644 --- a/plotlykt-core/api/plotlykt-core.api +++ b/plotlykt-core/api/plotlykt-core.api @@ -55,6 +55,7 @@ public final class kscience/plotly/Plot : hep/dataforge/meta/Configurable, hep/d public fun ()V public fun (Lhep/dataforge/meta/Config;)V public synthetic fun (Lhep/dataforge/meta/Config;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun addTrace (Lkscience/plotly/models/Trace;)V public fun getConfig ()Lhep/dataforge/meta/Config; public final fun getData ()Ljava/util/List; public final fun getLayout ()Lkscience/plotly/models/Layout; diff --git a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt index c8825b6e..79705806 100644 --- a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt +++ b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt @@ -25,7 +25,7 @@ public class Plot( */ public val layout: Layout by config.spec(Layout) - private fun appendTrace(trace: Trace) { + public fun addTrace(trace: Trace) { val traceRoot = trace.rootNode if (traceRoot is Config) { config.append("data", traceRoot) @@ -41,14 +41,14 @@ public class Plot( * Append all traces from [traces] to the plot */ public fun traces(traces: Collection) { - traces.forEach { appendTrace(it) } + traces.forEach { addTrace(it) } } /** * Append all [traces] */ public fun traces(vararg traces: Trace) { - traces.forEach { appendTrace(it) } + traces.forEach { addTrace(it) } } /** diff --git a/settings.gradle.kts b/settings.gradle.kts index b81fcdc7..5071e8b5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { - val kotlinVersion = "1.4.21" - val toolsVersion = "0.7.1" + val kotlinVersion = "1.4.30-RC" + val toolsVersion = "0.7.3-1.4.30-RC" repositories { mavenLocal() From 334aa48dde2042211cb38b1c69684e34888c6a7f Mon Sep 17 00:00:00 2001 From: Artificial Date: Fri, 29 Jan 2021 09:58:04 +0100 Subject: [PATCH 02/12] Fix PlotGrid "grid" getter to have consistent ordering of rows Previous implementation did not sort the map by keys, occasionally causing random row order. New implementation sorts the intermediary map by its keys (rows) in ascending order. --- .../src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt index 63eaf5f8..41e1f442 100644 --- a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt +++ b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt @@ -8,9 +8,12 @@ class PlotGrid { private val cells = HashMap() + /** + * @return Columns in ascending order, grouped by rows in ascending order. + * */ val grid - get() = cells.values.groupBy { it.row }.values.map { - it.sortedBy { plot -> plot.col } + get() = cells.values.groupBy { it.row }.toSortedMap().values.map { + it.sortedBy { cell -> cell.col } }.toList() operator fun get(id: String): PlotCell? = cells[id] From ec919f7bd71bc54abbc8ae6d672032783ab7be8f Mon Sep 17 00:00:00 2001 From: Artificial Date: Fri, 29 Jan 2021 09:58:42 +0100 Subject: [PATCH 03/12] Resolve PlotGrid IDE inspections --- .../src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt index 41e1f442..a042dc39 100644 --- a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt +++ b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotGrid.kt @@ -3,25 +3,25 @@ package kscience.plotly import kotlinx.html.div @UnstablePlotlyAPI -class PlotGrid { - data class PlotCell(val id: String, val plot: Plot, val row: Int, val col: Int, val width: Int = 1) +public class PlotGrid { + public data class PlotCell(val id: String, val plot: Plot, val row: Int, val col: Int, val width: Int = 1) private val cells = HashMap() /** * @return Columns in ascending order, grouped by rows in ascending order. * */ - val grid + public val grid: List> get() = cells.values.groupBy { it.row }.toSortedMap().values.map { it.sortedBy { cell -> cell.col } }.toList() - operator fun get(id: String): PlotCell? = cells[id] + public operator fun get(id: String): PlotCell? = cells[id] private var currentRow = 0 private var currentCol = 0 - fun plot( + public fun plot( plot: Plot, id: String = plot.toString(), width: Int = 6, @@ -42,7 +42,7 @@ class PlotGrid { return plot } - fun plot( + public fun plot( row: Int? = null, col: Int? = null, id: String? = null, @@ -55,7 +55,7 @@ class PlotGrid { } @UnstablePlotlyAPI -fun Plotly.grid(block: PlotGrid.() -> Unit): PlotlyPage { +public fun Plotly.grid(block: PlotGrid.() -> Unit): PlotlyPage { val grid = PlotGrid().apply(block) return page(cdnBootstrap, cdnPlotlyHeader) { container -> div("col") { From 959a5e110717a43b142012e67e0904446790b245 Mon Sep 17 00:00:00 2001 From: Artificial Date: Fri, 29 Jan 2021 17:02:09 +0100 Subject: [PATCH 04/12] Add "table" Plot extension function --- .../src/commonMain/kotlin/kscience/plotly/plotExtensions.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/plotExtensions.kt b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/plotExtensions.kt index c6f82455..5bfaf435 100644 --- a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/plotExtensions.kt +++ b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/plotExtensions.kt @@ -63,6 +63,12 @@ public inline fun Plot.bar(block: Bar.() -> Unit): Bar { return trace } +public inline fun Plot.table(block: Table.() -> Unit): Table { + val trace = Table(block) + traces(trace) + return trace +} + public fun Plot.text(block: Text.() -> Unit) { layout.annotation(block) } From c7e34faad3164644c6b612bf9bbc254749d145e3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 29 Jan 2021 19:17:30 +0300 Subject: [PATCH 05/12] update API --- plotlykt-core/api/plotlykt-core.api | 1 + 1 file changed, 1 insertion(+) diff --git a/plotlykt-core/api/plotlykt-core.api b/plotlykt-core/api/plotlykt-core.api index aa91c40e..bbf9a60c 100644 --- a/plotlykt-core/api/plotlykt-core.api +++ b/plotlykt-core/api/plotlykt-core.api @@ -75,6 +75,7 @@ public final class kscience/plotly/PlotExtensionsKt { public static final fun pie (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)Lkscience/plotly/models/Pie; public static final fun scatter (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)Lkscience/plotly/models/Scatter; public static final fun shape (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)V + public static final fun table (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)Lkscience/plotly/models/Table; public static final fun text (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)V public static final fun violin (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)Lkscience/plotly/models/Violin; } From 39f6668049faf70b16fecd48e65b8f2da40fa231 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 22:29:41 +0300 Subject: [PATCH 06/12] Upgrade to DF 0.3.0 --- build.gradle.kts | 2 +- examples/src/main/kotlin/io/ioUtils.kt | 6 ++--- examples/src/main/kotlin/latexLabels.kt | 24 +------------------ .../src/main/kotlin/tutorials/SinusPicture.kt | 1 + .../commonMain/kotlin/kscience/plotly/Plot.kt | 1 + .../kotlin/kscience/plotly/dfExt.kt | 1 + .../kotlin/kscience/plotly/plotlyHeaders.kt | 4 ++-- settings.gradle.kts | 4 ++-- 8 files changed, 12 insertions(+), 31 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fc5d95fe..3eea3a16 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } val ktorVersion by extra("1.5.0") -val dataforgeVersion by extra("0.3.0-dev-1") +val dataforgeVersion by extra("0.3.0") val htmlVersion by extra("0.7.2") val bintrayRepo by extra("kscience") diff --git a/examples/src/main/kotlin/io/ioUtils.kt b/examples/src/main/kotlin/io/ioUtils.kt index d19b3fb6..604cfc81 100644 --- a/examples/src/main/kotlin/io/ioUtils.kt +++ b/examples/src/main/kotlin/io/ioUtils.kt @@ -4,9 +4,9 @@ import krangl.DataFrame import krangl.readCSV import kscience.plotly.Plotly -@OptIn(ExperimentalStdlibApi::class) fun readResourceAsString(resource: String): String = - Plotly.javaClass.getResourceAsStream(resource).readAllBytes().decodeToString() + Plotly.javaClass.getResourceAsStream(resource)?.readAllBytes()?.decodeToString() + ?: error("Resource $resource not found") fun readResourceAsCsv(resource: String): DataFrame = - DataFrame.readCSV(Plotly.javaClass.getResource(resource).file.toString()) \ No newline at end of file + DataFrame.readCSV(Plotly.javaClass.getResource(resource)?.file?.toString() ?: error("Resource $resource not found")) \ No newline at end of file diff --git a/examples/src/main/kotlin/latexLabels.kt b/examples/src/main/kotlin/latexLabels.kt index c4898d5e..233c1565 100644 --- a/examples/src/main/kotlin/latexLabels.kt +++ b/examples/src/main/kotlin/latexLabels.kt @@ -1,29 +1,7 @@ -import kotlinx.html.script -import kotlinx.html.unsafe import kscience.plotly.* -val customMathJaxHeader = HtmlFragment { - script { - type = "text/x-mathjax-config" - unsafe { - //language=JavaScript - +""" - MathJax.Hub.Config({ - tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]} - }); - """ - } - } - script { - type = "text/javascript" - async = true - src = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_SVG" - } -} - - fun main() { - Plotly.page(customMathJaxHeader, cdnPlotlyHeader) { + Plotly.page(mathJaxHeader, cdnPlotlyHeader) { plot { scatter { x(2, 3, 4, 5) diff --git a/examples/src/main/kotlin/tutorials/SinusPicture.kt b/examples/src/main/kotlin/tutorials/SinusPicture.kt index 2bc48b62..f1abda59 100644 --- a/examples/src/main/kotlin/tutorials/SinusPicture.kt +++ b/examples/src/main/kotlin/tutorials/SinusPicture.kt @@ -17,6 +17,7 @@ import kotlin.math.sin * - Change margins on the plot edges * - Add shapes (vertical lines) */ +@OptIn(UnstablePlotlyAPI::class) fun main() { val div = 200 / PI val sub = PI / 6 diff --git a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt index 79705806..711dcf94 100644 --- a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt +++ b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/Plot.kt @@ -1,6 +1,7 @@ package kscience.plotly import hep.dataforge.meta.* +import hep.dataforge.misc.DFBuilder import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonArray import kotlinx.serialization.json.buildJsonObject diff --git a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/dfExt.kt b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/dfExt.kt index e3da7902..acdbc10e 100644 --- a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/dfExt.kt +++ b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/dfExt.kt @@ -1,6 +1,7 @@ package kscience.plotly import hep.dataforge.meta.* +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.* import hep.dataforge.values.Value import hep.dataforge.values.asValue diff --git a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/plotlyHeaders.kt b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/plotlyHeaders.kt index 145ea8cb..6e594fbe 100644 --- a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/plotlyHeaders.kt +++ b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/plotlyHeaders.kt @@ -11,7 +11,7 @@ internal const val PLOTLY_SCRIPT_PATH = "/js/plotly.min.js" private const val PLOTLY_CDN = "https://cdn.plot.ly/plotly-${Plotly.VERSION}.min.js" //"https://cdnjs.cloudflare.com/ajax/libs/plotly.js/${Plotly.VERSION}/plotly.min.js" -public val cdnPlotlyHeader = HtmlFragment { +public val cdnPlotlyHeader: HtmlFragment = HtmlFragment { script { type = "text/javascript" src = PLOTLY_CDN @@ -53,7 +53,7 @@ internal val systemPlotlyHeader = HtmlFragment { internal val embededPlotlyHeader = HtmlFragment { script { unsafe { - val bytes = HtmlFragment::class.java.getResourceAsStream(PLOTLY_SCRIPT_PATH).readAllBytes() + val bytes = HtmlFragment::class.java.getResourceAsStream(PLOTLY_SCRIPT_PATH)!!.readAllBytes() +bytes.toString(Charsets.UTF_8) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 5071e8b5..599a8999 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { - val kotlinVersion = "1.4.30-RC" - val toolsVersion = "0.7.3-1.4.30-RC" + val kotlinVersion = "1.4.30" + val toolsVersion = "0.7.6" repositories { mavenLocal() From 5fe3097bf61080bae870ea01b67b02ea5bb5b5a7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 22:37:47 +0300 Subject: [PATCH 07/12] Fix for #37 --- .../src/jvmMain/kotlin/kscience/plotly/html.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/html.kt b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/html.kt index f422cfde..14d21e53 100644 --- a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/html.kt +++ b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/html.kt @@ -22,7 +22,7 @@ public operator fun HtmlFragment.plus(other: HtmlFragment): HtmlFragment = HtmlF */ public fun Plot.toHTML( vararg headers: HtmlFragment = arrayOf(cdnPlotlyHeader), - config: PlotlyConfig = PlotlyConfig() + config: PlotlyConfig = PlotlyConfig(), ): String { return createHTML().html { head { @@ -53,7 +53,7 @@ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): } else { //TODO add logging - val bytes = HtmlFragment::class.java.getResourceAsStream(resource).readAllBytes() + val bytes = HtmlFragment::class.java.getResourceAsStream(resource)!!.readAllBytes() Files.createDirectories(fullPath.parent) Files.write(fullPath, bytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) } @@ -71,7 +71,7 @@ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): public fun localScriptHeader( basePath: Path, scriptPath: Path, - resource: String + resource: String, ): HtmlFragment = HtmlFragment { val relativePath = checkOrStoreFile(basePath, scriptPath, resource) script { @@ -85,7 +85,7 @@ public fun localScriptHeader( public fun localCssHeader( basePath: Path, cssPath: Path, - resource: String + resource: String, ): HtmlFragment = HtmlFragment { val relativePath = checkOrStoreFile(basePath, cssPath, resource) link { @@ -94,7 +94,6 @@ public fun localCssHeader( } } -@UnstablePlotlyAPI public val mathJaxHeader: HtmlFragment = HtmlFragment { script { type = "text/x-mathjax-config" @@ -111,5 +110,4 @@ public val mathJaxHeader: HtmlFragment = HtmlFragment { async = true src = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_SVG" } -} - +} \ No newline at end of file From a855e358142526549923c724efc586485e5b27ca Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 23:24:08 +0300 Subject: [PATCH 08/12] Implementation for #4 --- CHANGELOG.md | 3 + examples/src/main/kotlin/tabPageLayout.kt | 57 +++++++++++++++++++ .../kotlin/kscience/plotly/PlotTabs.kt | 56 ++++++++++++++++++ .../kscience/plotly/bootstrapHeaders.kt | 18 +++++- .../resources/js/jquery-3.5.1.slim.min.js | 2 + 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 examples/src/main/kotlin/tabPageLayout.kt create mode 100644 plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotTabs.kt create mode 100644 plotlykt-core/src/jvmMain/resources/js/jquery-3.5.1.slim.min.js diff --git a/CHANGELOG.md b/CHANGELOG.md index cee5dfa6..93477f78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Table widget implementation by @ArtificialPB +- Mathjax header promoted to stable +- Tabbed plots layout (experimental) ### Changed - **Breaking API change!** Trace `text` replaced by `TraceValues` @@ -22,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - https://github.com/mipt-npm/plotly.kt/issues/53 +- Add JQuery to Bootstrap headers ### Security diff --git a/examples/src/main/kotlin/tabPageLayout.kt b/examples/src/main/kotlin/tabPageLayout.kt new file mode 100644 index 00000000..fe3e5681 --- /dev/null +++ b/examples/src/main/kotlin/tabPageLayout.kt @@ -0,0 +1,57 @@ +import kscience.plotly.* +import kscience.plotly.models.Trace +import kscience.plotly.models.invoke +import kscience.plotly.palettes.T10 +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.sin + +@UnstablePlotlyAPI +fun main() { + + val x = (0..100).map { it.toDouble() / 100.0 } + val y1 = x.map { sin(2.0 * PI * it) } + val y2 = x.map { cos(2.0 * PI * it) } + + val trace1 = Trace(x, y1) { + name = "sin" + marker.color(T10.BLUE) + + } + + val trace2 = Trace(x, y2) { + name = "cos" + marker.color(T10.ORANGE) + + } + + val responsive = PlotlyConfig{ + responsive = true + } + + val plot = Plotly.tabs { + + tab("First"){ + plot (config = responsive){ + traces(trace1) + layout { + title = "First graph" + xaxis.title = "x axis name" + xaxis.title = "y axis name" + } + } + } + tab("Second"){ + plot(config = responsive) { + traces(trace2) + layout { + title = "Second graph" + xaxis.title = "x axis name" + xaxis.title = "y axis name" + } + } + } + } + + plot.makeFile() +} diff --git a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotTabs.kt b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotTabs.kt new file mode 100644 index 00000000..ccae62cc --- /dev/null +++ b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/PlotTabs.kt @@ -0,0 +1,56 @@ +package kscience.plotly + +import kotlinx.html.* + +public class PlotTabs { + public data class Tab(val title: String, val id: String, val content: PlotlyFragment) + + private val _tabs = ArrayList() + public val tabs: List get() = _tabs + + public fun tab(title: String, id: String = title, block: FlowContent.(renderer: PlotlyRenderer) -> Unit) { + _tabs.add(Tab(title, id, PlotlyFragment(block))) + } +} + +@UnstablePlotlyAPI +public fun Plotly.tabs(tabsID: String = "tabs", block: PlotTabs.() -> Unit): PlotlyPage { + val grid = PlotTabs().apply(block) + + return page(cdnBootstrap, cdnPlotlyHeader) { container -> + ul("nav nav-tabs") { + role = "tablist" + id = tabsID + grid.tabs.forEachIndexed { index, tab-> + li("nav-item") { + a(classes = "nav-link") { + if(index == 0){ + classes = classes + "active" + } + id = "${tab.id}-tab" + attributes["data-toggle"] = "tab" + href = "#${tab.id}" + role = "tab" + attributes["aria-controls"] = tab.id + attributes["aria-selected"] = "true" + +tab.title + } + } + } + } + div("tab-content") { + id = "$tabsID-content" + grid.tabs.forEachIndexed {index, tab-> + div("tab-pane fade") { + if(index == 0){ + classes = classes + setOf("show", "active") + } + id = tab.id + role = "tabpanel" + attributes["aria-labelledby"] = "${tab.id}-tab" + tab.content.render(this, container) + } + } + } + } +} \ No newline at end of file diff --git a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/bootstrapHeaders.kt b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/bootstrapHeaders.kt index da84a2ba..a98ceee1 100644 --- a/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/bootstrapHeaders.kt +++ b/plotlykt-core/src/jvmMain/kotlin/kscience/plotly/bootstrapHeaders.kt @@ -6,9 +6,18 @@ import java.nio.file.Path private const val bootstrapJsPath = "/js/bootstrap.bundle.min.js" +private const val jQueryPath = "/js/jquery-3.5.1.slim.min.js" private const val bootstrapCssPath = "/css/bootstrap.min.css" -fun localBootstrap(basePath: Path) = HtmlFragment { +public fun localBootstrap(basePath: Path) = HtmlFragment { + script { + type = "text/javascript" + src = checkOrStoreFile( + basePath, + Path.of(assetsDirectory + jQueryPath), + jQueryPath + ).toString() + } script { type = "text/javascript" src = checkOrStoreFile( @@ -27,7 +36,12 @@ fun localBootstrap(basePath: Path) = HtmlFragment { } } -val cdnBootstrap = HtmlFragment { +public val cdnBootstrap: HtmlFragment = HtmlFragment { + script { + src = "https://code.jquery.com/jquery-3.5.1.slim.min.js" + integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" + attributes["crossorigin"] = "anonymous" + } script { src = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" integrity = "sha384-1CmrxMRARb6aLqgBO7yyAxTOQE2AKb9GfXnEo760AUcUmFx3ibVJJAzGytlQcNXd" diff --git a/plotlykt-core/src/jvmMain/resources/js/jquery-3.5.1.slim.min.js b/plotlykt-core/src/jvmMain/resources/js/jquery-3.5.1.slim.min.js new file mode 100644 index 00000000..36b4e1a1 --- /dev/null +++ b/plotlykt-core/src/jvmMain/resources/js/jquery-3.5.1.slim.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(g,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,v=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),m={},b=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},w=g.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function C(e,t,n){var r,i,o=(n=n||w).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function T(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector",E=function(e,t){return new E.fn.init(e,t)};function d(e){var t=!!e&&"length"in e&&e.length,n=T(e);return!b(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+R+")"+R+"*"),U=new RegExp(R+"|>"),V=new RegExp(W),X=new RegExp("^"+B+"$"),Q={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),TAG:new RegExp("^("+B+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+R+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){C()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{O.apply(t=P.call(d.childNodes),d.childNodes),t[d.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){q.apply(e,P.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(C(e),e=e||T,E)){if(11!==d&&(u=Z.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return O.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&p.getElementsByClassName&&e.getElementsByClassName)return O.apply(n,e.getElementsByClassName(i)),n}if(p.qsa&&!k[t+" "]&&(!v||!v.test(t))&&(1!==d||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===d&&(U.test(t)||_.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&p.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=A)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+be(l[o]);c=l.join(",")}try{return O.apply(n,f.querySelectorAll(c)),n}catch(e){k(t,!0)}finally{s===A&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[A]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in p=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},C=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:d;return r!=T&&9===r.nodeType&&r.documentElement&&(a=(T=r).documentElement,E=!i(T),d!=T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),p.scope=ce(function(e){return a.appendChild(e).appendChild(T.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),p.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=J.test(T.getElementsByClassName),p.getById=ce(function(e){return a.appendChild(e).id=A,!T.getElementsByName||!T.getElementsByName(A).length}),p.getById?(x.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=p.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},x.find.CLASS=p.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(p.qsa=J.test(T.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+R+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+A+"-]").length||v.push("~="),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+R+"*name"+R+"*="+R+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+A+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(p.matchesSelector=J.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=J.test(a.compareDocumentPosition),y=t||J.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e==T||e.ownerDocument==d&&y(d,e)?-1:t==T||t.ownerDocument==d&&y(d,t)?1:u?H(u,e)-H(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==T?-1:t==T?1:i?-1:o?1:u?H(u,e)-H(u,t):0;if(i===o)return de(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?de(a[r],s[r]):a[r]==d?-1:s[r]==d?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(C(e),p.matchesSelector&&E&&!k[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){k(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return b(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||L,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:j.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:w,!0)),k.test(r[1])&&E.isPlainObject(t))for(r in t)b(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=w.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this)}).prototype=E.fn,L=E(w);var q=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i;le=w.createDocumentFragment().appendChild(w.createElement("div")),(ce=w.createElement("input")).setAttribute("type","radio"),ce.setAttribute("checked","checked"),ce.setAttribute("name","t"),le.appendChild(ce),m.checkClone=le.cloneNode(!0).cloneNode(!0).lastChild.checked,le.innerHTML="",m.noCloneChecked=!!le.cloneNode(!0).lastChild.defaultValue,le.innerHTML="",m.option=!!le.lastChild;var he={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?E.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var ye=/<|&#?\w+;/;function me(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p\s*$/g;function Le(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function je(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n
",2===ft.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(m.createHTMLDocument?((r=(t=w.implementation.createHTMLDocument("")).createElement("base")).href=w.location.href,t.head.appendChild(r)):t=w),o=!n&&[],(i=k.exec(e))?[t.createElement(i[1])]:(i=me([e],t,o),o&&o.length&&E(o).remove(),E.merge([],i.childNodes)));var r,i,o},E.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=E.css(e,"position"),c=E(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=E.css(e,"top"),u=E.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),b(t)&&(t=t.call(e,n,E.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),i.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-E.css(r,"marginTop",!0),left:t.left-i.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===E.css(e,"position"))e=e.offsetParent;return e||re})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;E.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=Fe(m.pixelPosition,function(e,t){if(t)return t=We(e,n),Ie.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){E.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,i):E.style(e,t,n,i)},s,n?e:void 0,n)}})}),E.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0 Date: Mon, 8 Feb 2021 12:08:32 +0300 Subject: [PATCH 09/12] Implementation for #30 --- CHANGELOG.md | 1 + examples/src/main/kotlin/functionTraces.kt | 35 +++++++++++ .../kscience/plotly/models/fillTrace.kt | 63 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 examples/src/main/kotlin/functionTraces.kt create mode 100644 plotlykt-core/src/commonMain/kotlin/kscience/plotly/models/fillTrace.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 93477f78..1e9b21cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Table widget implementation by @ArtificialPB - Mathjax header promoted to stable - Tabbed plots layout (experimental) +- Trace value builders for functions and ranges (experimental) ### Changed - **Breaking API change!** Trace `text` replaced by `TraceValues` diff --git a/examples/src/main/kotlin/functionTraces.kt b/examples/src/main/kotlin/functionTraces.kt new file mode 100644 index 00000000..2edbf90a --- /dev/null +++ b/examples/src/main/kotlin/functionTraces.kt @@ -0,0 +1,35 @@ +import hep.dataforge.meta.invoke +import kscience.plotly.Plotly +import kscience.plotly.UnstablePlotlyAPI +import kscience.plotly.makeFile +import kscience.plotly.models.functionXY +import kscience.plotly.trace +import kotlin.math.PI +import kotlin.math.sin + +@OptIn(UnstablePlotlyAPI::class) +fun main() { + val plot = Plotly.plot { + repeat(50) { phase -> + trace { + functionXY(0.0..2 * PI, step = 0.05) { + sin(it + phase * 2 * PI / 60) + } + name = "Sin with phase offset ${phase * 2 * PI / 60}" + } + } + + layout { + title = "Graph name" + xaxis { + title = "x axis" + } + yaxis { + title = "y axis" + } + height = 700 + } + } + + plot.makeFile() +} diff --git a/plotlykt-core/src/commonMain/kotlin/kscience/plotly/models/fillTrace.kt b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/models/fillTrace.kt new file mode 100644 index 00000000..9a9c3a33 --- /dev/null +++ b/plotlykt-core/src/commonMain/kotlin/kscience/plotly/models/fillTrace.kt @@ -0,0 +1,63 @@ +package kscience.plotly.models + +import kscience.plotly.UnstablePlotlyAPI + +// Utilities to fill traces with values + +/** + * Append x and y axis values to a trace with optional x error [xErr] and y error [yErr] + */ +@UnstablePlotlyAPI +public fun Trace.appendXY(x: Number, y: Number, xErr: Number? = null, yErr: Number? = null) { + this.x.numbers += x + this.y.numbers += y + xErr?.let { error_x.array += xErr } + yErr?.let { error_y.array += yErr } +} + +/** + * Append trace values using given [pairs] of x-y number coordinates + */ +@UnstablePlotlyAPI +public fun Trace.appendXY(vararg pairs: Pair) { + this.x.numbers += pairs.map { it.first } + this.y.numbers += pairs.map { it.second } +} + +/** + * Fill trace [x] and [y] with values based on a given integer [xRange] and a numeric [function]. Old values are erased. + */ +@UnstablePlotlyAPI +public fun Trace.functionXY(xRange: IntRange, function: (Int) -> Number) { + x.numbers = xRange + y.numbers = xRange.map(function) +} + +/** + * Fill trace [x] and [y] with values based on given [xs] and a [function] for y values. Old values are erased. + */ +@UnstablePlotlyAPI +public fun Trace.functionXY(xs: Iterable, function: (Double) -> Number) { + x.numbers = xs + y.numbers = xs.map { function(it.toDouble()) } +} + +/** + * Fill values in [xRange] with given [step] using given [function]. Old values are erased. + */ +@UnstablePlotlyAPI +public fun Trace.functionXY( + xRange: ClosedFloatingPointRange, + step: Double = 1.0, + function: (Double) -> Number, +) { + val xs = buildList { + var value = xRange.start + while (value <= xRange.endInclusive) { + add(value) + value += step + } + } + x.numbers = xs + y.numbers = xs.map { function(it) } +} \ No newline at end of file From 6f3641977ffe770b26cf870cd8dccaf2af27fdfa Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 8 Feb 2021 12:30:40 +0300 Subject: [PATCH 10/12] Patch CHANGELOG.md --- CHANGELOG.md | 16 ++++++++++++- plotlykt-core/api/plotlykt-core.api | 37 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e9b21cf..408b3b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + +## [0.3.1] +### Added - Table widget implementation by @ArtificialPB - Mathjax header promoted to stable - Tabbed plots layout (experimental) @@ -15,7 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - **Breaking API change!** Trace `text` replaced by `TraceValues` - Moved to DataForge 0.3 API -- Kotlin 1.4.21 +- Kotlin 1.4.30 +- **JVM-IR** - Plot `Config` moved to constructor - Replaced direct color accessor by a delegate diff --git a/plotlykt-core/api/plotlykt-core.api b/plotlykt-core/api/plotlykt-core.api index bbf9a60c..3fc82a45 100644 --- a/plotlykt-core/api/plotlykt-core.api +++ b/plotlykt-core/api/plotlykt-core.api @@ -120,6 +120,33 @@ public final class kscience/plotly/PlotKt { public static final fun trace (Lkscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)Lkscience/plotly/models/Trace; } +public final class kscience/plotly/PlotTabs { + public fun ()V + public final fun getTabs ()Ljava/util/List; + public final fun tab (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)V + public static synthetic fun tab$default (Lkscience/plotly/PlotTabs;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V +} + +public final class kscience/plotly/PlotTabs$Tab { + public fun (Ljava/lang/String;Ljava/lang/String;Lkscience/plotly/PlotlyFragment;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Lkscience/plotly/PlotlyFragment; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Lkscience/plotly/PlotlyFragment;)Lkscience/plotly/PlotTabs$Tab; + public static synthetic fun copy$default (Lkscience/plotly/PlotTabs$Tab;Ljava/lang/String;Ljava/lang/String;Lkscience/plotly/PlotlyFragment;ILjava/lang/Object;)Lkscience/plotly/PlotTabs$Tab; + public fun equals (Ljava/lang/Object;)Z + public final fun getContent ()Lkscience/plotly/PlotlyFragment; + public final fun getId ()Ljava/lang/String; + public final fun getTitle ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class kscience/plotly/PlotTabsKt { + public static final fun tabs (Lkscience/plotly/Plotly;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkscience/plotly/PlotlyPage; + public static synthetic fun tabs$default (Lkscience/plotly/Plotly;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkscience/plotly/PlotlyPage; +} + public final class kscience/plotly/Plotly { public static final field INSTANCE Lkscience/plotly/Plotly; public static final field VERSION Ljava/lang/String; @@ -742,6 +769,16 @@ public final class kscience/plotly/models/Fill : hep/dataforge/meta/Scheme { public final class kscience/plotly/models/Fill$Companion : hep/dataforge/meta/SchemeSpec { } +public final class kscience/plotly/models/FillTraceKt { + public static final fun appendXY (Lkscience/plotly/models/Trace;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;)V + public static final fun appendXY (Lkscience/plotly/models/Trace;[Lkotlin/Pair;)V + public static synthetic fun appendXY$default (Lkscience/plotly/models/Trace;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;ILjava/lang/Object;)V + public static final fun functionXY (Lkscience/plotly/models/Trace;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)V + public static final fun functionXY (Lkscience/plotly/models/Trace;Lkotlin/ranges/ClosedFloatingPointRange;DLkotlin/jvm/functions/Function1;)V + public static final fun functionXY (Lkscience/plotly/models/Trace;Lkotlin/ranges/IntRange;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun functionXY$default (Lkscience/plotly/models/Trace;Lkotlin/ranges/ClosedFloatingPointRange;DLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + public final class kscience/plotly/models/FillType : java/lang/Enum { public static final field none Lkscience/plotly/models/FillType; public static final field tonext Lkscience/plotly/models/FillType; From cfff9ef82e6c7b77f1d9e28b2f2ef4c6185ca5c8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 8 Feb 2021 12:32:59 +0300 Subject: [PATCH 11/12] 0.3.1. Promote to release version. --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3eea3a16..8e32856e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ val githubProject by extra("plotly.kt") allprojects { group = "kscience.plotlykt" - version = "0.3.1-dev-5" + version = "0.3.1" repositories { mavenLocal() From 7b10437f49dd569a85a547667e58361b667ef1b8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 8 Feb 2021 12:39:02 +0300 Subject: [PATCH 12/12] add Space publishing --- build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 8e32856e..f134f07e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,4 +23,8 @@ allprojects { apiValidation { ignoredProjects.addAll(listOf("examples", "fx-demo", "js-demo")) +} + +ksciencePublish{ + spaceRepo = "https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven" } \ No newline at end of file