diff options
50 files changed, 544 insertions, 361 deletions
diff --git a/build.gradle b/build.gradle index abb8f25d..35fa82c7 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ configure(allprojects - project(':kvision-modules')) { } configure(allprojects - project(':kvision-modules') - project(":kvision-modules:kvision-base")) { + apply plugin: 'io.gitlab.arturbosch.detekt' apply plugin: 'com.jfrog.bintray' apply plugin: 'maven' apply plugin: 'maven-publish' @@ -54,6 +55,11 @@ configure(allprojects - project(':kvision-modules') - project(":kvision-modules: tasks.build.dependsOn(['sourceJar', 'emptyJar']) + detekt { + config = files(rootProject.projectDir.path + "/detekt.yml") + filters = ".*test.*,.*/resources/.*,.*/tmp/.*" + } + publishing { publications { mavenProject(MavenPublication) { @@ -88,7 +94,6 @@ if (!project.gradle.startParameter.taskNames.contains("dokka")) { apply plugin: 'kotlin' } apply plugin: 'org.jetbrains.kotlin.frontend' -apply plugin: 'io.gitlab.arturbosch.detekt' apply plugin: 'org.jetbrains.dokka' apply plugin: 'kotlinx-serialization' @@ -145,15 +150,6 @@ kotlinFrontend { } -detekt { - version = "${detektVersion}" - profile("main") { - input = "$projectDir/src/main/kotlin" - config = "$projectDir/detekt.yml" - filters = ".*test.*,.*/resources/.*,.*/tmp/.*" - } -} - dokka { includes = ['Module.md'] sourceDirs = files('kvision-modules/kvision-bootstrap/src/main/kotlin', diff --git a/gradle.properties b/gradle.properties index 8bbe74bd..a7d1cbf9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,14 @@ group=pl.treksoft version=0.0.18 -kotlinVersion=1.3.0-rc-116 +kotlinVersion=1.3.0-rc-146 javaVersion=1.8 -coroutinesVersion=0.26.1-eap13 +coroutinesVersion=0.30.0-eap13 serializationVersion=0.8.1-rc13 frontendPluginVersion=0.0.37 dokkaVersion=0.9.17 -detektVersion=1.0.0.RC8 +detektVersion=1.0.0.RC9.2 junitVersion=4.12 -joobyVersion=1.5.0 +joobyVersion=1.5.1 kweryVersion=0.17 dependencyManagementPluginVersion=1.0.4.RELEASE -jacksonModuleKotlinVersion=2.9.6 +jacksonModuleKotlinVersion=2.9.7 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differindex a5fe1cb9..f6b961fd 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 79799daf..d76b502e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Mar 28 23:46:27 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt b/kvision-modules/kvision-common/src/main/kotlin/pl/treksoft/kvision/types/Date.kt index c81ebde1..86a72b33 100644 --- a/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt +++ b/kvision-modules/kvision-common/src/main/kotlin/pl/treksoft/kvision/types/Date.kt @@ -21,19 +21,8 @@ */ package pl.treksoft.kvision.types -import pl.treksoft.kvision.utils.toDateF -import pl.treksoft.kvision.utils.toStringF -import kotlin.js.Date +expect class Date -@Suppress("MayBeConstant", "TopLevelPropertyNaming") -actual val KDATE_FORMAT = "YYYY-MM-DD HH:mm:ss" +expect fun String.toDateF(format: String = "YYYY-MM-DD HH:mm:ss"): Date -actual fun nowDate(): KDate = - KDate(Date().getTime().toLong()) - -actual fun String.toKDateF(format: String): KDate = - this.toDateF(format)?.getTime()?.toLong()?.let { KDate(it) } ?: KDate() - -actual fun KDate.toStringF(format: String) = this.toJS().toStringF(format) - -fun KDate.toJS(): kotlin.js.Date = kotlin.js.Date(this.time) +expect fun Date.toStringF(format: String = "YYYY-MM-DD HH:mm:ss"): String diff --git a/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/KVManagerDatetime.kt b/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/KVManagerDatetime.kt index cde55b1e..4cc71c8b 100644 --- a/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/KVManagerDatetime.kt +++ b/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/KVManagerDatetime.kt @@ -21,7 +21,7 @@ */ package pl.treksoft.kvision -internal val KVManagerDatetimeInit = KVManagerDatetime.init() +internal val kVManagerDatetimeInit = KVManagerDatetime.init() /** * Internal singleton object which initializes and configures KVision datetime module. diff --git a/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt b/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt index 9cdd0369..3d32fd8c 100644 --- a/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt +++ b/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt @@ -24,12 +24,12 @@ package pl.treksoft.kvision.form.time import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.form.DateFormControl import pl.treksoft.kvision.form.FieldLabel import pl.treksoft.kvision.form.HelpBlock -import pl.treksoft.kvision.form.KDateFormControl import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.types.KDate import pl.treksoft.kvision.utils.SnOn +import kotlin.js.Date /** * Form field date/time chooser component. @@ -42,9 +42,9 @@ import pl.treksoft.kvision.utils.SnOn * @param rich determines if [label] can contain HTML code */ open class DateTime( - value: KDate? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, + value: Date? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, rich: Boolean = false -) : SimplePanel(setOf("form-group")), KDateFormControl { +) : SimplePanel(setOf("form-group")), DateFormControl { /** * Date/time input value. @@ -235,7 +235,7 @@ open class DateTime( * It takes the same parameters as the constructor of the built component. */ fun Container.dateTime( - value: KDate? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, + value: Date? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, rich: Boolean = false, init: (DateTime.() -> Unit)? = null ): DateTime { val dateTime = DateTime(value, name, format, label, rich).apply { init?.invoke(this) } diff --git a/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt b/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt index 1df8a082..69b84c87 100644 --- a/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt +++ b/kvision-modules/kvision-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt @@ -29,11 +29,10 @@ import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.i18n.I18n -import pl.treksoft.kvision.types.KDate -import pl.treksoft.kvision.types.toJS -import pl.treksoft.kvision.types.toKDateF +import pl.treksoft.kvision.types.toDateF import pl.treksoft.kvision.types.toStringF import pl.treksoft.kvision.utils.obj +import kotlin.js.Date internal const val DEFAULT_MINUTE_STEP = 5 internal const val MAX_VIEW = 4 @@ -48,7 +47,7 @@ internal const val MAX_VIEW = 4 */ @Suppress("TooManyFunctions") open class DateTimeInput( - value: KDate? = null, format: String = "YYYY-MM-DD HH:mm", + value: Date? = null, format: String = "YYYY-MM-DD HH:mm", classes: Set<String> = setOf() ) : Widget(classes + "form-control"), FormInput { @@ -63,11 +62,11 @@ open class DateTimeInput( /** * Date/time input value. */ - var value by refreshOnUpdate(value, { refreshState() }) + var value by refreshOnUpdate(value) { refreshState() } /** * Date/time format. */ - var format by refreshOnUpdate(format, { refreshDatePicker() }) + var format by refreshOnUpdate(format) { refreshDatePicker() } /** * The placeholder for the date/time input. */ @@ -95,31 +94,31 @@ open class DateTimeInput( /** * Day of the week start. 0 (Sunday) to 6 (Saturday). */ - var weekStart by refreshOnUpdate(0, { refreshDatePicker() }) + var weekStart by refreshOnUpdate(0) { refreshDatePicker() } /** * Days of the week that should be disabled. Multiple values should be comma separated. */ - var daysOfWeekDisabled by refreshOnUpdate(arrayOf<Int>(), { refreshDatePicker() }) + var daysOfWeekDisabled by refreshOnUpdate(arrayOf<Int>()) { refreshDatePicker() } /** * Determines if *Clear* button should be visible. */ - var clearBtn by refreshOnUpdate(true, { refreshDatePicker() }) + var clearBtn by refreshOnUpdate(true) { refreshDatePicker() } /** * Determines if *Today* button should be visible. */ - var todayBtn by refreshOnUpdate(false, { refreshDatePicker() }) + var todayBtn by refreshOnUpdate(false) { refreshDatePicker() } /** * Determines if the current day should be highlighted. */ - var todayHighlight by refreshOnUpdate(false, { refreshDatePicker() }) + var todayHighlight by refreshOnUpdate(false) { refreshDatePicker() } /** * The increment used to build the hour view. */ - var minuteStep by refreshOnUpdate(DEFAULT_MINUTE_STEP, { refreshDatePicker() }) + var minuteStep by refreshOnUpdate(DEFAULT_MINUTE_STEP) { refreshDatePicker() } /** * Determines if meridian views are visible in day and hour views. */ - var showMeridian by refreshOnUpdate(false, { refreshDatePicker() }) + var showMeridian by refreshOnUpdate(false) { refreshDatePicker() } override fun render(): VNode { return render("input") @@ -164,7 +163,7 @@ open class DateTimeInput( @Suppress("UnsafeCastFromDynamic") protected open fun refreshState() { value?.let { - getElementJQueryD()?.datetimepicker("update", it.toJS()) + getElementJQueryD()?.datetimepicker("update", it) } ?: run { getElementJQueryD()?.`val`(null) getElementJQueryD()?.datetimepicker("update", null) @@ -181,7 +180,7 @@ open class DateTimeInput( protected open fun changeValue() { val v = getElementJQuery()?.`val`() as String? if (v != null && v.isNotEmpty()) { - this.value = v.toKDateF(format) + this.value = v.toDateF(format) } else { this.value = null } @@ -205,15 +204,15 @@ open class DateTimeInput( override fun afterInsert(node: VNode) { if (!this.disabled) { this.initDateTimePicker() - this.getElementJQuery()?.on("changeDate", { e, _ -> + this.getElementJQuery()?.on("changeDate") { e, _ -> this.dispatchEvent("change", obj { detail = e }) - }) - this.getElementJQuery()?.on("show", { e, _ -> + } + this.getElementJQuery()?.on("show") { e, _ -> this.dispatchEvent("showBsDateTime", obj { detail = e }) - }) - this.getElementJQuery()?.on("hide", { e, _ -> + } + this.getElementJQuery()?.on("hide") { e, _ -> this.dispatchEvent("hideBsDateTime", obj { detail = e }) - }) + } refreshState() } } @@ -280,7 +279,7 @@ open class DateTimeInput( * It takes the same parameters as the constructor of the built component. */ fun Container.dateTimeInput( - value: KDate? = null, format: String = "YYYY-MM-DD HH:mm", classes: Set<String> = setOf(), + value: Date? = null, format: String = "YYYY-MM-DD HH:mm", classes: Set<String> = setOf(), init: (DateTimeInput.() -> Unit)? = null ): DateTimeInput { val dateTimeInput = DateTimeInput(value, format, classes).apply { init?.invoke(this) } diff --git a/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeInputSpec.kt b/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeInputSpec.kt index d824125b..69ab46ec 100644 --- a/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeInputSpec.kt +++ b/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeInputSpec.kt @@ -23,9 +23,9 @@ package test.pl.treksoft.kvision.form.time import pl.treksoft.kvision.form.time.DateTimeInput import pl.treksoft.kvision.panel.Root -import pl.treksoft.kvision.types.KDate import pl.treksoft.kvision.types.toStringF import test.pl.treksoft.kvision.DomSpec +import kotlin.js.Date import kotlin.test.Test import kotlin.test.assertEquals @@ -35,7 +35,7 @@ class DateTimeInputSpec : DomSpec { fun render() { run { val root = Root("test", true) - val data = KDate() + val data = Date() val dti = DateTimeInput(value = data).apply { placeholder = "place" id = "idti" diff --git a/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeSpec.kt b/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeSpec.kt index 482a7b7a..19cefd86 100644 --- a/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeSpec.kt +++ b/kvision-modules/kvision-datetime/src/test/kotlin/test/pl/treksoft/kvision/form/time/DateTimeSpec.kt @@ -23,10 +23,10 @@ package test.pl.treksoft.kvision.form.time import pl.treksoft.kvision.form.time.DateTime import pl.treksoft.kvision.panel.Root -import pl.treksoft.kvision.types.KDate import pl.treksoft.kvision.types.toStringF import test.pl.treksoft.kvision.DomSpec import kotlin.browser.document +import kotlin.js.Date import kotlin.test.Test class DateTimeSpec : DomSpec { @@ -35,7 +35,7 @@ class DateTimeSpec : DomSpec { fun render() { run { val root = Root("test", true) - val data = KDate() + val data = Date() val ti = DateTime(value = data, label = "Label").apply { placeholder = "place" name = "name" diff --git a/kvision-modules/kvision-handlebars/src/main/kotlin/pl/treksoft/kvision/KVManagerHandlebars.kt b/kvision-modules/kvision-handlebars/src/main/kotlin/pl/treksoft/kvision/KVManagerHandlebars.kt index b7f5fbc6..b2e52bf7 100644 --- a/kvision-modules/kvision-handlebars/src/main/kotlin/pl/treksoft/kvision/KVManagerHandlebars.kt +++ b/kvision-modules/kvision-handlebars/src/main/kotlin/pl/treksoft/kvision/KVManagerHandlebars.kt @@ -21,10 +21,7 @@ */ package pl.treksoft.kvision -import org.w3c.dom.asList -import kotlin.browser.document - -internal val KVManagerHandlebarsInit = KVManagerHandlebars.init() +internal val kVManagerHandlebarsInit = KVManagerHandlebars.init() /** * Internal singleton object which initializes and configures KVision handlebars module. diff --git a/kvision-modules/kvision-i18n/src/main/kotlin/pl/treksoft/kvision/KVManagerI18n.kt b/kvision-modules/kvision-i18n/src/main/kotlin/pl/treksoft/kvision/KVManagerI18n.kt index 66e3e72c..9e24327b 100644 --- a/kvision-modules/kvision-i18n/src/main/kotlin/pl/treksoft/kvision/KVManagerI18n.kt +++ b/kvision-modules/kvision-i18n/src/main/kotlin/pl/treksoft/kvision/KVManagerI18n.kt @@ -21,10 +21,7 @@ */ package pl.treksoft.kvision -import org.w3c.dom.asList -import kotlin.browser.document - -internal val KVManagerI18nInit = KVManagerI18n.init() +internal val kVManagerI18nInit = KVManagerI18n.init() /** * Internal singleton object which initializes and configures KVision i18n module. diff --git a/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/KVManagerRichText.kt b/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/KVManagerRichText.kt index 1ccd7a85..c7cd444c 100644 --- a/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/KVManagerRichText.kt +++ b/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/KVManagerRichText.kt @@ -25,7 +25,7 @@ import pl.treksoft.kvision.i18n.I18n import pl.treksoft.kvision.utils.obj import kotlin.browser.window -internal val KVManagerRichTextInit = KVManagerRichText.init() +internal val kVManagerRichTextInit = KVManagerRichText.init() /** * Internal singleton object which initializes and configures KVision RichText module. diff --git a/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt b/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt index 961c27cd..43b018f4 100644 --- a/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt +++ b/kvision-modules/kvision-richtext/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt @@ -23,7 +23,6 @@ package pl.treksoft.kvision.form.text import com.github.snabbdom.VNode import pl.treksoft.jquery.jQuery -import pl.treksoft.kvision.KVManagerRichText import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.StringPair import kotlin.browser.document @@ -67,7 +66,7 @@ open class RichTextInput(value: String? = null, classes: Set<String> = setOf()) if (this.disabled || this.readonly == true) { this.getElementJQuery()?.removeAttr("contenteditable") } else { - this.getElementJQuery()?.on("trix-change", { _, _ -> + this.getElementJQuery()?.on("trix-change") { _, _ -> if (trixId != null) { val v = document.getElementById("trix-input-$trixId")?.let { jQuery(it).`val`() as String? } value = if (v != null && v.isNotEmpty()) { @@ -78,9 +77,9 @@ open class RichTextInput(value: String? = null, classes: Set<String> = setOf()) val event = org.w3c.dom.events.Event("change") this.getElement()?.dispatchEvent(event) } - }) + } } - this.getElementJQuery()?.on("trix-initialize", { _, _ -> + this.getElementJQuery()?.on("trix-initialize") { _, _ -> trixId = this.getElementJQuery()?.attr("trix-id") if (trixId != null) { value?.let { @@ -89,8 +88,8 @@ open class RichTextInput(value: String? = null, classes: Set<String> = setOf()) } } } - }) - this.getElementJQuery()?.on("trix-file-accept", { e, _ -> e.preventDefault() }) + } + this.getElementJQuery()?.on("trix-file-accept") { e, _ -> e.preventDefault() } } override fun afterDestroy() { diff --git a/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt b/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt index c7c3815a..e2c556c1 100644 --- a/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt +++ b/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt @@ -21,7 +21,7 @@ */ package pl.treksoft.kvision -internal val KVManagerSelectInit = KVManagerSelect.init() +internal val kVManagerSelectInit = KVManagerSelect.init() /** * Internal singleton object which initializes and configures KVision select module. diff --git a/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt index 8f4569c7..30609907 100644 --- a/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt +++ b/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -66,11 +66,11 @@ open class SelectInput( /** * A list of options (label to value pairs) for the select control. */ - internal var options by refreshOnUpdate(options, { setChildrenFromOptions() }) + internal var options by refreshOnUpdate(options) { setChildrenFromOptions() } /** * A value of the selected option. */ - var value by refreshOnUpdate(value, { refreshState() }) + var value by refreshOnUpdate(value) { refreshState() } /** * The name attribute of the generated HTML select element. */ @@ -82,12 +82,12 @@ open class SelectInput( /** * Additional options for remote (AJAX) data source. */ - var ajaxOptions by refreshOnUpdate(ajaxOptions, { + var ajaxOptions by refreshOnUpdate(ajaxOptions) { if (it != null) { liveSearch = true } refresh() - }) + } /** * Maximal number of selected options. */ @@ -115,7 +115,7 @@ open class SelectInput( /** * Determines if an empty option is automatically generated. */ - var emptyOption by refreshOnUpdate(false, { setChildrenFromOptions() }) + var emptyOption by refreshOnUpdate(false) { setChildrenFromOptions() } /** * Determines if the field is disabled. */ @@ -291,31 +291,31 @@ open class SelectInput( getElementJQueryD()?.selectpicker("render").ajaxSelectPicker(it.toJs(emptyOption)) } ?: getElementJQueryD()?.selectpicker("render") - this.getElementJQuery()?.on("show.bs.select", { e, _ -> + this.getElementJQuery()?.on("show.bs.select") { e, _ -> this.dispatchEvent("showBsSelect", obj { detail = e }) - }) - this.getElementJQuery()?.on("shown.bs.select", { e, _ -> + } + this.getElementJQuery()?.on("shown.bs.select") { e, _ -> this.dispatchEvent("shownBsSelect", obj { detail = e }) - }) - this.getElementJQuery()?.on("hide.bs.select", { e, _ -> + } + this.getElementJQuery()?.on("hide.bs.select") { e, _ -> this.dispatchEvent("hideBsSelect", obj { detail = e }) - }) - this.getElementJQuery()?.on("hidden.bs.select", { e, _ -> + } + this.getElementJQuery()?.on("hidden.bs.select") { e, _ -> this.dispatchEvent("hiddenBsSelect", obj { detail = e }) - }) - this.getElementJQuery()?.on("loaded.bs.select", { e, _ -> + } + this.getElementJQuery()?.on("loaded.bs.select") { e, _ -> this.dispatchEvent("loadedBsSelect", obj { detail = e }) - }) - this.getElementJQuery()?.on("rendered.bs.select", { e, _ -> + } + this.getElementJQuery()?.on("rendered.bs.select") { e, _ -> this.dispatchEvent("renderedBsSelect", obj { detail = e }) - }) - this.getElementJQuery()?.on("refreshed.bs.select", { e, _ -> + } + this.getElementJQuery()?.on("refreshed.bs.select") { e, _ -> this.dispatchEvent("refreshedBsSelect", obj { detail = e }) - }) - this.getElementJQueryD()?.on("changed.bs.select", { e, cIndex: Int -> + } + this.getElementJQueryD()?.on("changed.bs.select") { e, cIndex: Int -> e["clickedIndex"] = cIndex this.dispatchEvent("changedBsSelect", obj { detail = e }) - }) + } refreshState() } diff --git a/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt b/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt index e33b3457..d90f0e6d 100644 --- a/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt +++ b/kvision-modules/kvision-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt @@ -49,7 +49,7 @@ open class SelectOptGroup( /** * A list of options (label to value pairs) for the group. */ - var options by refreshOnUpdate(options, { setChildrenFromOptions() }) + var options by refreshOnUpdate(options) { setChildrenFromOptions() } /** * Maximal number of selected options in the group. */ diff --git a/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/KVServer.kt b/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/KVServer.kt index 76f1ee30..38edc1b8 100644 --- a/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/KVServer.kt +++ b/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/KVServer.kt @@ -19,8 +19,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -@file:Suppress("EXPERIMENTAL_FEATURE_WARNING") - package pl.treksoft.kvision.remote import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper @@ -31,6 +29,7 @@ import org.jooby.Kooby import org.jooby.Session import org.jooby.json.Jackson import org.pac4j.core.profile.CommonProfile +import java.text.SimpleDateFormat import kotlinx.coroutines.async as coroutinesAsync /** @@ -42,7 +41,9 @@ actual open class KVServer(init: KVServer.() -> Unit) : Kooby() { assets("/", "index.html") @Suppress("LeakingThis") assets("/**").onMissing(0) - val mapper = jacksonObjectMapper() + val mapper = jacksonObjectMapper().apply { + dateFormat = SimpleDateFormat("YYYY-MM-DD HH:mm:ss") + } @Suppress("LeakingThis") use(Jackson(mapper)) @Suppress("LeakingThis") diff --git a/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt b/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt index b37d7319..3091ce26 100644 --- a/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt +++ b/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt @@ -28,11 +28,11 @@ import org.jooby.Response import org.jooby.Status import org.slf4j.Logger import org.slf4j.LoggerFactory +import java.text.SimpleDateFormat /** * Multiplatform service manager. */ -@Suppress("EXPERIMENTAL_FEATURE_WARNING") actual open class ServiceManager<out T> actual constructor(val service: T) { companion object { @@ -40,7 +40,9 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { } protected val routes: MutableList<KVServer.() -> Unit> = mutableListOf() - val mapper = jacksonObjectMapper() + val mapper = jacksonObjectMapper().apply { + dateFormat = SimpleDateFormat("YYYY-MM-DD HH:mm:ss") + } var counter: Int = 0 /** @@ -50,12 +52,13 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { * @param method a HTTP method * @param prefix an URL address prefix */ + @Suppress("TooGenericExceptionCaught") protected actual inline fun <reified RET> bind( noinline function: T.(Request?) -> Deferred<RET>, route: String?, method: RpcHttpMethod, prefix: String ) { val routeDef = route ?: "route${this::class.simpleName}${counter++}" - routes.add({ + routes.add { call(method, "$prefix$routeDef") { req, res -> if (service != null) { val jsonRpcRequest = req.body(JsonRpcRequest::class.java) @@ -75,7 +78,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { res.status(Status.SERVER_ERROR) } }.invoke(this) - }) + } } /** @@ -85,12 +88,13 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { * @param method a HTTP method * @param prefix an URL address prefix */ + @Suppress("TooGenericExceptionCaught") protected actual inline fun <reified PAR, reified RET> bind( noinline function: T.(PAR, Request?) -> Deferred<RET>, route: String?, method: RpcHttpMethod, prefix: String ) { val routeDef = route ?: "route${this::class.simpleName}${counter++}" - routes.add({ + routes.add { call(method, "$prefix$routeDef") { req, res -> if (service != null) { val jsonRpcRequest = req.body(JsonRpcRequest::class.java) @@ -115,7 +119,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { res.status(Status.SERVER_ERROR) } }.invoke(this) - }) + } } /** @@ -125,12 +129,13 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { * @param method a HTTP method * @param prefix an URL address prefix */ + @Suppress("TooGenericExceptionCaught") protected actual inline fun <reified PAR1, reified PAR2, reified RET> bind( noinline function: T.(PAR1, PAR2, Request?) -> Deferred<RET>, route: String?, method: RpcHttpMethod, prefix: String ) { val routeDef = route ?: "route${this::class.simpleName}${counter++}" - routes.add({ + routes.add { call(method, "$prefix$routeDef") { req, res -> if (service != null) { val jsonRpcRequest = req.body(JsonRpcRequest::class.java) @@ -156,7 +161,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { res.status(Status.SERVER_ERROR) } }.invoke(this) - }) + } } /** @@ -166,15 +171,17 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { * @param method a HTTP method * @param prefix an URL address prefix */ + @Suppress("TooGenericExceptionCaught") protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified RET> bind( noinline function: T.(PAR1, PAR2, PAR3, Request?) -> Deferred<RET>, route: String?, method: RpcHttpMethod, prefix: String ) { val routeDef = route ?: "route${this::class.simpleName}${counter++}" - routes.add({ + routes.add { call(method, "$prefix$routeDef") { req, res -> if (service != null) { val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + @Suppress("MagicNumber") if (jsonRpcRequest.params.size == 3) { val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) @@ -198,7 +205,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { res.status(Status.SERVER_ERROR) } }.invoke(this) - }) + } } /** @@ -208,15 +215,17 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { * @param method a HTTP method * @param prefix an URL address prefix */ + @Suppress("TooGenericExceptionCaught") protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified PAR4, reified RET> bind( noinline function: T.(PAR1, PAR2, PAR3, PAR4, Request?) -> Deferred<RET>, route: String?, method: RpcHttpMethod, prefix: String ) { val routeDef = route ?: "route${this::class.simpleName}${counter++}" - routes.add({ + routes.add { call(method, "$prefix$routeDef") { req, res -> if (service != null) { val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + @Suppress("MagicNumber") if (jsonRpcRequest.params.size == 4) { val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) @@ -242,7 +251,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { res.status(Status.SERVER_ERROR) } }.invoke(this) - }) + } } /** @@ -252,6 +261,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { * @param method a HTTP method * @param prefix an URL address prefix */ + @Suppress("TooGenericExceptionCaught") protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified PAR4, reified PAR5, reified RET> bind( noinline function: T.(PAR1, PAR2, PAR3, PAR4, PAR5, Request?) -> Deferred<RET>, @@ -260,10 +270,11 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { prefix: String ) { val routeDef = route ?: "route${this::class.simpleName}${counter++}" - routes.add({ + routes.add { call(method, "$prefix$routeDef") { req, res -> if (service != null) { val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + @Suppress("MagicNumber") if (jsonRpcRequest.params.size == 5) { val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) @@ -292,7 +303,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { res.status(Status.SERVER_ERROR) } }.invoke(this) - }) + } } fun call( @@ -310,6 +321,7 @@ actual open class ServiceManager<out T> actual constructor(val service: T) { } } + @Suppress("TooGenericExceptionCaught") protected inline fun <reified T> getParameter(str: String?): T { return str?.let { if (T::class == String::class) { diff --git a/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt b/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/types/Date.kt index 9fc534c4..32c8923e 100644 --- a/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt +++ b/kvision-modules/kvision-server-jooby/src/main/kotlin/pl/treksoft/kvision/types/Date.kt @@ -28,31 +28,19 @@ import com.github.andrewoma.kwery.mapper.standardConverters import com.github.andrewoma.kwery.mapper.util.camelToLowerUnderscore import java.sql.Timestamp import java.text.SimpleDateFormat -import java.util.* -/** - * A serializable wrapper for a multiplatform Date type. - */ -@Suppress("MayBeConstant") -actual val KDATE_FORMAT = "yyyy-MM-dd HH:mm:ss" - -actual fun nowDate(): KDate = - KDate(Date().time) - -actual fun String.toKDateF(format: String): KDate = - KDate(SimpleDateFormat(format).parse(this).time) +actual typealias Date = java.util.Date -actual fun KDate.toStringF(format: String) = - SimpleDateFormat(format).format(this.toJava()) +actual fun String.toDateF(format: String): Date = SimpleDateFormat(format).parse(this) -fun KDate.toJava(): java.util.Date = java.util.Date(this.time) +actual fun Date.toStringF(format: String): String = SimpleDateFormat(format).format(this) -object KDateConverter : SimpleConverter<KDate>( - { row, c -> KDate(row.timestamp(c).time) }, +object DateConverter : SimpleConverter<Date>( + { row, c -> Date(row.timestamp(c).time) }, { Timestamp(it.time) } ) val kvTableConfig = TableConfiguration( - converters = standardConverters + reifiedConverter(KDateConverter), + converters = standardConverters + reifiedConverter(DateConverter), namingConvention = camelToLowerUnderscore ) diff --git a/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/KVManagerSpinner.kt b/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/KVManagerSpinner.kt index 0de06c8c..ca4d3764 100644 --- a/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/KVManagerSpinner.kt +++ b/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/KVManagerSpinner.kt @@ -21,7 +21,7 @@ */ package pl.treksoft.kvision -internal val KVManagerSpinnerInit = KVManagerSpinner.init() +internal val kVManagerSpinnerInit = KVManagerSpinner.init() /** * Internal singleton object which initializes and configures KVision spinner module. diff --git a/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt b/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt index 7d3af684..b4a8138a 100644 --- a/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt +++ b/kvision-modules/kvision-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt @@ -98,38 +98,38 @@ open class SpinnerInput( /** * Spinner value. */ - var value by refreshOnUpdate(value, { refreshState() }) + var value by refreshOnUpdate(value) { refreshState() } /** * The value attribute of the generated HTML input element. * * This value is placed directly in generated HTML code, while the [value] property is dynamically * bound to the spinner input value. */ - var startValue by refreshOnUpdate(value, { this.value = it; refresh() }) + var startValue by refreshOnUpdate(value) { this.value = it; refresh() } /** * Minimal value. */ - var min by refreshOnUpdate(min, { refreshSpinner() }) + var min by refreshOnUpdate(min) { refreshSpinner() } /** * Maximal value. */ - var max by refreshOnUpdate(max, { refreshSpinner() }) + var max by refreshOnUpdate(max) { refreshSpinner() } /** * Step value. */ - var step by refreshOnUpdate(step, { refreshSpinner() }) + var step by refreshOnUpdate(step) { refreshSpinner() } /** * Number of decimal digits value. */ - var decimals by refreshOnUpdate(decimals, { refreshSpinner() }) + var decimals by refreshOnUpdate(decimals) { refreshSpinner() } /** * Spinner buttons type. */ - var buttonsType by refreshOnUpdate(buttonsType, { refreshSpinner() }) + var buttonsType by refreshOnUpdate(buttonsType) { refreshSpinner() } /** * Spinner force rounding type. */ - var forceType by refreshOnUpdate(forceType, { refreshSpinner() }) + var forceType by refreshOnUpdate(forceType) { refreshSpinner() } /** * The placeholder for the spinner input. */ @@ -217,18 +217,18 @@ open class SpinnerInput( size?.let { siblings?.find("button")?.addClass(it.className) } - this.getElementJQuery()?.on("change", { e, _ -> + this.getElementJQuery()?.on("change") { e, _ -> if (e.asDynamic().isTrigger != null) { val event = org.w3c.dom.events.Event("change") this.getElement()?.dispatchEvent(event) } - }) - this.getElementJQuery()?.on("touchspin.on.min", { e, _ -> + } + this.getElementJQuery()?.on("touchspin.on.min") { e, _ -> this.dispatchEvent("onMinBsSpinner", obj { detail = e }) - }) - this.getElementJQuery()?.on("touchspin.on.max", { e, _ -> + } + this.getElementJQuery()?.on("touchspin.on.max") { e, _ -> this.dispatchEvent("onMaxBsSpinner", obj { detail = e }) - }) + } refreshState() } diff --git a/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/KVManagerUpload.kt b/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/KVManagerUpload.kt index 64a25545..af0950eb 100644 --- a/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/KVManagerUpload.kt +++ b/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/KVManagerUpload.kt @@ -21,7 +21,7 @@ */ package pl.treksoft.kvision -internal val KVManagerUploadInit = KVManagerUpload.init() +internal val kVManagerUploadInit = KVManagerUpload.init() /** * Internal singleton object which initializes and configures KVision upload module. diff --git a/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt b/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt index 51b73aa1..da35f9ef 100644 --- a/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt +++ b/kvision-modules/kvision-upload/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt @@ -62,67 +62,67 @@ open class UploadInput(uploadUrl: String? = null, multiple: Boolean = false, cla * The optional URL for the upload processing action. * If not set the upload button action will default to form submission. */ - var uploadUrl: String? by refreshOnUpdate(uploadUrl, { refreshUploadInput() }) + var uploadUrl: String? by refreshOnUpdate(uploadUrl) { refreshUploadInput() } /** * Determines if multiple file upload is supported. */ - var multiple: Boolean by refreshOnUpdate(multiple, { refresh(); refreshUploadInput() }) + var multiple: Boolean by refreshOnUpdate(multiple) { refresh(); refreshUploadInput() } /** * The extra data that will be passed as data to the AJAX server call via POST. */ - var uploadExtraData: ((String, Int) -> dynamic)? by refreshOnUpdate({ refreshUploadInput() }) + var uploadExtraData: ((String, Int) -> dynamic)? by refreshOnUpdate { refreshUploadInput() } /** * Determines if the explorer theme is used. */ - var explorerTheme: Boolean by refreshOnUpdate(false, { refreshUploadInput() }) + var explorerTheme: Boolean by refreshOnUpdate(false) { refreshUploadInput() } /** * Determines if the input selection is required. */ - var required: Boolean by refreshOnUpdate(false, { refreshUploadInput() }) + var required: Boolean by refreshOnUpdate(false) { refreshUploadInput() } /** * Determines if the caption is shown. */ - var showCaption: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var showCaption: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the preview is shown. */ - var showPreview: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var showPreview: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the remove button is shown. */ - var showRemove: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var showRemove: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the upload button is shown. */ - var showUpload: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var showUpload: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the cancel button is shown. */ - var showCancel: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var showCancel: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the file browse button is shown. */ - var showBrowse: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var showBrowse: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the click on the preview zone opens file browse window. */ - var browseOnZoneClick: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var browseOnZoneClick: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * Determines if the iconic preview is prefered. */ - var preferIconicPreview: Boolean by refreshOnUpdate(false, { refreshUploadInput() }) + var preferIconicPreview: Boolean by refreshOnUpdate(false) { refreshUploadInput() } /** * Allowed file types. */ - var allowedFileTypes: Set<String>? by refreshOnUpdate({ refreshUploadInput() }) + var allowedFileTypes: Set<String>? by refreshOnUpdate { refreshUploadInput() } /** * Allowed file extensions. */ - var allowedFileExtensions: Set<String>? by refreshOnUpdate({ refreshUploadInput() }) + var allowedFileExtensions: Set<String>? by refreshOnUpdate { refreshUploadInput() } /** * Determines if Drag&Drop zone is enabled. */ - var dropZoneEnabled: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) + var dropZoneEnabled: Boolean by refreshOnUpdate(true) { refreshUploadInput() } /** * The name attribute of the generated HTML input element. */ @@ -130,7 +130,7 @@ open class UploadInput(uploadUrl: String? = null, multiple: Boolean = false, cla /** * Determines if the field is disabled. */ - override var disabled by refreshOnUpdate(false, { refresh(); refreshUploadInput() }) + override var disabled by refreshOnUpdate(false) { refresh(); refreshUploadInput() } /** * The size of the input (currently not working) */ @@ -173,24 +173,24 @@ open class UploadInput(uploadUrl: String? = null, multiple: Boolean = false, cla @Suppress("UnsafeCastFromDynamic") override fun afterInsert(node: VNode) { getElementJQueryD()?.fileinput(getSettingsObj()) - this.getElementJQuery()?.on("fileselect", { e, _ -> + this.getElementJQuery()?.on("fileselect") { e, _ -> this.dispatchEvent("fileSelectUpload", obj { detail = e }) - }) - this.getElementJQuery()?.on("fileclear", { e, _ -> + } + this.getElementJQuery()?.on("fileclear") { e, _ -> this.dispatchEvent("fileClearUpload", obj { detail = e }) - }) - this.getElementJQuery()?.on("filereset", { e, _ -> + } + this.getElementJQuery()?.on("filereset") { e, _ -> this.dispatchEvent("fileResetUpload", obj { detail = e }) - }) - this.getElementJQuery()?.on("filebrowse", { e, _ -> + } + this.getElementJQuery()?.on("filebrowse") { e, _ -> this.dispatchEvent("fileBrowseUpload", obj { detail = e }) - }) - this.getElementJQueryD()?.on("filepreupload", lambda@{ _, data, previewId, index -> + } + this.getElementJQueryD()?.on("filepreupload") lambda@{ _, data, previewId, index -> data["previewId"] = previewId data["index"] = index this.dispatchEvent("filePreUpload", obj { detail = data }) return@lambda null - }) + } } override fun afterDestroy() { @@ -334,7 +334,6 @@ open class UploadInput(uploadUrl: String? = null, multiple: Boolean = false, cla * @param kFile object identifying the file * @return KFile object */ - @Suppress("EXPERIMENTAL_FEATURE_WARNING") suspend fun <K : Any> Form<K>.getContent( key: KProperty1<K, List<KFile>?>, kFile: KFile @@ -351,7 +350,6 @@ open class UploadInput(uploadUrl: String? = null, multiple: Boolean = false, cla * @param kFile object identifying the file * @return KFile object */ - @Suppress("EXPERIMENTAL_FEATURE_WARNING") suspend fun <K : Any> FormPanel<K>.getContent( key: KProperty1<K, List<KFile>?>, kFile: KFile diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt index 52f60813..9f28c52c 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt @@ -312,7 +312,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { afterPostpatch(v) } } - destroy = { _ -> + destroy = { afterDestroy() vnode = null vnode diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt index 80a05ba7..d8b74c54 100644 --- a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt +++ b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt @@ -61,7 +61,7 @@ class DataContainer<M, C : Component>( init { container.parent = this - model.onUpdate += { _ -> + model.onUpdate += { update() } update() diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index 4ae1e93b..a521fe95 100644 --- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -61,6 +61,7 @@ enum class DD(val option: String) { * @param withCaret determines if the dropdown button renders caret * @param classes a set of CSS class names */ +@Suppress("TooManyFunctions") open class DropDown( text: String, elements: List<StringPair>? = null, icon: String? = null, style: ButtonStyle = ButtonStyle.DEFAULT, disabled: Boolean = false, val forNavbar: Boolean = false, @@ -74,7 +75,7 @@ open class DropDown( set(value) { button.text = value } - private var elements by refreshOnUpdate(elements, { setChildrenFromElements() }) + private var elements by refreshOnUpdate(elements) { setChildrenFromElements() } /** * The icon of the dropdown button. */ @@ -207,18 +208,18 @@ open class DropDown( @Suppress("UnsafeCastFromDynamic") override fun afterInsert(node: VNode) { - this.getElementJQuery()?.on("show.bs.dropdown", { e, _ -> + this.getElementJQuery()?.on("show.bs.dropdown") { e, _ -> this.dispatchEvent("showBsDropdown", obj { detail = e }) - }) - this.getElementJQuery()?.on("shown.bs.dropdown", { e, _ -> + } + this.getElementJQuery()?.on("shown.bs.dropdown") { e, _ -> this.dispatchEvent("shownBsDropdown", obj { detail = e }) - }) - this.getElementJQuery()?.on("hide.bs.dropdown", { e, _ -> + } + this.getElementJQuery()?.on("hide.bs.dropdown") { e, _ -> this.dispatchEvent("hideBsDropdown", obj { detail = e }) - }) - this.getElementJQuery()?.on("hidden.bs.dropdown", { e, _ -> + } + this.getElementJQuery()?.on("hidden.bs.dropdown") { e, _ -> this.dispatchEvent("hiddenBsDropdown", obj { detail = e }) - }) + } } override fun getSnClass(): List<StringBoolPair> { diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt index 19f7e68a..8f93c7ea 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt @@ -23,12 +23,15 @@ package pl.treksoft.kvision.form import kotlinx.serialization.KSerializer import kotlinx.serialization.Mapper +import kotlinx.serialization.context.MutableSerialContextImpl import kotlinx.serialization.decode -import kotlinx.serialization.json.JSON import kotlinx.serialization.serializer import pl.treksoft.kvision.i18n.I18n.trans -import pl.treksoft.kvision.types.KDate +import pl.treksoft.kvision.types.DateSerializer import pl.treksoft.kvision.types.KFile +import pl.treksoft.kvision.types.toStringF +import pl.treksoft.kvision.utils.JSON +import kotlin.js.Date import kotlin.js.Json import kotlin.reflect.KProperty1 @@ -84,8 +87,8 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali modelFactory = { val map = it.flatMap { entry -> when (entry.value) { - is KDate -> { - listOf(entry.key to entry.value, "${entry.key}.time" to (entry.value as KDate).time) + is Date -> { + listOf(entry.key to (entry.value as? Date)?.toStringF()) } is List<*> -> { @Suppress("UNCHECKED_CAST") @@ -93,9 +96,9 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali listOf(entry.key to entry.value, "${entry.key}.size" to list.size) + list.mapIndexed { index, kFile -> listOf( - "${entry.key}.${index}.name" to kFile.name, - "${entry.key}.${index}.size" to kFile.size, - "${entry.key}.${index}.content" to kFile.content + "${entry.key}.$index.name" to kFile.name, + "${entry.key}.$index.size" to kFile.size, + "${entry.key}.$index.content" to kFile.content ) }.flatten() } ?: listOf() @@ -104,6 +107,7 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali } }.toMap() val mapper = Mapper.InNullableMapper(FormMapWrapper(map)) + mapper.context = MutableSerialContextImpl().apply { registerSerializer(Date::class, DateSerializer) } mapper.decode(serializer) } } @@ -182,8 +186,8 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali * @param validator optional validation function * @return current form */ - fun <C : KDateFormControl> add( - key: KProperty1<K, KDate?>, control: C, required: Boolean = false, requiredMessage: String? = null, + fun <C : DateFormControl> add( + key: KProperty1<K, Date?>, control: C, required: Boolean = false, requiredMessage: String? = null, validatorMessage: ((C) -> String?)? = null, validator: ((C) -> Boolean?)? = null ): Form<K> { @@ -278,7 +282,7 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali * @return data model as JSON */ fun getDataJson(): Json { - return kotlin.js.JSON.parse(JSON.stringify(serializer, getData())) + return kotlin.js.JSON.parse(JSON.plain.stringify(serializer, getData())) } /** diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt index 6aff842a..6759fef9 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt @@ -22,8 +22,8 @@ package pl.treksoft.kvision.form import pl.treksoft.kvision.core.Component -import pl.treksoft.kvision.types.KDate import pl.treksoft.kvision.types.KFile +import kotlin.js.Date /** * Input controls sizes. @@ -190,15 +190,15 @@ interface BoolFormControl : FormControl { /** * Base interface of a form control with a date value. */ -interface KDateFormControl : FormControl { +interface DateFormControl : FormControl { /** * Date value. */ - var value: KDate? + var value: Date? - override fun getValue(): KDate? = value + override fun getValue(): Date? = value override fun setValue(v: Any?) { - value = v as? KDate + value = v as? Date } override fun getValueAsString(): String? = value?.toString() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt index 3811ed82..3533dee5 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt @@ -32,8 +32,8 @@ import pl.treksoft.kvision.form.check.Radio import pl.treksoft.kvision.html.TAG import pl.treksoft.kvision.html.Tag import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.types.KDate import pl.treksoft.kvision.types.KFile +import kotlin.js.Date import kotlin.js.Json import kotlin.reflect.KProperty1 @@ -287,8 +287,8 @@ open class FormPanel<K : Any>( * @param validator optional validation function * @return current form panel */ - open fun <C : KDateFormControl> add( - key: KProperty1<K, KDate?>, control: C, required: Boolean = false, requiredMessage: String? = null, + open fun <C : DateFormControl> add( + key: KProperty1<K, Date?>, control: C, required: Boolean = false, requiredMessage: String? = null, validatorMessage: ((C) -> String?)? = null, validator: ((C) -> Boolean?)? = null ): FormPanel<K> { diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt index f79c1b48..2df3a055 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt @@ -67,14 +67,14 @@ open class CheckInput( /** * The selection state of the input. */ - var value by refreshOnUpdate(value, { refreshState() }) + var value by refreshOnUpdate(value) { refreshState() } /** * The value attribute of the generated HTML input element. * * This value is placed directly in generated HTML code, while the [value] property is dynamically * bound to the input selection state. */ - var startValue by refreshOnUpdate(value, { this.value = it; refresh() }) + var startValue by refreshOnUpdate(value) { this.value = it; refresh() } /** * The type of the generated HTML input element. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt index fdc1e3af..e2f27c36 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt @@ -54,12 +54,12 @@ open class RadioGroup( /** * A list of options (label to value pairs) for the group. */ - var options by refreshOnUpdate(options, { setChildrenFromOptions() }) + var options by refreshOnUpdate(options) { setChildrenFromOptions() } /** * A value of the selected option. */ - override var value by refreshOnUpdate(value, { setValueToChildren(it) }) + override var value by refreshOnUpdate(value) { setValueToChildren(it) } /** * Determines if the options are rendered inline. diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt index e41cfb8f..3a06f47b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt @@ -51,14 +51,14 @@ abstract class AbstractTextInput( /** * Text input value. */ - var value by refreshOnUpdate(value, { refreshState() }) + var value by refreshOnUpdate(value) { refreshState() } /** * The value attribute of the generated HTML input element. * * This value is placed directly in generated HTML code, while the [value] property is dynamically * bound to the text input value. */ - var startValue by refreshOnUpdate(value, { this.value = it; refresh() }) + var startValue by refreshOnUpdate(value) { this.value = it; refresh() } /** * The placeholder for the text input. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Template.kt b/src/main/kotlin/pl/treksoft/kvision/html/Template.kt index 9a52eb5b..1f377406 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Template.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Template.kt @@ -47,6 +47,6 @@ interface Template { set(value) { if (!rich) rich = true templateDataObj = value - content = template?.invoke(value) ?: templates.get(I18n.language)?.invoke(value) + content = template?.invoke(value) ?: templates[I18n.language]?.invoke(value) } } diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt index 693ecbaa..e16ca87e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt @@ -78,7 +78,7 @@ open class Confirm( /** * Determines if Cancel button is visible. */ - var cancelVisible by refreshOnUpdate(cancelVisible, { refreshCancelButton() }) + var cancelVisible by refreshOnUpdate(cancelVisible) { refreshCancelButton() } /** * Yes button text. diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Dialog.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Dialog.kt index d5695db3..e67a6f17 100644 --- a/src/main/kotlin/pl/treksoft/kvision/modal/Dialog.kt +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Dialog.kt @@ -37,7 +37,6 @@ import kotlin.coroutines.resume * @param classes a set of CSS class names * @param init an initializer extension function */ -@Suppress("EXPERIMENTAL_FEATURE_WARNING") open class Dialog<R>( caption: String? = null, closeButton: Boolean = true, size: ModalSize? = null, animation: Boolean = true, escape: Boolean = true, diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt index e09e9f7f..a0456e7e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt @@ -218,20 +218,20 @@ open class Modal( keyboard = escape backdrop = if (escape) "true" else "static" }) - this.getElementJQuery()?.on("show.bs.modal", { e, _ -> + this.getElementJQuery()?.on("show.bs.modal") { e, _ -> this.dispatchEvent("showBsModal", obj { detail = e }) - }) - this.getElementJQuery()?.on("shown.bs.modal", { e, _ -> + } + this.getElementJQuery()?.on("shown.bs.modal") { e, _ -> this.dispatchEvent("shownBsModal", obj { detail = e }) - }) - this.getElementJQuery()?.on("hide.bs.modal", { e, _ -> + } + this.getElementJQuery()?.on("hide.bs.modal") { e, _ -> this.dispatchEvent("hideBsModal", obj { detail = e }) - }) - this.getElementJQuery()?.on("hidden.bs.modal", { e, _ -> + } + this.getElementJQuery()?.on("hidden.bs.modal") { e, _ -> this.visible = false hide() this.dispatchEvent("hiddenBsModal", obj { detail = e }) - }) + } } override fun hide(): Widget { diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt index d22a285b..310d4d49 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt @@ -104,7 +104,7 @@ open class FlexPanel( /** * CSS flexbox direction. */ - var direction by refreshOnUpdate(direction, { refreshSpacing(); refresh() }) + var direction by refreshOnUpdate(direction) { refreshSpacing(); refresh() } /** * CSS flexbox wrap mode. */ @@ -124,7 +124,7 @@ open class FlexPanel( /** * The spacing between columns/rows. */ - var spacing by refreshOnUpdate(spacing, { refreshSpacing(); refresh() }) + var spacing by refreshOnUpdate(spacing) { refreshSpacing(); refresh() } init { @Suppress("LeakingThis") diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt index edcf5789..7a5b07d6 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt @@ -62,7 +62,7 @@ open class ResponsiveGridPanel( /** * Text align of grid cells. */ - var align by refreshOnUpdate(align, { refreshRowContainers() }) + var align by refreshOnUpdate(align) { refreshRowContainers() } internal val map = mutableMapOf<Int, MutableMap<Int, WidgetParam>>() private var auto: Boolean = true @@ -86,7 +86,7 @@ open class ResponsiveGridPanel( val cCol = maxOf(col, 0) if (row > rows - 1) rows = cRow + 1 if (col > cols - 1) cols = cCol + 1 - map.getOrPut(cRow, { mutableMapOf() })[cCol] = WidgetParam(child, size, offset) + map.getOrPut(cRow) { mutableMapOf() }[cCol] = WidgetParam(child, size, offset) if (size > 0 || offset > 0) auto = false refreshRowContainers() return this diff --git a/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt b/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt index 479ad62e..256d15d7 100644 --- a/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt +++ b/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt @@ -67,15 +67,15 @@ internal class ProgressIndicator( /** * The current progress. */ - var progress by refreshOnUpdate(progress, { refreshWidth() }) + var progress by refreshOnUpdate(progress) { refreshWidth() } /** * The minimal progress. */ - var min by refreshOnUpdate(min, { refreshWidth() }) + var min by refreshOnUpdate(min) { refreshWidth() } /** * The maximal progress. */ - var max by refreshOnUpdate(max, { refreshWidth() }) + var max by refreshOnUpdate(max) { refreshWidth() } /** * The style of the progress indicator. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/remote/CallAgent.kt b/src/main/kotlin/pl/treksoft/kvision/remote/CallAgent.kt index 4247b0b7..a48a6b1f 100644 --- a/src/main/kotlin/pl/treksoft/kvision/remote/CallAgent.kt +++ b/src/main/kotlin/pl/treksoft/kvision/remote/CallAgent.kt @@ -21,10 +21,10 @@ */ package pl.treksoft.kvision.remote -import kotlinx.serialization.json.JSON import pl.treksoft.jquery.JQueryAjaxSettings import pl.treksoft.jquery.JQueryXHR import pl.treksoft.jquery.jQuery +import pl.treksoft.kvision.utils.JSON import pl.treksoft.kvision.utils.obj import kotlin.js.Promise import kotlin.js.undefined @@ -56,8 +56,8 @@ open class CallAgent { method: RpcHttpMethod = RpcHttpMethod.POST ): Promise<String> { val jsonRpcRequest = JsonRpcRequest(counter++, url, data) - val jsonData = JSON.stringify(jsonRpcRequest) - return Promise({ resolve, reject -> + val jsonData = JSON.plain.stringify(jsonRpcRequest) + return Promise { resolve, reject -> jQuery.ajax(url, obj { this.contentType = "application/json" this.data = jsonData @@ -85,7 +85,7 @@ open class CallAgent { } } }) - }) + } } /** @@ -103,7 +103,7 @@ open class CallAgent { contentType: String = "application/json", beforeSend: ((JQueryXHR, JQueryAjaxSettings) -> Boolean)? = null ): Promise<dynamic> { - return Promise({ resolve, reject -> + return Promise { resolve, reject -> jQuery.ajax(url, obj { this.contentType = contentType this.data = data @@ -127,6 +127,6 @@ open class CallAgent { } this.beforeSend = beforeSend }) - }) + } } } diff --git a/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt b/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt index 25c20445..b4c8561e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt +++ b/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt @@ -24,25 +24,24 @@ package pl.treksoft.kvision.remote import kotlinx.coroutines.Deferred import kotlinx.coroutines.asDeferred import kotlinx.serialization.KSerializer -import kotlinx.serialization.internal.ArrayListSerializer -import kotlinx.serialization.internal.BooleanSerializer -import kotlinx.serialization.internal.CharSerializer -import kotlinx.serialization.internal.DoubleSerializer -import kotlinx.serialization.internal.LongSerializer -import kotlinx.serialization.internal.StringSerializer -import kotlinx.serialization.json.JSON +import kotlinx.serialization.internal.* import kotlinx.serialization.list import kotlinx.serialization.serializer -import kotlin.js.js +import pl.treksoft.kvision.types.DateSerializer +import pl.treksoft.kvision.types.toStringF +import pl.treksoft.kvision.utils.JSON +import kotlin.js.Date import kotlin.reflect.KClass import kotlin.js.JSON as NativeJSON -internal class NonStandardTypeException(type: String) : Exception("Non standard type: $type!") +internal class NotStandardTypeException(type: String) : Exception("Not a standard type: $type!") + +internal class NotEnumTypeException : Exception("Not the Enum type!") /** * Client side agent for JSON-RPC remote calls. */ -@Suppress("EXPERIMENTAL_FEATURE_WARNING", "LargeClass", "TooManyFunctions") +@Suppress("LargeClass", "TooManyFunctions") open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { val callAgent = CallAgent() @@ -55,9 +54,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, method = method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -72,9 +77,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, method = method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -92,9 +102,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { return callAgent.jsonRpcCall(url, listOf(data), method).then { try { @Suppress("UNCHECKED_CAST") - deserialize<RET>(it, (RET::class as KClass<Any>).js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + deserialize<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -111,9 +126,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -131,9 +151,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -151,9 +177,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -173,9 +204,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -195,9 +232,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -224,9 +266,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -253,9 +301,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -287,9 +340,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4, data5), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -321,9 +380,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4, data5), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -337,18 +401,81 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { inline fun <reified PAR> serialize(value: PAR, serializer: KSerializer<PAR>?): String? { return value?.let { if (serializer != null) { - JSON.stringify(serializer, it) + JSON.plain.stringify(serializer, it) } else { - if (it is Enum<*>) { - "\"$it\"" - } else { - try { + @Suppress("UNCHECKED_CAST") + trySerialize((PAR::class as KClass<Any>), it as Any) + } + } + } + + /** + * @suppress + * Internal function + */ + @Suppress("ComplexMethod", "TooGenericExceptionCaught", "NestedBlockDepth") + fun trySerialize(kClass: KClass<Any>, value: Any): String { + return if (value is List<*>) { + if (value.size > 0) { + when { + value[0] is String -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(StringSerializer) as KSerializer<Any>, value) + value[0] is Date -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(DateSerializer) as KSerializer<Any>, value) + value[0] is Int -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(IntSerializer) as KSerializer<Any>, value) + value[0] is Long -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(LongSerializer) as KSerializer<Any>, value) + value[0] is Boolean -> @Suppress("UNCHECKED_CAST") - JSON.stringify((PAR::class as KClass<Any>).serializer(), it as Any) + JSON.plain.stringify(ArrayListSerializer(BooleanSerializer) as KSerializer<Any>, value) + value[0] is Float -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(FloatSerializer) as KSerializer<Any>, value) + value[0] is Double -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(DoubleSerializer) as KSerializer<Any>, value) + value[0] is Char -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(CharSerializer) as KSerializer<Any>, value) + value[0] is Short -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(ShortSerializer) as KSerializer<Any>, value) + value[0] is Byte -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(ByteSerializer) as KSerializer<Any>, value) + value[0] is Enum<*> -> "[" + value.joinToString(",") { "\"$it\"" } + "]" + else -> try { + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(kClass.serializer()) as KSerializer<Any>, value) } catch (e: Throwable) { - it.toString() + try { + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(StringSerializer) as KSerializer<Any>, value) + } catch (e: Throwable) { + value.toString() + } } } + } else { + "[]" + } + } else { + when (value) { + is Enum<*> -> "\"$value\"" + is String -> value + is Char -> "\"$value\"" + is Date -> "\"${value.toStringF()}\"" + else -> try { + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(kClass.serializer(), value) + } catch (e: Throwable) { + value.toString() + } } } } @@ -357,15 +484,37 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { * @suppress * Internal function */ - @Suppress("UNCHECKED_CAST") - fun <RET> deserialize(value: String, type: String): RET { - return when (type) { - "String" -> JSON.parse(StringSerializer, value) as RET - "Number" -> JSON.parse(DoubleSerializer, value) as RET - "Long" -> JSON.parse(LongSerializer, value) as RET - "Boolean" -> JSON.parse(BooleanSerializer, value) as RET - "Char" -> JSON.parse(CharSerializer, value) as RET - else -> throw NonStandardTypeException(type) + @Suppress("UNCHECKED_CAST", "ComplexMethod") + fun <RET> deserialize(value: String, jsType: String): RET { + return when (jsType) { + "String" -> JSON.plain.parse(StringSerializer, value) as RET + "Number" -> JSON.plain.parse(DoubleSerializer, value) as RET + "Long" -> JSON.plain.parse(LongSerializer, value) as RET + "Boolean" -> JSON.plain.parse(BooleanSerializer, value) as RET + "BoxedChar" -> JSON.plain.parse(CharSerializer, value) as RET + "Short" -> JSON.plain.parse(ShortSerializer, value) as RET + "Date" -> JSON.plain.parse(DateSerializer, value) as RET + "Byte" -> JSON.plain.parse(ByteSerializer, value) as RET + else -> throw NotStandardTypeException(jsType) + } + } + + /** + * @suppress + * Internal function + */ + @Suppress("UNCHECKED_CAST", "ComplexMethod") + fun <RET> deserializeList(value: String, jsType: String): List<RET> { + return when (jsType) { + "String" -> JSON.plain.parse(ArrayListSerializer(StringSerializer), value) as List<RET> + "Number" -> JSON.plain.parse(ArrayListSerializer(DoubleSerializer), value) as List<RET> + "Long" -> JSON.plain.parse(ArrayListSerializer(LongSerializer), value) as List<RET> + "Boolean" -> JSON.plain.parse(ArrayListSerializer(BooleanSerializer), value) as List<RET> + "BoxedChar" -> JSON.plain.parse(ArrayListSerializer(CharSerializer), value) as List<RET> + "Short" -> JSON.plain.parse(ArrayListSerializer(ShortSerializer), value) as List<RET> + "Date" -> JSON.plain.parse(ArrayListSerializer(DateSerializer), value) as List<RET> + "Byte" -> JSON.plain.parse(ArrayListSerializer(ByteSerializer), value) as List<RET> + else -> throw NotStandardTypeException(jsType) } } @@ -373,15 +522,41 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { * @suppress * Internal function */ - @Suppress("UNCHECKED_CAST") - fun <RET> deserializeLists(value: String, type: String): List<RET> { - return when (type) { - "String" -> JSON.parse(ArrayListSerializer(StringSerializer), value) as List<RET> - "Number" -> JSON.parse(ArrayListSerializer(DoubleSerializer), value) as List<RET> - "Long" -> JSON.parse(ArrayListSerializer(LongSerializer), value) as List<RET> - "Boolean" -> JSON.parse(ArrayListSerializer(BooleanSerializer), value) as List<RET> - "Char" -> JSON.parse(ArrayListSerializer(CharSerializer), value) as List<RET> - else -> throw NonStandardTypeException(type) + @Suppress("TooGenericExceptionCaught", "ThrowsCount") + fun tryDeserializeEnum(kClass: KClass<Any>, value: String): Any { + return try { + if (kClass.asDynamic().jClass.`$metadata$`.interfaces[0].name == "Enum") { + findEnumValue(kClass, JSON.plain.parse(StringSerializer, value)) ?: throw NotEnumTypeException() + } else { + throw NotEnumTypeException() + } + } catch (e: Throwable) { + throw NotEnumTypeException() + } + } + + /** + * @suppress + * Internal function + */ + @Suppress("TooGenericExceptionCaught", "ThrowsCount") + fun tryDeserializeEnumList(kClass: KClass<Any>, value: String): List<Any> { + return try { + if (kClass.asDynamic().jClass.`$metadata$`.interfaces[0].name == "Enum") { + JSON.plain.parse(ArrayListSerializer(StringSerializer), value).map { + findEnumValue(kClass, JSON.plain.parse(StringSerializer, it)) ?: throw NotEnumTypeException() + } + } else { + throw NotEnumTypeException() + } + } catch (e: Throwable) { + throw NotEnumTypeException() + } + } + + private fun findEnumValue(kClass: KClass<Any>, value: String): Any? { + return (kClass.asDynamic().jClass.values() as Array<Any>).find { + it.asDynamic().name == value } } } diff --git a/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt b/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt index 56141f5e..bf77a06b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt +++ b/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt @@ -61,7 +61,6 @@ class LoginService { /** * Pac4j form login dispatcher. */ -@Suppress("EXPERIMENTAL_FEATURE_WARNING") abstract class SecurityMgr { private var isLoggedIn = false diff --git a/src/main/kotlin/pl/treksoft/kvision/table/Table.kt b/src/main/kotlin/pl/treksoft/kvision/table/Table.kt index 1d44633d..f78d3f84 100644 --- a/src/main/kotlin/pl/treksoft/kvision/table/Table.kt +++ b/src/main/kotlin/pl/treksoft/kvision/table/Table.kt @@ -63,7 +63,7 @@ open class Table( /** * Table headers names. */ - var headerNames by refreshOnUpdate(headerNames, { refreshHeaders() }) + var headerNames by refreshOnUpdate(headerNames) { refreshHeaders() } /** * Table types. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/types/Date.kt b/src/main/kotlin/pl/treksoft/kvision/types/Date.kt new file mode 100644 index 00000000..b6b60d5c --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/types/Date.kt @@ -0,0 +1,65 @@ +/* + * 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.types + +import kotlinx.serialization.Decoder +import kotlinx.serialization.Encoder +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialDescriptor +import kotlinx.serialization.internal.SerialClassDescImpl +import pl.treksoft.kvision.KVManager +import kotlin.js.Date + +actual typealias Date = kotlin.js.Date + +/** + * Extension function to convert String to Date with a given date format. + * @param format date/time format + * @return Date object + */ +@Suppress("UnsafeCastFromDynamic") +actual fun String.toDateF(format: String): Date { + val result = KVManager.fecha.parse(this, format) + return if (result) result else Date() +} + +/** + * Extension function to convert Date to String with a given date format. + * @param format date/time format + * @return String object + */ +@Suppress("UnsafeCastFromDynamic") +actual fun Date.toStringF(format: String): String { + return KVManager.fecha.format(this, format) +} + +object DateSerializer : KSerializer<Date> { + override val descriptor: SerialDescriptor = SerialClassDescImpl("kotlin.js.Date") + + override fun deserialize(input: Decoder): Date { + return input.decodeString().toDateF() + } + + override fun serialize(output: Encoder, obj: Date) { + output.encodeString(obj.toStringF()) + } +} diff --git a/kvision-modules/kvision-common/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt b/src/main/kotlin/pl/treksoft/kvision/utils/JSON.kt index 0cb1a1f1..e47351ac 100644 --- a/kvision-modules/kvision-common/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt +++ b/src/main/kotlin/pl/treksoft/kvision/utils/JSON.kt @@ -19,31 +19,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package pl.treksoft.kvision.types +package pl.treksoft.kvision.utils -import kotlinx.serialization.Serializable +import kotlinx.serialization.context.SimpleModule +import kotlinx.serialization.json.JSON +import pl.treksoft.kvision.types.DateSerializer +import kotlin.js.Date -expect val KDATE_FORMAT: String +object JSON { -/** - * A serializable wrapper for a multiplatform Date type. - */ -@Serializable -data class KDate(val time: Long) { - constructor() : this(now().time) - constructor(str: String) : this(str.toKDateF(KDATE_FORMAT).time) - - override fun toString(): String { - return this.toStringF(KDATE_FORMAT) + val plain = JSON().apply { + install(SimpleModule(Date::class, DateSerializer)) } - companion object { - fun now() = nowDate() + val nonstrict = JSON(strictMode = false).apply { + install(SimpleModule(Date::class, DateSerializer)) } -} -expect fun nowDate(): KDate - -expect fun String.toKDateF(format: String): KDate - -expect fun KDate.toStringF(format: String): String +} diff --git a/src/main/kotlin/pl/treksoft/kvision/utils/Utils.kt b/src/main/kotlin/pl/treksoft/kvision/utils/Utils.kt index e5f608c2..e252d672 100644 --- a/src/main/kotlin/pl/treksoft/kvision/utils/Utils.kt +++ b/src/main/kotlin/pl/treksoft/kvision/utils/Utils.kt @@ -27,13 +27,11 @@ import com.lightningkite.kotlin.observable.list.ObservableList import kotlinx.coroutines.suspendCancellableCoroutine import org.w3c.files.File import org.w3c.files.FileReader -import pl.treksoft.kvision.KVManager import pl.treksoft.kvision.core.CssSize import pl.treksoft.kvision.core.UNIT import kotlin.browser.window import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException -import kotlin.js.Date /** * Extension property to convert Int to CSS px units. @@ -186,27 +184,6 @@ fun Int.toHexString(): String { } /** - * Extension function to convert String to Date with a given date format. - * @param format date/time format - * @return Date object - */ -@Suppress("UnsafeCastFromDynamic") -fun String.toDateF(format: String = "YYYY-MM-DD HH:mm:ss"): Date? { - val result = KVManager.fecha.parse(this, format) - return if (result) result else null -} - -/** - * Extension function to convert Date to String with a given date format. - * @param format date/time format - * @return String object - */ -@Suppress("UnsafeCastFromDynamic") -fun Date.toStringF(format: String = "YYYY-MM-DD HH:mm:ss"): String { - return KVManager.fecha.format(this, format) -} - -/** * Utility function to detect Internet Explorer 11. * @return true if the current browser is IE11 */ @@ -216,7 +193,6 @@ fun isIE11(): Boolean = window.navigator.userAgent.matches("Trident\\/7\\.") * Suspending extension function to get file content. * @return file content */ -@Suppress("EXPERIMENTAL_FEATURE_WARNING") suspend fun File.getContent(): String = suspendCancellableCoroutine { cont -> val reader = FileReader() reader.onload = { diff --git a/src/main/kotlin/pl/treksoft/kvision/window/Window.kt b/src/main/kotlin/pl/treksoft/kvision/window/Window.kt index bdfb2c0f..ee20dc51 100644 --- a/src/main/kotlin/pl/treksoft/kvision/window/Window.kt +++ b/src/main/kotlin/pl/treksoft/kvision/window/Window.kt @@ -105,11 +105,11 @@ open class Window( /** * Determines if the window is resizable. */ - var isResizable by refreshOnUpdate(isResizable, { checkIsResizable() }) + var isResizable by refreshOnUpdate(isResizable) { checkIsResizable() } /** * Determines if the window is draggable. */ - var isDraggable by refreshOnUpdate(isDraggable, { checkIsDraggable(); checkHeaderVisibility() }) + var isDraggable by refreshOnUpdate(isDraggable) { checkIsDraggable(); checkHeaderVisibility() } /** * Determines if Close button is visible. */ @@ -201,7 +201,7 @@ open class Window( } kotlin.browser.window.addEventListener("mousemove", moveCallback) var upCallback: ((Event) -> Unit)? = null - upCallback = { _ -> + upCallback = { isDrag = false kotlin.browser.window.removeEventListener("mousemove", moveCallback) kotlin.browser.window.removeEventListener("mouseup", upCallback) diff --git a/src/test/kotlin/test/pl/treksoft/kvision/panel/RootSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/panel/RootSpec.kt index 9e4342cc..659dac9a 100644 --- a/src/test/kotlin/test/pl/treksoft/kvision/panel/RootSpec.kt +++ b/src/test/kotlin/test/pl/treksoft/kvision/panel/RootSpec.kt @@ -21,7 +21,6 @@ */ package test.pl.treksoft.kvision.panel -import pl.treksoft.kvision.modal.Modal import pl.treksoft.kvision.panel.Root import test.pl.treksoft.kvision.DomSpec import kotlin.browser.document @@ -40,7 +39,7 @@ class RootSpec : DomSpec { } @Test - fun getSnClass_Fluid() { + fun getSnClassFluid() { run { Root("test", fixed = true) val rootElem = document.getElementById("test") diff --git a/src/test/kotlin/test/pl/treksoft/kvision/utils/UtilsSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/utils/UtilsSpec.kt index 97e157bd..36217dd7 100644 --- a/src/test/kotlin/test/pl/treksoft/kvision/utils/UtilsSpec.kt +++ b/src/test/kotlin/test/pl/treksoft/kvision/utils/UtilsSpec.kt @@ -21,9 +21,9 @@ */ package test.pl.treksoft.kvision.utils -import pl.treksoft.kvision.utils.toDateF +import pl.treksoft.kvision.types.toDateF +import pl.treksoft.kvision.types.toStringF import pl.treksoft.kvision.utils.toHexString -import pl.treksoft.kvision.utils.toStringF import test.pl.treksoft.kvision.SimpleSpec import kotlin.js.Date import kotlin.test.Test @@ -47,7 +47,7 @@ class UtilsSpec : SimpleSpec { val res = "2017-03-14 14:50:35".toDateF() assertEquals( js("new Date(2017,2,14,14,50,35).getTime()"), - res?.getTime(), + res.getTime(), "Should convert String value to Date" ) } |