diff --git a/Readme.md b/Readme.md index 878eea8..f70829f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,7 @@ # HtmlFlow [![Build Status](https://sonarcloud.io/api/project_badges/measure?project=com.github.xmlet%3Ahtmlflow&metric=alert_status)](https://sonarcloud.io/dashboard?id=com.github.xmlet%3Ahtmlflow) -[![Maven Central Version](https://maven-badges.herokuapp.com/maven-central/com.github.xmlet/htmlflow/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cxmlet%20htmlflow) +[![Maven Central Version](https://maven-badges.herokuapp.com/maven-central/com.github.xmlet/htmlflow/badge.svg)](https://search.maven.org/artifact/com.github.xmlet/htmlflow) [![Coverage Status](https://sonarcloud.io/api/project_badges/measure?project=com.github.xmlet%3Ahtmlflow&metric=coverage)](https://sonarcloud.io/component_measures?id=com.github.xmlet%3Ahtmlflow&metric=Coverage) [![javadoc HtmlApiFaster](https://img.shields.io/badge/javadocs-HtmlApiFaster-blue)](https://javadoc.io/doc/com.github.xmlet/htmlApiFaster/latest/org/xmlet/htmlapifaster/package-summary.html) [![javadoc HtmlFlow](https://img.shields.io/badge/javadocs-HtmlFlow-blue)](https://javadoc.io/doc/com.github.xmlet/htmlflow) @@ -9,7 +9,7 @@ [![Petclinic Sample](https://img.shields.io/badge/petclinic-Spring%20boot%20sample%20with%20HtmlFlow-blue)](https://github.com/xmlet/spring-petclinic) [**HtmlFlow**](https://htmlflow.org/) is a Java DSL to write **typesafe HTML** -in a fluent style. +in a fluent style, in both **Java** or **Kotlin** (for Kotlin check the [examples](https://htmlflow.org/features#data-binding)) You may use the utility `Flowifier.fromHtml(String html)` if you need the HtmlFlow definition for an existing HTML source: @@ -125,10 +125,10 @@ Bonus points it also produces only valid HTML according to HTML 5.2. ## Installation First, in order to include it to your Gradle project, simply add the following dependency, -or use any other form provided in [Maven Central Repository](https://search.maven.org/artifact/com.github.xmlet/htmlflow/4.4/jar): +or use any other form provided in [Maven Central Repository](https://search.maven.org/artifact/com.github.xmlet/htmlflow/4.6/jar): ```xml -implementation 'com.github.xmlet:htmlflow:4.4' +implementation 'com.github.xmlet:htmlflow:4.6' ``` You can also download the artifact directly from [Maven diff --git a/pom.xml b/pom.xml index 19dd89d..8f7e549 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.github.xmlet htmlflow - 4.6-SNAPSHOT + 4.7-SNAPSHOT jar ${project.groupId}:${project.artifactId} @@ -43,16 +43,16 @@ - org.slf4j - slf4j-api - 2.0.3 - test + org.slf4j + slf4j-api + 2.0.3 + test - org.slf4j - slf4j-simple - 2.0.3 - test + org.slf4j + slf4j-simple + 2.0.3 + test com.github.xmlet @@ -118,6 +118,18 @@ guava 33.0.0-jre + + com.squareup + kotlinpoet-jvm + 1.16.0 + provided + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + provided + UTF-8 @@ -130,6 +142,25 @@ + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + htmlflow.propertiesGenerator.ReflectionKt + true + compile + + + + teste + process-classes + + java + + + + org.apache.maven.plugins maven-surefire-plugin @@ -206,7 +237,7 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.12 default-prepare-agent diff --git a/src/main/kotlin/htmlflow/HtmlFlowExtensionProperties.kt b/src/main/kotlin/htmlflow/HtmlFlowExtensionProperties.kt new file mode 100644 index 0000000..a1fdaf5 --- /dev/null +++ b/src/main/kotlin/htmlflow/HtmlFlowExtensionProperties.kt @@ -0,0 +1,1156 @@ +package htmlflow + +import kotlin.Unit +import org.xmlet.htmlapifaster.A +import org.xmlet.htmlapifaster.Abbr +import org.xmlet.htmlapifaster.Address +import org.xmlet.htmlapifaster.Area +import org.xmlet.htmlapifaster.Article +import org.xmlet.htmlapifaster.Aside +import org.xmlet.htmlapifaster.Audio +import org.xmlet.htmlapifaster.B +import org.xmlet.htmlapifaster.Base +import org.xmlet.htmlapifaster.Bdi +import org.xmlet.htmlapifaster.Bdo +import org.xmlet.htmlapifaster.Blockquote +import org.xmlet.htmlapifaster.Body +import org.xmlet.htmlapifaster.Br +import org.xmlet.htmlapifaster.Button +import org.xmlet.htmlapifaster.Canvas +import org.xmlet.htmlapifaster.Caption +import org.xmlet.htmlapifaster.Cite +import org.xmlet.htmlapifaster.Code +import org.xmlet.htmlapifaster.Col +import org.xmlet.htmlapifaster.Colgroup +import org.xmlet.htmlapifaster.Data +import org.xmlet.htmlapifaster.Datalist +import org.xmlet.htmlapifaster.Dd +import org.xmlet.htmlapifaster.Del +import org.xmlet.htmlapifaster.Details +import org.xmlet.htmlapifaster.DetailsComplete +import org.xmlet.htmlapifaster.DetailsSummary +import org.xmlet.htmlapifaster.Dfn +import org.xmlet.htmlapifaster.Dialog +import org.xmlet.htmlapifaster.Div +import org.xmlet.htmlapifaster.Dl +import org.xmlet.htmlapifaster.Dt +import org.xmlet.htmlapifaster.Element +import org.xmlet.htmlapifaster.Em +import org.xmlet.htmlapifaster.Embed +import org.xmlet.htmlapifaster.Fieldset +import org.xmlet.htmlapifaster.Figcaption +import org.xmlet.htmlapifaster.Figure +import org.xmlet.htmlapifaster.Footer +import org.xmlet.htmlapifaster.Form +import org.xmlet.htmlapifaster.H1 +import org.xmlet.htmlapifaster.H2 +import org.xmlet.htmlapifaster.H3 +import org.xmlet.htmlapifaster.H4 +import org.xmlet.htmlapifaster.H5 +import org.xmlet.htmlapifaster.H6 +import org.xmlet.htmlapifaster.Head +import org.xmlet.htmlapifaster.Header +import org.xmlet.htmlapifaster.Hr +import org.xmlet.htmlapifaster.I +import org.xmlet.htmlapifaster.Iframe +import org.xmlet.htmlapifaster.Img +import org.xmlet.htmlapifaster.Input +import org.xmlet.htmlapifaster.Ins +import org.xmlet.htmlapifaster.Kbd +import org.xmlet.htmlapifaster.Label +import org.xmlet.htmlapifaster.Legend +import org.xmlet.htmlapifaster.Li +import org.xmlet.htmlapifaster.Link +import org.xmlet.htmlapifaster.Main +import org.xmlet.htmlapifaster.Map +import org.xmlet.htmlapifaster.Mark +import org.xmlet.htmlapifaster.Math +import org.xmlet.htmlapifaster.Meta +import org.xmlet.htmlapifaster.Meter +import org.xmlet.htmlapifaster.Nav +import org.xmlet.htmlapifaster.Noscript +import org.xmlet.htmlapifaster.Object +import org.xmlet.htmlapifaster.Ol +import org.xmlet.htmlapifaster.Optgroup +import org.xmlet.htmlapifaster.Option +import org.xmlet.htmlapifaster.Output +import org.xmlet.htmlapifaster.P +import org.xmlet.htmlapifaster.Param +import org.xmlet.htmlapifaster.Picture +import org.xmlet.htmlapifaster.Pre +import org.xmlet.htmlapifaster.Progress +import org.xmlet.htmlapifaster.Q +import org.xmlet.htmlapifaster.Rb +import org.xmlet.htmlapifaster.Root +import org.xmlet.htmlapifaster.Rp +import org.xmlet.htmlapifaster.Rt +import org.xmlet.htmlapifaster.Rtc +import org.xmlet.htmlapifaster.Ruby +import org.xmlet.htmlapifaster.S +import org.xmlet.htmlapifaster.Samp +import org.xmlet.htmlapifaster.Script +import org.xmlet.htmlapifaster.Section +import org.xmlet.htmlapifaster.Select +import org.xmlet.htmlapifaster.Small +import org.xmlet.htmlapifaster.Source +import org.xmlet.htmlapifaster.Span +import org.xmlet.htmlapifaster.Strong +import org.xmlet.htmlapifaster.Style +import org.xmlet.htmlapifaster.Sub +import org.xmlet.htmlapifaster.Summary +import org.xmlet.htmlapifaster.Sup +import org.xmlet.htmlapifaster.Svg +import org.xmlet.htmlapifaster.Table +import org.xmlet.htmlapifaster.Tbody +import org.xmlet.htmlapifaster.Td +import org.xmlet.htmlapifaster.Template +import org.xmlet.htmlapifaster.Textarea +import org.xmlet.htmlapifaster.Tfoot +import org.xmlet.htmlapifaster.Th +import org.xmlet.htmlapifaster.Thead +import org.xmlet.htmlapifaster.Time +import org.xmlet.htmlapifaster.Title +import org.xmlet.htmlapifaster.Tr +import org.xmlet.htmlapifaster.Track +import org.xmlet.htmlapifaster.U +import org.xmlet.htmlapifaster.Ul +import org.xmlet.htmlapifaster.Var +import org.xmlet.htmlapifaster.Video +import org.xmlet.htmlapifaster.Wbr + +public inline val , Z : Element<*,*>> T.h2: H2 + get() = H2(this.self()) + +public fun , Z : Element<*,*>> T.h2(block: H2.() -> Unit): T { + val elem = H2(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.mark: Mark + get() = Mark(this.self()) + +public fun , Z : Element<*,*>> T.mark(block: Mark.() -> Unit): T { + val elem = Mark(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.i: I + get() = I(this.self()) + +public fun , Z : Element<*,*>> T.i(block: I.() -> Unit): T { + val elem = I(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.th: Th + get() = Th(this.self()) + +public fun , Z : Element<*,*>> T.th(block: Th.() -> Unit): T { + val elem = Th(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.strong: Strong + get() = Strong(this.self()) + +public fun , Z : Element<*,*>> T.strong(block: Strong.() -> Unit): T { + val elem = Strong(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.col: Col + get() = Col(this.self()) + +public fun , Z : Element<*,*>> T.col(block: Col.() -> Unit): T { + val elem = Col(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.math: Math + get() = Math(this.self()) + +public fun , Z : Element<*,*>> T.math(block: Math.() -> Unit): T { + val elem = Math(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.tbody: Tbody + get() = Tbody(this.self()) + +public fun , Z : Element<*,*>> T.tbody(block: Tbody.() -> Unit): T { + val elem = Tbody(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.textarea: Textarea + get() = Textarea(this.self()) + +public fun , Z : Element<*,*>> T.textarea(block: Textarea.() -> Unit): T { + val elem = Textarea(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.source: Source + get() = Source(this.self()) + +public fun , Z : Element<*,*>> T.source(block: Source.() -> Unit): T { + val elem = Source(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.rp: Rp + get() = Rp(this.self()) + +public fun , Z : Element<*,*>> T.rp(block: Rp.() -> Unit): T { + val elem = Rp(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.picture: Picture + get() = Picture(this.self()) + +public fun , Z : Element<*,*>> T.picture(block: Picture.() -> Unit): T { + val elem = Picture(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.p: P + get() = P(this.self()) + +public fun , Z : Element<*,*>> T.p(block: P.() -> Unit): T { + val elem = P(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.dt: Dt + get() = Dt(this.self()) + +public fun , Z : Element<*,*>> T.dt(block: Dt.() -> Unit): T { + val elem = Dt(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.label: Label + get() = Label(this.self()) + +public fun , Z : Element<*,*>> T.label(block: Label.() -> Unit): T { + val elem = Label(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.embed: Embed + get() = Embed(this.self()) + +public fun , Z : Element<*,*>> T.embed(block: Embed.() -> Unit): T { + val elem = Embed(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.rt: Rt + get() = Rt(this.self()) + +public fun , Z : Element<*,*>> T.rt(block: Rt.() -> Unit): T { + val elem = Rt(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.address: Address + get() = Address(this.self()) + +public fun , Z : Element<*,*>> T.address(block: Address.() -> Unit): T { + val elem = Address(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.h4: H4 + get() = H4(this.self()) + +public fun , Z : Element<*,*>> T.h4(block: H4.() -> Unit): T { + val elem = H4(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.`data`: Data + get() = Data(this.self()) + +public fun , Z : Element<*,*>> T.`data`(block: Data.() -> Unit): T { + val elem = Data(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.legend: Legend + get() = Legend(this.self()) + +public fun , Z : Element<*,*>> T.legend(block: Legend.() -> Unit): T { + val elem = Legend(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.option: Option + get() = Option(this.self()) + +public fun , Z : Element<*,*>> T.option(block: Option.() -> Unit): T { + val elem = Option(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.detailssummary: DetailsSummary + get() = DetailsSummary(this.self()) + +public fun , Z : Element<*,*>> + T.detailssummary(block: DetailsSummary.() -> Unit): T { + val elem = DetailsSummary(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.track: Track + get() = Track(this.self()) + +public fun , Z : Element<*,*>> T.track(block: Track.() -> Unit): T { + val elem = Track(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.figcaption: Figcaption + get() = Figcaption(this.self()) + +public fun , Z : Element<*,*>> T.figcaption(block: Figcaption.() -> Unit): T { + val elem = Figcaption(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.sub: Sub + get() = Sub(this.self()) + +public fun , Z : Element<*,*>> T.sub(block: Sub.() -> Unit): T { + val elem = Sub(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.details: Details + get() = Details(this.self()) + +public fun , Z : Element<*,*>> T.details(block: Details.() -> Unit): T { + val elem = Details(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.aside: Aside + get() = Aside(this.self()) + +public fun , Z : Element<*,*>> T.aside(block: Aside.() -> Unit): T { + val elem = Aside(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.footer: Footer + get() = Footer(this.self()) + +public fun , Z : Element<*,*>> T.footer(block: Footer.() -> Unit): T { + val elem = Footer(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.h6: H6 + get() = H6(this.self()) + +public fun , Z : Element<*,*>> T.h6(block: H6.() -> Unit): T { + val elem = H6(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.hr: Hr + get() = Hr(this.self()) + +public fun , Z : Element<*,*>> T.hr(block: Hr.() -> Unit): T { + val elem = Hr(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.link: Link + get() = Link(this.self()) + +public fun , Z : Element<*,*>> T.link(block: Link.() -> Unit): T { + val elem = Link(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.meta: Meta + get() = Meta(this.self()) + +public fun , Z : Element<*,*>> T.meta(block: Meta.() -> Unit): T { + val elem = Meta(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.`var`: Var + get() = Var(this.self()) + +public fun , Z : Element<*,*>> T.`var`(block: Var.() -> Unit): T { + val elem = Var(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.h1: H1 + get() = H1(this.self()) + +public fun , Z : Element<*,*>> T.h1(block: H1.() -> Unit): T { + val elem = H1(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.q: Q + get() = Q(this.self()) + +public fun , Z : Element<*,*>> T.q(block: Q.() -> Unit): T { + val elem = Q(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.output: Output + get() = Output(this.self()) + +public fun , Z : Element<*,*>> T.output(block: Output.() -> Unit): T { + val elem = Output(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.li: Li + get() = Li(this.self()) + +public fun , Z : Element<*,*>> T.li(block: Li.() -> Unit): T { + val elem = Li(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.span: Span + get() = Span(this.self()) + +public fun , Z : Element<*,*>> T.span(block: Span.() -> Unit): T { + val elem = Span(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.time: Time + get() = Time(this.self()) + +public fun , Z : Element<*,*>> T.time(block: Time.() -> Unit): T { + val elem = Time(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.caption: Caption + get() = Caption(this.self()) + +public fun , Z : Element<*,*>> T.caption(block: Caption.() -> Unit): T { + val elem = Caption(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.progress: Progress + get() = Progress(this.self()) + +public fun , Z : Element<*,*>> T.progress(block: Progress.() -> Unit): T { + val elem = Progress(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.dl: Dl + get() = Dl(this.self()) + +public fun , Z : Element<*,*>> T.dl(block: Dl.() -> Unit): T { + val elem = Dl(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.s: S + get() = S(this.self()) + +public fun , Z : Element<*,*>> T.s(block: S.() -> Unit): T { + val elem = S(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.h3: H3 + get() = H3(this.self()) + +public fun , Z : Element<*,*>> T.h3(block: H3.() -> Unit): T { + val elem = H3(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.cite: Cite + get() = Cite(this.self()) + +public fun , Z : Element<*,*>> T.cite(block: Cite.() -> Unit): T { + val elem = Cite(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.abbr: Abbr + get() = Abbr(this.self()) + +public fun , Z : Element<*,*>> T.abbr(block: Abbr.() -> Unit): T { + val elem = Abbr(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.tr: Tr + get() = Tr(this.self()) + +public fun , Z : Element<*,*>> T.tr(block: Tr.() -> Unit): T { + val elem = Tr(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.`param`: Param + get() = Param(this.self()) + +public fun , Z : Element<*,*>> T.`param`(block: Param.() -> Unit): T { + val elem = Param(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.colgroup: Colgroup + get() = Colgroup(this.self()) + +public fun , Z : Element<*,*>> T.colgroup(block: Colgroup.() -> Unit): T { + val elem = Colgroup(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.dfn: Dfn + get() = Dfn(this.self()) + +public fun , Z : Element<*,*>> T.dfn(block: Dfn.() -> Unit): T { + val elem = Dfn(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.br: Br + get() = Br(this.self()) + +public fun , Z : Element<*,*>> T.br(block: Br.() -> Unit): T { + val elem = Br(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.head: Head + get() = Head(this.self()) + +public fun , Z : Element<*,*>> T.head(block: Head.() -> Unit): T { + val elem = Head(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.table: Table + get() = Table(this.self()) + +public fun , Z : Element<*,*>> T.table(block: Table.() -> Unit): T { + val elem = Table(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.template: Template + get() = Template(this.self()) + +public fun , Z : Element<*,*>> T.template(block: Template.() -> Unit): T { + val elem = Template(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.kbd: Kbd + get() = Kbd(this.self()) + +public fun , Z : Element<*,*>> T.kbd(block: Kbd.() -> Unit): T { + val elem = Kbd(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.h5: H5 + get() = H5(this.self()) + +public fun , Z : Element<*,*>> T.h5(block: H5.() -> Unit): T { + val elem = H5(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.u: U + get() = U(this.self()) + +public fun , Z : Element<*,*>> T.u(block: U.() -> Unit): T { + val elem = U(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.noscript: Noscript + get() = Noscript(this.self()) + +public fun , Z : Element<*,*>> T.noscript(block: Noscript.() -> Unit): T { + val elem = Noscript(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.root: Root + get() = Root(this.self()) + +public fun , Z : Element<*,*>> T.root(block: Root.() -> Unit): T { + val elem = Root(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.div: Div + get() = Div(this.self()) + +public fun , Z : Element<*,*>> T.div(block: Div.() -> Unit): T { + val elem = Div(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.select: Select + get() = Select(this.self()) + +public fun , Z : Element<*,*>> T.select(block: Select.() -> Unit): T { + val elem = Select(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.`header`: Header + get() = Header(this.self()) + +public fun , Z : Element<*,*>> T.`header`(block: Header.() -> Unit): T { + val elem = Header(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.datalist: Datalist + get() = Datalist(this.self()) + +public fun , Z : Element<*,*>> T.datalist(block: Datalist.() -> Unit): T { + val elem = Datalist(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.bdi: Bdi + get() = Bdi(this.self()) + +public fun , Z : Element<*,*>> T.bdi(block: Bdi.() -> Unit): T { + val elem = Bdi(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.rb: Rb + get() = Rb(this.self()) + +public fun , Z : Element<*,*>> T.rb(block: Rb.() -> Unit): T { + val elem = Rb(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.input: Input + get() = Input(this.self()) + +public fun , Z : Element<*,*>> T.input(block: Input.() -> Unit): T { + val elem = Input(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.canvas: Canvas + get() = Canvas(this.self()) + +public fun , Z : Element<*,*>> T.canvas(block: Canvas.() -> Unit): T { + val elem = Canvas(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.b: B + get() = B(this.self()) + +public fun , Z : Element<*,*>> T.b(block: B.() -> Unit): T { + val elem = B(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.ol: Ol + get() = Ol(this.self()) + +public fun , Z : Element<*,*>> T.ol(block: Ol.() -> Unit): T { + val elem = Ol(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.code: Code + get() = Code(this.self()) + +public fun , Z : Element<*,*>> T.code(block: Code.() -> Unit): T { + val elem = Code(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.dd: Dd + get() = Dd(this.self()) + +public fun , Z : Element<*,*>> T.dd(block: Dd.() -> Unit): T { + val elem = Dd(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.title: Title + get() = Title(this.self()) + +public fun , Z : Element<*,*>> T.title(block: Title.() -> Unit): T { + val elem = Title(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.thead: Thead + get() = Thead(this.self()) + +public fun , Z : Element<*,*>> T.thead(block: Thead.() -> Unit): T { + val elem = Thead(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.em: Em + get() = Em(this.self()) + +public fun , Z : Element<*,*>> T.em(block: Em.() -> Unit): T { + val elem = Em(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.fieldset: Fieldset + get() = Fieldset(this.self()) + +public fun , Z : Element<*,*>> T.fieldset(block: Fieldset.() -> Unit): T { + val elem = Fieldset(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.area: Area + get() = Area(this.self()) + +public fun , Z : Element<*,*>> T.area(block: Area.() -> Unit): T { + val elem = Area(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.sup: Sup + get() = Sup(this.self()) + +public fun , Z : Element<*,*>> T.sup(block: Sup.() -> Unit): T { + val elem = Sup(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.style: Style + get() = Style(this.self()) + +public fun , Z : Element<*,*>> T.style(block: Style.() -> Unit): T { + val elem = Style(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.nav: Nav + get() = Nav(this.self()) + +public fun , Z : Element<*,*>> T.nav(block: Nav.() -> Unit): T { + val elem = Nav(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.ul: Ul + get() = Ul(this.self()) + +public fun , Z : Element<*,*>> T.ul(block: Ul.() -> Unit): T { + val elem = Ul(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.tfoot: Tfoot + get() = Tfoot(this.self()) + +public fun , Z : Element<*,*>> T.tfoot(block: Tfoot.() -> Unit): T { + val elem = Tfoot(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.bdo: Bdo + get() = Bdo(this.self()) + +public fun , Z : Element<*,*>> T.bdo(block: Bdo.() -> Unit): T { + val elem = Bdo(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.script: Script + get() = Script(this.self()) + +public fun , Z : Element<*,*>> T.script(block: Script.() -> Unit): T { + val elem = Script(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.section: Section + get() = Section(this.self()) + +public fun , Z : Element<*,*>> T.section(block: Section.() -> Unit): T { + val elem = Section(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.meter: Meter + get() = Meter(this.self()) + +public fun , Z : Element<*,*>> T.meter(block: Meter.() -> Unit): T { + val elem = Meter(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.small: Small + get() = Small(this.self()) + +public fun , Z : Element<*,*>> T.small(block: Small.() -> Unit): T { + val elem = Small(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.form: Form + get() = Form(this.self()) + +public fun , Z : Element<*,*>> T.form(block: Form.() -> Unit): T { + val elem = Form(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.base: Base + get() = Base(this.self()) + +public fun , Z : Element<*,*>> T.base(block: Base.() -> Unit): T { + val elem = Base(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.blockquote: Blockquote + get() = Blockquote(this.self()) + +public fun , Z : Element<*,*>> T.blockquote(block: Blockquote.() -> Unit): T { + val elem = Blockquote(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.audio: Audio + get() = Audio(this.self()) + +public fun , Z : Element<*,*>> T.audio(block: Audio.() -> Unit): T { + val elem = Audio(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.a: A + get() = A(this.self()) + +public fun , Z : Element<*,*>> T.a(block: A.() -> Unit): T { + val elem = A(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.article: Article + get() = Article(this.self()) + +public fun , Z : Element<*,*>> T.article(block: Article.() -> Unit): T { + val elem = Article(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.detailscomplete: DetailsComplete + get() = DetailsComplete(this.self()) + +public fun , Z : Element<*,*>> + T.detailscomplete(block: DetailsComplete.() -> Unit): T { + val elem = DetailsComplete(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.rtc: Rtc + get() = Rtc(this.self()) + +public fun , Z : Element<*,*>> T.rtc(block: Rtc.() -> Unit): T { + val elem = Rtc(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.`object`: Object + get() = Object(this.self()) + +public fun , Z : Element<*,*>> T.`object`(block: Object.() -> Unit): T { + val elem = Object(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.main: Main + get() = Main(this.self()) + +public fun , Z : Element<*,*>> T.main(block: Main.() -> Unit): T { + val elem = Main(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.video: Video + get() = Video(this.self()) + +public fun , Z : Element<*,*>> T.video(block: Video.() -> Unit): T { + val elem = Video(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.iframe: Iframe + get() = Iframe(this.self()) + +public fun , Z : Element<*,*>> T.iframe(block: Iframe.() -> Unit): T { + val elem = Iframe(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.del: Del + get() = Del(this.self()) + +public fun , Z : Element<*,*>> T.del(block: Del.() -> Unit): T { + val elem = Del(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.pre: Pre + get() = Pre(this.self()) + +public fun , Z : Element<*,*>> T.pre(block: Pre.() -> Unit): T { + val elem = Pre(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.wbr: Wbr + get() = Wbr(this.self()) + +public fun , Z : Element<*,*>> T.wbr(block: Wbr.() -> Unit): T { + val elem = Wbr(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.map: Map + get() = Map(this.self()) + +public fun , Z : Element<*,*>> T.map(block: Map.() -> Unit): T { + val elem = Map(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.figure: Figure + get() = Figure(this.self()) + +public fun , Z : Element<*,*>> T.figure(block: Figure.() -> Unit): T { + val elem = Figure(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.summary: Summary + get() = Summary(this.self()) + +public fun , Z : Element<*,*>> T.summary(block: Summary.() -> Unit): T { + val elem = Summary(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.body: Body + get() = Body(this.self()) + +public fun , Z : Element<*,*>> T.body(block: Body.() -> Unit): T { + val elem = Body(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.img: Img + get() = Img(this.self()) + +public fun , Z : Element<*,*>> T.img(block: Img.() -> Unit): T { + val elem = Img(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.optgroup: Optgroup + get() = Optgroup(this.self()) + +public fun , Z : Element<*,*>> T.optgroup(block: Optgroup.() -> Unit): T { + val elem = Optgroup(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.ins: Ins + get() = Ins(this.self()) + +public fun , Z : Element<*,*>> T.ins(block: Ins.() -> Unit): T { + val elem = Ins(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.svg: Svg + get() = Svg(this.self()) + +public fun , Z : Element<*,*>> T.svg(block: Svg.() -> Unit): T { + val elem = Svg(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.samp: Samp + get() = Samp(this.self()) + +public fun , Z : Element<*,*>> T.samp(block: Samp.() -> Unit): T { + val elem = Samp(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.td: Td + get() = Td(this.self()) + +public fun , Z : Element<*,*>> T.td(block: Td.() -> Unit): T { + val elem = Td(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.dialog: Dialog + get() = Dialog(this.self()) + +public fun , Z : Element<*,*>> T.dialog(block: Dialog.() -> Unit): T { + val elem = Dialog(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.ruby: Ruby + get() = Ruby(this.self()) + +public fun , Z : Element<*,*>> T.ruby(block: Ruby.() -> Unit): T { + val elem = Ruby(this) + elem.block() + return elem.l +} + +public inline val , Z : Element<*,*>> T.button: Button + get() = Button(this.self()) + +public fun , Z : Element<*,*>> T.button(block: Button.() -> Unit): T { + val elem = Button(this) + elem.block() + return elem.l +} diff --git a/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt b/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt index ca9c2f5..d6a7c7a 100644 --- a/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt +++ b/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt @@ -1,17 +1,50 @@ package htmlflow +//import jdk.internal.org.jline.utils.ShutdownHooks import htmlflow.continuations.HtmlContinuationSuspendableTerminationNode +import htmlflow.visitor.HtmlVisitor import htmlflow.visitor.PreprocessingVisitor import org.xmlet.htmlapifaster.Element +import org.xmlet.htmlapifaster.Html +import org.xmlet.htmlapifaster.Text +/** + * Alternative close tag function for `__()`. + */ inline val , Z : Element<*,*>> T.l: Z get() = this.`__`() -inline fun , Z : Element<*,*>> T.add(block: T.() -> Unit): T { - block() - return this +/** + * Root property of HTML element. + */ +inline val HtmlPage.html: Html + get() { + (this.visitor as HtmlVisitor).write(HtmlPage.HEADER) + return Html(self()) + } + +/** + * Root builder of HTML element with lambda with receiver. + */ +inline fun HtmlPage.html(block : Html.() -> Unit) : HtmlPage { + (this.visitor as HtmlVisitor).write(HtmlPage.HEADER) + return Html(self()).also { it.block() }.l } +/** + * Text node property. + */ +inline var , Z : Element<*,*>> T.text : T + get() = self() + set(value) { + visitor.visitText( + Text( + self(), + visitor, + value, + ) + ) + } /** * @param T type of the Element receiver @@ -37,6 +70,11 @@ inline fun , Z : Element<*,*>, M> Element.await(crossinli return this.self() } +/** + * Appendable extension to build an HtmlDoc. + */ +fun Appendable.doc(block: HtmlDoc.() -> Unit): HtmlDoc = HtmlFlow.doc(this).also(block) + /** * @param M type of the model (aka context object) */ diff --git a/src/main/kotlin/htmlflow/propertiesGenerator/HtmlFlowExtensionPropertiesGenerator.kt b/src/main/kotlin/htmlflow/propertiesGenerator/HtmlFlowExtensionPropertiesGenerator.kt new file mode 100644 index 0000000..74fc13d --- /dev/null +++ b/src/main/kotlin/htmlflow/propertiesGenerator/HtmlFlowExtensionPropertiesGenerator.kt @@ -0,0 +1,69 @@ +package htmlflow.propertiesGenerator + +import com.squareup.kotlinpoet.* +import java.io.File +import kotlin.reflect.KClass + +class HtmlFlowExtensionPropertiesGenerator { + + val t = TypeVariableName("T") + val type = TypeVariableName("T : Element, Z : Element<*,*>") + + fun createHtmlFlowExtensionProperties( + list: List>, + name : String = "HtmlFlowExtensionProperties", + destinationPackage : String = "src/main/kotlin", + element : KClass<*> + ) { + val file = FileSpec.builder("htmlflow", name) + file.addImport(element,"") + list.forEach {kClass -> + addProperty(file, kClass) + addFun(file, kClass) + } + file.build().writeTo(File(destinationPackage)) + } + + private fun addProperty( + file: FileSpec.Builder, + clazz: KClass<*> + ){ + clazz.simpleName?.let { className -> + file.addProperty( + PropertySpec.builder(className.lowercase(),TypeVariableName("${clazz.simpleName}")) + .receiver(t) + .addTypeVariable(type) + .getter( + FunSpec.getterBuilder() + .addModifiers(KModifier.INLINE) + .addStatement("return %T(%L.self())", clazz, "this") + .build() + ) + .build() + ) + } + } + + private fun addFun( + file: FileSpec.Builder, + clazz: KClass<*>, + ) { + clazz.simpleName?.let { className -> + val typeVariable = TypeVariableName("${className}") + file.addFunction( + FunSpec.builder(className.lowercase()) + .returns(t) + .receiver(t) + .addTypeVariable(type) + .addParameter("block", LambdaTypeName.get( + receiver = typeVariable, + returnType = ClassName("kotlin", "Unit"), + )) + .addStatement("val elem = %T(%L)", clazz, "this") + .addStatement("elem.block()") + .addStatement("return elem.l", clazz, "this") + .build() + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/htmlflow/propertiesGenerator/Reflection.kt b/src/main/kotlin/htmlflow/propertiesGenerator/Reflection.kt new file mode 100644 index 0000000..6f47536 --- /dev/null +++ b/src/main/kotlin/htmlflow/propertiesGenerator/Reflection.kt @@ -0,0 +1,59 @@ +package htmlflow.propertiesGenerator + +import org.xmlet.htmlapifaster.Element +import java.net.URL +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf + +fun main() { + doStuff() +} + +fun doStuff() { + val url = Element::class.java.protectionDomain.codeSource.location + val list = listKClasses(url) + val element = Element::class + val generator = HtmlFlowExtensionPropertiesGenerator() + + val filteredList = list + .filter { it.isSubclassOf(element) } + .filter { + it.simpleName != "Text" + && it.simpleName != "CustomElement" + && it.simpleName != "Html" + } + + generator.createHtmlFlowExtensionProperties( + list = filteredList, + element = element, + ) +} + +fun listKClasses(url: URL): List> { + val loader = Element::class.java.protectionDomain.classLoader + ZipInputStream(url.openStream()).use { zip -> + val res = mutableListOf>() + var entry = zip.nextEntry + while (entry != null) { + println(entry.name) + if (!entry.isDirectory + && entry.name.indexOf(".class") >= 0 + //&& !entry.name.lowercase().contains("enum") + ) { + val clazz = loader.loadClass(qualifiedName(entry)).kotlin + if (!clazz.java.isInterface && !clazz.java.isEnum) + res.add(clazz) + } + entry = zip.nextEntry + } + return res; + } +} + + +fun qualifiedName(entry: ZipEntry) = entry + .name + .replace('/', '.') // including ".class" + .let { name -> name.substring(0, name.length - ".class".length) } \ No newline at end of file diff --git a/src/test/java/htmlflow/test/views/HtmlPartials.java b/src/test/java/htmlflow/test/views/HtmlPartials.java index 46d4ef9..04678ef 100644 --- a/src/test/java/htmlflow/test/views/HtmlPartials.java +++ b/src/test/java/htmlflow/test/views/HtmlPartials.java @@ -71,19 +71,13 @@ public void testPartials() { /** * Sample showcase of data binding with HtmlDoc */ - public void trackDoc(Appendable out, Track track) { + private void trackDoc(Appendable out, Track track) { HtmlFlow.doc(out) .html() .body() .ul() - .li() - .of((li) -> li - .text(format("Artist: %s", track.getArtist()))) - .__() // li - .li() - .of((li) -> li - .text(format("Track: %s", track.getName()))) - .__() // li + .li().text(format("Artist: %s", track.getArtist())).__() + .li().text(format("Track: %s", track.getName())).__() // li .of(ul -> { if(track.getDiedDate() != null) ul.li().text(format("Died in %d", track.getDiedDate().getYear())).__(); @@ -102,15 +96,10 @@ public void trackView() { .html() .body() .ul() - .li() - .dynamic((li, track) -> li - .text(format("Artist: %s", track.getArtist()))) - .__() // li - .li() - .dynamic((li, track) -> li - .text(format("Track: %s", track.getName()))) - .__() // li .dynamic((ul, track) -> { + ul + .li().text(format("Artist: %s", track.getArtist())).__() + .li().text(format("Track: %s", track.getName())).__(); if(track.getDiedDate() != null) ul.li().text(format("Died in %d", track.getDiedDate().getYear())).__(); }) @@ -125,7 +114,7 @@ public void trackView() { // trackView.setOut(System.out).write(spaceOddity); } - public void playlistDoc(Appendable out, List tracks) { + private void playlistDoc(Appendable out, List tracks) { HtmlFlow.doc(out) .html() .body() diff --git a/src/test/kotlin/htmlflow/test/TestKotlinExtensions.kt b/src/test/kotlin/htmlflow/test/TestKotlinExtensions.kt index c1e7645..4f02051 100644 --- a/src/test/kotlin/htmlflow/test/TestKotlinExtensions.kt +++ b/src/test/kotlin/htmlflow/test/TestKotlinExtensions.kt @@ -1,9 +1,13 @@ package htmlflow.test -import htmlflow.* +import htmlflow.HtmlFlow.view import org.junit.Test import org.xmlet.htmlapifaster.EnumBorderType import kotlin.test.assertEquals +import htmlflow.HtmlFlow.doc +import org.xmlet.htmlapifaster.EnumRelType +import htmlflow.* + data class Weather(val country: String, val locations: Iterable) data class Location(val city: String, val desc: String, val celsius: Int) @@ -14,8 +18,403 @@ class TestKotlinExtensions { val html = weatherView.render(portugal) assertEquals(expectedPortugalWeather, html) } -} + @Test + fun testExtensionSimple() { + val html = weatherView1.render(portugal) + assertEquals(expectedPortugalWeather, html) + } + + @Test + fun `test div extension property`() { + + val expectedHtml = """ + + +
+ Hello, HtmlFlow! +
+ +""" + val builder = StringBuilder() + + doc(builder) + .html() + .body() + .div() + .text("Hello, HtmlFlow!") + .`__`() //close div + .`__`() // close body + .`__`() // close html + + assertEquals(expectedHtml, builder.toString()) + builder.clear() + doc(builder) + .html + .body + .div + .text("Hello, HtmlFlow!") + .l // close div + .l // close body + .l // close html + + assertEquals(expectedHtml, builder.toString()) + builder.clear() + doc(builder) + .html { + body { + div { + text("Hello, HtmlFlow!") + } + } + } + + assertEquals(expectedHtml, builder.toString()) + + builder.clear() + doc(builder). + html { + body { + div.text("Hello, HtmlFlow!").l + } + } + + assertEquals(expectedHtml, builder.toString()) + + + } + + @Test + fun `test complex HTML page`() { + + val expectedHtml = """ + + + + My Complex Page + + + + + + + + + +
+

+ Welcome to My Complex Page +

+ +
+
+
+

+ Section 1 +

+

+ This is the first section. +

+ Image 1 +
+
+

+ Section 2 +

+

+ This is the second section. +

+ Image 2 +
+
+

+ Section 3 +

+

+ This is the third section. +

+ Image 3 +
+
+
+

+ Copyright © 2024 +

+
+ + + """.trimIndent() + + val builder = StringBuilder() + + doc(builder) + .html() + .head() + .title() + .raw("My Complex Page") + .`__`() //title + .meta().attrCharset("UTF-8") + .`__`() //meta + .meta().attrName("description").attrContent("This is a complex HTML page.") + .`__`() //meta + .meta().attrName("keywords").attrContent("HTML,CSS,JavaScript") + .`__`() //meta + .meta().attrName("author").attrContent("John Doe") + .`__`() //meta + .link().attrRel(EnumRelType.STYLESHEET).attrHref("styles.css") + .`__`() //link + .script().attrSrc("script.js") + .`__`() //script + .`__`() //head + .body() + .header() + .h1() + .raw("Welcome to My Complex Page") + .`__`() //h1 + .nav() + .ul() + .li() + .a().attrHref("#section1") + .raw("Section 1") + .`__`() //a + .`__`() //li + .li() + .a().attrHref("#section2") + .raw("Section 2") + .`__`() //a + .`__`() //li + .li() + .a().attrHref("#section3") + .raw("Section 3") + .`__`() //a + .`__`() //li + .`__`() //ul + .`__`() //nav + .`__`() //header + .main() + .section().attrId("section1") + .h2() + .raw("Section 1") + .`__`() //h2 + .p() + .raw("This is the first section.") + .`__`() //p + .img().attrSrc("image1.jpg").attrAlt("Image 1") + .`__`() //img + .`__`() //section + .section().attrId("section2") + .h2() + .raw("Section 2") + .`__`() //h2 + .p() + .raw("This is the second section.") + .`__`() //p + .img().attrSrc("image2.jpg").attrAlt("Image 2") + .`__`() //img + .`__`() //section + .section().attrId("section3") + .h2() + .raw("Section 3") + .`__`() //h2 + .p() + .raw("This is the third section.") + .`__`() //p + .img().attrSrc("image3.jpg").attrAlt("Image 3") + .`__`() //img + .`__`() //section + .`__`() //main + .footer() + .p() + .raw("Copyright © 2024") + .`__`() //p + .`__`() //footer + .`__`() //body + .`__`() //html + + assertEquals(expectedHtml, builder.toString()) + builder.clear() + + doc(builder) + .html + .head + .title + .raw("My Complex Page") + .l //title + .meta.attrCharset("UTF-8") + .l //meta + .meta.attrName("description").attrContent("This is a complex HTML page.") + .l //meta + .meta.attrName("keywords").attrContent("HTML,CSS,JavaScript") + .l //meta + .meta.attrName("author").attrContent("John Doe") + .l //meta + .link.attrRel(EnumRelType.STYLESHEET).attrHref("styles.css") + .l //link + .script.attrSrc("script.js") + .l //script + .l //head + .body + .header + .h1 + .raw("Welcome to My Complex Page") + .l //h1 + .nav + .ul + .li + .a.attrHref("#section1") + .raw("Section 1") + .l //a + .l //li + .li + .a.attrHref("#section2") + .raw("Section 2") + .l //a + .l //li + .li + .a.attrHref("#section3") + .raw("Section 3") + .l //a + .l //li + .l //ul + .l //nav + .l //header + .main + .section.attrId("section1") + .h2 + .raw("Section 1") + .l //h2 + .p + .raw("This is the first section.") + .l //p + .img.attrSrc("image1.jpg").attrAlt("Image 1") + .l //img + .l //section + .section.attrId("section2") + .h2 + .raw("Section 2") + .l //h2 + .p + .raw("This is the second section.") + .l //p + .img.attrSrc("image2.jpg").attrAlt("Image 2") + .l //img + .l //section + .section.attrId("section3") + .h2 + .raw("Section 3") + .l //h2 + .p + .raw("This is the third section.") + .l //p + .img.attrSrc("image3.jpg").attrAlt("Image 3") + .l //img + .l //section + .l //main + .footer + .p + .raw("Copyright © 2024") + .l //p + .l //footer + .l //body + .l //html + + assertEquals(expectedHtml, builder.toString()) + builder.clear() + + doc(builder) + .html { + head { + title { + text("My Complex Page") + } + meta { + attrCharset("UTF-8") + } + meta { + attrName("description") + attrContent("This is a complex HTML page.") + } + meta { + attrName("keywords") + attrContent("HTML,CSS,JavaScript") + } + meta { + attrName("author") + attrContent("John Doe") + } + link { + attrRel(EnumRelType.STYLESHEET) + attrHref("styles.css") + } + script { + attrSrc("script.js") + } + } + body { + header { + h1 { + text("Welcome to My Complex Page") + } + nav { + ul { + li.a.attrHref("#section1").text("Section 1").l.l + li.a.attrHref("#section2").text("Section 2").l.l + li.a.attrHref("#section3").text("Section 3").l.l + } + } + } + main { + section { + attrId("section1") + h2.text("Section 1").l + p.text("This is the first section.").l + img.attrSrc("image1.jpg").attrAlt("Image 1").l + } + section { + attrId("section2") + h2.text("Section 2").l + p.text("This is the second section.").l + img.attrSrc("image2.jpg").attrAlt("Image 2").l + } + section { + attrId("section3") + h2.text("Section 3").l + p.text("This is the third section.").l + img.attrSrc("image3.jpg").attrAlt("Image 3").l + } + } + footer { + p { + text("Copyright © 2024") + } + } + } + } + + assertEquals(expectedHtml, builder.toString()) + builder.clear() + } + +} private val portugal = Weather("Portugal", listOf( Location("Porto", "Light rain", 14), Location("Lisbon", "Sunny day", 14), @@ -27,34 +426,64 @@ private val portugal = Weather("Portugal", listOf( )) private val weatherView = view { - html() - .head() - .title().dyn { weather: Weather -> + it.html + .head + .title.dyn { weather: Weather -> +weather.country } .l // title .l // head - .body() - .table().attrBorder(EnumBorderType._1) - .tr() - .th().add { +"City" }.l - .th().text("Temperature").l - .l // tr - .dyn { weather: Weather -> - weather.locations.forEach{ loc -> - tr().add { - td().text(loc.city).l - td().text(loc.celsius).l + .body { + table.attrBorder(EnumBorderType._1) + .tr { + th { + text("City") + } + th { + text("Temperature") + } + } // tr + .dyn { weather: Weather -> + weather.locations.forEach { loc -> + tr { + td.text(loc.city).l + td.text(loc.celsius).l + } + } } - .l // tr - } + .l // table + } // body + .l // html +} + +private val weatherView1 = view { + it.html + .head + .title.dyn { weather: Weather -> + +weather.country } - .l // table - .l // body + .l // title + .l // head + .body { + table.attrBorder(EnumBorderType._1) + .tr + .th.text("City").l + .th.text("Temperature").l + .l // tr + .dyn { weather: Weather -> + weather.locations.forEach { loc -> + tr { + td.text(loc.city).l + td.text(loc.celsius).l + } + } + } + .l // table + } // body .l // html } -private const val expectedPortugalWeather = """ +private val expectedPortugalWeather = """ @@ -97,4 +526,4 @@ private const val expectedPortugalWeather = """<!DOCTYPE html> </tr> </table> </body> -</html>""" \ No newline at end of file +</html>""" diff --git a/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt b/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt new file mode 100644 index 0000000..813aad9 --- /dev/null +++ b/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt @@ -0,0 +1,279 @@ +package htmlflow.test + +import htmlflow.* +import htmlflow.test.model.Track +import kotlinx.coroutines.reactive.asFlow +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Test +import org.xmlet.htmlapifaster.* +import reactor.core.publisher.Flux +import java.lang.String.format +import java.time.Duration +import java.time.LocalDate +import java.util.* + + +/** + * These tests do not contain any assertion because they are only a sample for README.md + * and HtmlFlow site examples. + */ +class TestKotlinExtensionsOnPartials { + + private fun testOpeningExampleOfReadme() { + System.out.doc { + html { + head { + title { text("HtmlFlow") } + } + body { + div { + attrClass("container") + h1 { text("My first HtmlFlow page") } + img { attrSrc("http://bit.ly/2MoHwrU") } + p { text("Typesafe is awesome! :-)") } + } + }// body + } //html + } // doc + } + + /** + * Sample showcase of data binding with HtmlDoc + */ + private fun Appendable.trackDoc(track: Track) { + doc { + html { + body { + ul { + li { text(format("Artist: %s", track.artist)) } + li { text(format("Track: %s", track.name)) } + if (track.diedDate != null) { + li { text(format("Died in %d", track.diedDate.year)) } + } + } // ul + } // body + } // html + } // doc + } + + /** + * Sample showcase of data binding with HtmlView + */ + @Test + fun trackView() { + val trackView = view<Track> { + html { + body { + ul { + dyn { track: Track -> + li { text(format("Artist: %s", track.artist)) } + li { text(format("Track: %s", track.name)) } + if (track.diedDate != null) { + li { text(format("Died in %d", track.diedDate.year)) } + } + } + } + } + } + } + val spaceOddity = Track("David Bowie", "Space Oddity", LocalDate.of(2016, 1, 10)) + val actual = StringBuilder() + actual.trackDoc(spaceOddity) + assertEquals(actual.toString(), trackView.render(spaceOddity)) +// trackView.setOut(System.out).write(spaceOddity); + } + + /** + * Sample showcase of loop with HtmlDoc + */ + private fun Appendable.playlistDoc(tracks: List<Track>) { + doc { + html { + body { + table { + tr { + th { text("Artist") } + th { text("Track") } + } + tracks.forEach { track -> + tr { td { text(track.artist) } } + tr { td { text(track.name) } } + } + } // table + } // body + } // html + } // doc + } + + /** + * Sample showcase of loop with HtmlView + */ + @Test + fun playlistView() { + val playlistView = view<List<Track>> { + html { + body { + table { + tr { + th { text("Artist") } + th { text("Track") } + } + dyn { tracks: List<Track> -> tracks + .forEach { track -> + tr { td { text(track.artist) } } + tr { td { text(track.name) } } + } + } + } // table + } // body + } // html + } + val tracks = listOf( + Track("David Bowie", "Space Oddity", LocalDate.of(2016, 1, 10)), + Track("U2", "Bad"), + Track("Queen", "Under Pressure") + ) + val actual = StringBuilder() + actual.playlistDoc(tracks) + assertEquals(actual.toString(), playlistView.render(tracks)) +// playlistView.setOut(System.out).write(tracks); + } + /** + * Sample showcase of loop with HtmlViewAsync + */ + @Test + fun playlistViewAsync() { + val playlistView = viewAsync<Flux<Track>> { + html { + body { + table { + tr { + th { text("Artist") } + th { text("Track") } + } + await { tracks: Flux<Track>, resume -> tracks + .doOnComplete(resume) + .doOnNext{ track -> + tr { td { text(track.artist) } } + tr { td { text(track.name) } } + } + } + } // table + } // body + } // html + } + val tracks = Arrays.asList( + Track("David Bowie", "Space Oddity", LocalDate.of(2016, 1, 10)), + Track("U2", "Bad"), + Track("Queen", "Under Pressure") + ) + val tracksFlux = Flux + .fromIterable(tracks) + .delayElements(Duration.ofMillis(10)) + val expected = StringBuilder() + expected.playlistDoc(tracks) + playlistView.renderAsync(tracksFlux).thenAccept { actual: String? -> + assertEquals( + expected.toString(), + actual + ) + } + } + /** + * Sample showcase of loop with HtmlViewSuspend + */ + @Test + fun playlistViewSuspend() { + val playlistView = viewSuspend<Flux<Track>> { + html { + body { + table { + tr { + th { text("Artist") } + th { text("Track") } + } + suspending { tracks: Flux<Track> -> tracks + .asFlow() + .collect { track -> + tr { td { text(track.artist) } } + tr { td { text(track.name) } } + } + } + } // table + } // body + } // html + } + val tracks = Arrays.asList( + Track("David Bowie", "Space Oddity", LocalDate.of(2016, 1, 10)), + Track("U2", "Bad"), + Track("Queen", "Under Pressure") + ) + val tracksFlux = Flux + .fromIterable(tracks) + .delayElements(Duration.ofMillis(10)) + val expected = StringBuilder() + expected.playlistDoc(tracks) + runBlocking { + val actual = playlistView.render(tracksFlux) + assertEquals( + expected.toString(), + actual + ) + } + } + private fun Div<*>.partialInputField(label: String, id: String, value: Any) { + div { + attrClass("form-group") + label { text(label) } + input { + attrClass("form-control") + attrType(EnumTypeInputType.TEXT) + attrId(id) + attrName(id) + attrValue(value.toString()) + } // input + } // div + } + private fun Div<*>.partialOwner() { + h2 { text("Owner") } + form { + attrMethod(EnumMethodType.POST) + div { + attrClass("form-group has-feedback") + dyn { owner: Owner -> + partialInputField("Name", "name", owner.name) + partialInputField("Address", "address", owner.address) + } + } // div + } // form + } + private fun navbarFragment(nav: Nav<*>) { + + } + + private fun ownerView(navbar: (Nav<*>) -> Unit, content: Div<*>.() -> Unit): HtmlView<Owner> { + return view<Owner> { + html { + head { + title { text("PetClinic :: a Spring Framework demonstration") } + link { attrRel(EnumRelType.STYLESHEET).attrHref("/resources/css/petclinic.css") } + } + body { + nav { navbar(this) } + div { + attrClass("container xd-container") + content(this) + } // div + } // body + } // html + } + } + + @Test fun testOwnerView() { + val view = ownerView(::navbarFragment) { partialOwner() } + // view.setOut(System.out).write(Owner("Ze Manel", "Rua da Alfandega")) + } + + class Owner(val name: String, val address: String) +} \ No newline at end of file