forked from SPC/spc-site
Add text preprocessors
This commit is contained in:
parent
781b185349
commit
bf884732c1
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
type: page
|
type: page
|
||||||
title: Consulting
|
title: Consulting
|
||||||
|
transformation: snark.replaceLinks
|
||||||
language: en
|
language: en
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ Our research, which primarily includes open source projects, has already been ap
|
|||||||
<img src="images/pic03.jpg" alt="" />
|
<img src="images/pic03.jpg" alt="" />
|
||||||
</span>
|
</span>
|
||||||
<header class="major">
|
<header class="major">
|
||||||
<h3><a href="consulting/research" class="link">Research</a></h3>
|
<h3><a href="${homeRef}/consulting/research" class="link">Research</a></h3>
|
||||||
<p>Analytics, prototyping and research for fundamental science and industry.</p>
|
<p>Analytics, prototyping and research for fundamental science and industry.</p>
|
||||||
</header>
|
</header>
|
||||||
</article>
|
</article>
|
||||||
@ -29,7 +30,7 @@ Our research, which primarily includes open source projects, has already been ap
|
|||||||
<img src="images/pic04.jpg" alt="" />
|
<img src="images/pic04.jpg" alt="" />
|
||||||
</span>
|
</span>
|
||||||
<header class="major">
|
<header class="major">
|
||||||
<h3><a href="consulting/expert" class="link">Expert activity</a></h3>
|
<h3><a href="${homeRef}/consulting/expert" class="link">Expert activity</a></h3>
|
||||||
<p>Analysis and improvement of existing scientific software and projects.</p>
|
<p>Analysis and improvement of existing scientific software and projects.</p>
|
||||||
</header>
|
</header>
|
||||||
</article>
|
</article>
|
||||||
@ -38,7 +39,7 @@ Our research, which primarily includes open source projects, has already been ap
|
|||||||
<img src="images/pic05.jpg" alt="" />
|
<img src="images/pic05.jpg" alt="" />
|
||||||
</span>
|
</span>
|
||||||
<header class="major">
|
<header class="major">
|
||||||
<h3><a href="consulting/architecture" class="link">Architecture</a></h3>
|
<h3><a href="${homeRef}/consulting/architecture" class="link">Architecture</a></h3>
|
||||||
<p>Design the architecture for scientific and technological applications.</p>
|
<p>Design the architecture for scientific and technological applications.</p>
|
||||||
</header>
|
</header>
|
||||||
</article>
|
</article>
|
||||||
@ -47,7 +48,7 @@ Our research, which primarily includes open source projects, has already been ap
|
|||||||
<img src="images/pic06.jpg" alt="" />
|
<img src="images/pic06.jpg" alt="" />
|
||||||
</span>
|
</span>
|
||||||
<header class="major">
|
<header class="major">
|
||||||
<h3><a href="consulting/development" class="link">Development</a></h3>
|
<h3><a href="${homeRef}/consulting/development" class="link">Development</a></h3>
|
||||||
<p>The development and support of open-source and in-house solutions( libraries, frameworks and end-user application).</p>
|
<p>The development and support of open-source and in-house solutions( libraries, frameworks and end-user application).</p>
|
||||||
</header>
|
</header>
|
||||||
</article>
|
</article>
|
||||||
|
@ -6,19 +6,23 @@ import kotlinx.html.FlowContent
|
|||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
import space.kscience.dataforge.data.Data
|
import space.kscience.dataforge.data.Data
|
||||||
import space.kscience.dataforge.data.await
|
import space.kscience.dataforge.data.await
|
||||||
import space.kscience.dataforge.meta.Meta
|
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.meta.int
|
import space.kscience.dataforge.meta.int
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
|
||||||
|
|
||||||
//TODO replace by VisionForge type
|
//TODO replace by VisionForge type
|
||||||
typealias HtmlFragment = context(PageBuilder, TagConsumer<*>) () -> Unit
|
//typealias HtmlFragment = context(PageBuilder, TagConsumer<*>) () -> Unit
|
||||||
|
|
||||||
|
fun interface HtmlFragment {
|
||||||
|
fun TagConsumer<*>.renderFragment(page: PageBuilder)
|
||||||
|
//TODO move pageBuilder to a context receiver after KT-52967 is fixed
|
||||||
|
}
|
||||||
|
|
||||||
typealias HtmlData = Data<HtmlFragment>
|
typealias HtmlData = Data<HtmlFragment>
|
||||||
|
|
||||||
fun HtmlData(meta: Meta, content: context(PageBuilder, TagConsumer<*>) () -> Unit): HtmlData =
|
//fun HtmlData(meta: Meta, content: context(PageBuilder) TagConsumer<*>.() -> Unit): HtmlData =
|
||||||
Data(content, meta)
|
// Data(HtmlFragment(content), meta)
|
||||||
|
|
||||||
internal val HtmlData.id: String get() = meta["id"]?.string ?: "block[${hashCode()}]"
|
internal val HtmlData.id: String get() = meta["id"]?.string ?: "block[${hashCode()}]"
|
||||||
internal val HtmlData.language: String? get() = meta["language"].string?.lowercase()
|
internal val HtmlData.language: String? get() = meta["language"].string?.lowercase()
|
||||||
@ -26,5 +30,5 @@ internal val HtmlData.language: String? get() = meta["language"].string?.lowerca
|
|||||||
internal val HtmlData.order: Int? get() = meta["order"]?.int
|
internal val HtmlData.order: Int? get() = meta["order"]?.int
|
||||||
|
|
||||||
context(PageBuilder) fun FlowContent.htmlData(data: HtmlData) = runBlocking(Dispatchers.IO) {
|
context(PageBuilder) fun FlowContent.htmlData(data: HtmlData) = runBlocking(Dispatchers.IO) {
|
||||||
data.await().invoke(this@PageBuilder, consumer)
|
with(data.await()) { consumer.renderFragment(this@PageBuilder) }
|
||||||
}
|
}
|
@ -14,7 +14,6 @@ import io.ktor.server.routing.createRouteFromPath
|
|||||||
import io.ktor.server.routing.get
|
import io.ktor.server.routing.get
|
||||||
import io.ktor.server.routing.routing
|
import io.ktor.server.routing.routing
|
||||||
import kotlinx.html.HTML
|
import kotlinx.html.HTML
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.data.DataTree
|
import space.kscience.dataforge.data.DataTree
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.withDefault
|
import space.kscience.dataforge.meta.withDefault
|
||||||
@ -57,7 +56,7 @@ internal class KtorSiteBuilder(
|
|||||||
val pageBaseUrl: String,
|
val pageBaseUrl: String,
|
||||||
override val meta: Meta = this@KtorSiteBuilder.meta,
|
override val meta: Meta = this@KtorSiteBuilder.meta,
|
||||||
) : PageBuilder {
|
) : PageBuilder {
|
||||||
override val context: Context get() = this@KtorSiteBuilder.context
|
override val snark: SnarkPlugin get() = this@KtorSiteBuilder.snark
|
||||||
override val data: DataTree<*> get() = this@KtorSiteBuilder.data
|
override val data: DataTree<*> get() = this@KtorSiteBuilder.data
|
||||||
|
|
||||||
override fun resolveRef(ref: String): String = resolveRef(pageBaseUrl, ref)
|
override fun resolveRef(ref: String): String = resolveRef(pageBaseUrl, ref)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package space.kscience.snark
|
package space.kscience.snark
|
||||||
|
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.data.*
|
import space.kscience.dataforge.data.*
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
@ -16,6 +17,11 @@ internal fun Name.toWebPath() = tokens.joinToString(separator = "/"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PageBuilder : ContextAware {
|
interface PageBuilder : ContextAware {
|
||||||
|
|
||||||
|
val snark: SnarkPlugin
|
||||||
|
|
||||||
|
override val context: Context get() = snark.context
|
||||||
|
|
||||||
val data: DataTree<*>
|
val data: DataTree<*>
|
||||||
|
|
||||||
val meta: Meta
|
val meta: Meta
|
||||||
|
@ -10,6 +10,7 @@ import space.kscience.dataforge.meta.Meta
|
|||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.meta.getIndexed
|
import space.kscience.dataforge.meta.getIndexed
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
@ -109,11 +110,14 @@ fun SiteBuilder.pages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Type(SiteLayout.TYPE)
|
||||||
fun interface SiteLayout {
|
fun interface SiteLayout {
|
||||||
|
|
||||||
context(SiteBuilder) fun render(item: DataTreeItem<*>)
|
context(SiteBuilder) fun render(item: DataTreeItem<*>)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val TYPE = "snark.layout"
|
||||||
const val LAYOUT_KEY = "layout"
|
const val LAYOUT_KEY = "layout"
|
||||||
const val ASSETS_KEY = "assets"
|
const val ASSETS_KEY = "assets"
|
||||||
val INDEX_PAGE_TOKEN = NameToken("index")
|
val INDEX_PAGE_TOKEN = NameToken("index")
|
||||||
@ -134,6 +138,7 @@ fun interface SiteLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
object DefaultSiteLayout : SiteLayout {
|
object DefaultSiteLayout : SiteLayout {
|
||||||
context(SiteBuilder) override fun render(item: DataTreeItem<*>) {
|
context(SiteBuilder) override fun render(item: DataTreeItem<*>) {
|
||||||
pages(item)
|
pages(item)
|
||||||
|
@ -18,6 +18,7 @@ import space.kscience.dataforge.misc.DFExperimental
|
|||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
import space.kscience.dataforge.workspace.FileData
|
import space.kscience.dataforge.workspace.FileData
|
||||||
import space.kscience.dataforge.workspace.readDataDirectory
|
import space.kscience.dataforge.workspace.readDataDirectory
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -36,12 +37,12 @@ interface SnarkParser<out R> {
|
|||||||
|
|
||||||
val priority: Int get() = DEFAULT_PRIORITY
|
val priority: Int get() = DEFAULT_PRIORITY
|
||||||
|
|
||||||
fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R
|
fun parse(context: Context, meta: Meta, bytes: ByteArray): R
|
||||||
|
|
||||||
fun reader(snark: SnarkPlugin, meta: Meta) = object : IOReader<R> {
|
fun reader(context: Context, meta: Meta) = object : IOReader<R> {
|
||||||
override val type: KType get() = this@SnarkParser.type
|
override val type: KType get() = this@SnarkParser.type
|
||||||
|
|
||||||
override fun readObject(input: Input): R = parse(snark, meta, input.readBytes())
|
override fun readObject(input: Input): R = parse(context, meta, input.readBytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -56,7 +57,7 @@ internal class SnarkParserWrapper<R : Any>(
|
|||||||
override val type: KType,
|
override val type: KType,
|
||||||
override val fileExtensions: Set<String>,
|
override val fileExtensions: Set<String>,
|
||||||
) : SnarkParser<R> {
|
) : SnarkParser<R> {
|
||||||
override fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R = bytes.asBinary().readWith(reader)
|
override fun parse(context: Context, meta: Meta, bytes: ByteArray): R = bytes.asBinary().readWith(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,6 +80,14 @@ class SnarkPlugin : AbstractPlugin() {
|
|||||||
context.gather(SnarkParser.TYPE, true)
|
context.gather(SnarkParser.TYPE, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val layouts: Map<Name, SiteLayout> by lazy {
|
||||||
|
context.gather(SiteLayout.TYPE, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textTransformations: Map<Name, TextTransformation> by lazy {
|
||||||
|
context.gather(TextTransformation.TYPE, true)
|
||||||
|
}
|
||||||
|
|
||||||
fun readDirectory(path: Path): DataTree<Any> = io.readDataDirectory(path) { dataPath, meta ->
|
fun readDirectory(path: Path): DataTree<Any> = io.readDataDirectory(path) { dataPath, meta ->
|
||||||
val fileExtension = meta[FileData.META_FILE_EXTENSION_KEY].string ?: dataPath.extension
|
val fileExtension = meta[FileData.META_FILE_EXTENSION_KEY].string ?: dataPath.extension
|
||||||
val parser: SnarkParser<Any> = parsers.values.filter { parser ->
|
val parser: SnarkParser<Any> = parsers.values.filter { parser ->
|
||||||
@ -90,11 +99,20 @@ class SnarkPlugin : AbstractPlugin() {
|
|||||||
byteArraySnarkParser
|
byteArraySnarkParser
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.reader(this, meta)
|
parser.reader(context, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun layout(meta: Meta): SiteLayout = when (meta[SiteLayout.LAYOUT_KEY]) {
|
internal fun layout(layoutMeta: Meta): SiteLayout {
|
||||||
else -> DefaultSiteLayout
|
val layoutName = layoutMeta.string
|
||||||
|
?: layoutMeta["name"].string ?: error("Layout name not defined in $layoutMeta")
|
||||||
|
return layouts[layoutName.parseAsName()] ?: error("Layout with name $layoutName not found in $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun textTransformation(transformationMeta: Meta): TextTransformation {
|
||||||
|
val transformationName = transformationMeta.string
|
||||||
|
?: transformationMeta["name"].string ?: error("Transformation name not defined in $transformationMeta")
|
||||||
|
return textTransformations[transformationName.parseAsName()]
|
||||||
|
?: error("Text transformation with name $transformationName not found in $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun content(target: String): Map<Name, Any> = when (target) {
|
override fun content(target: String): Map<Name, Any> = when (target) {
|
||||||
@ -107,6 +125,9 @@ class SnarkPlugin : AbstractPlugin() {
|
|||||||
"jpg".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"),
|
"jpg".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"),
|
||||||
"gif".asName() to SnarkParser(ImageIOReader, "gif"),
|
"gif".asName() to SnarkParser(ImageIOReader, "gif"),
|
||||||
)
|
)
|
||||||
|
TextTransformation.TYPE -> mapOf(
|
||||||
|
"replaceLinks".asName() to TextTransformation.replaceLinks
|
||||||
|
)
|
||||||
else -> super.content(target)
|
else -> super.content(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,10 @@ import kotlinx.html.unsafe
|
|||||||
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
||||||
import org.intellij.markdown.html.HtmlGenerator
|
import org.intellij.markdown.html.HtmlGenerator
|
||||||
import org.intellij.markdown.parser.MarkdownParser
|
import org.intellij.markdown.parser.MarkdownParser
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.io.IOReader
|
import space.kscience.dataforge.io.IOReader
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
@ -17,8 +19,13 @@ import kotlin.reflect.typeOf
|
|||||||
abstract class SnarkTextParser<R> : SnarkParser<R> {
|
abstract class SnarkTextParser<R> : SnarkParser<R> {
|
||||||
abstract fun parseText(text: String, meta: Meta): R
|
abstract fun parseText(text: String, meta: Meta): R
|
||||||
|
|
||||||
override fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R =
|
override fun parse(context: Context, meta: Meta, bytes: ByteArray): R =
|
||||||
parseText(bytes.decodeToString(), meta)
|
parseText(bytes.decodeToString(), meta)
|
||||||
|
|
||||||
|
fun transformText(text: String, meta: Meta, page: PageBuilder): String =
|
||||||
|
meta[TextTransformation.TEXT_TRANSFORMATION_KEY]?.let {
|
||||||
|
with(page){ page.snark.textTransformation(it).transform(text)}
|
||||||
|
} ?: text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -26,9 +33,9 @@ internal object SnarkHtmlParser : SnarkTextParser<HtmlFragment>() {
|
|||||||
override val fileExtensions: Set<String> = setOf("html")
|
override val fileExtensions: Set<String> = setOf("html")
|
||||||
override val type: KType = typeOf<HtmlFragment>()
|
override val type: KType = typeOf<HtmlFragment>()
|
||||||
|
|
||||||
override fun parseText(text: String, meta: Meta): HtmlFragment = {
|
override fun parseText(text: String, meta: Meta): HtmlFragment = HtmlFragment { page ->
|
||||||
div {
|
div {
|
||||||
unsafe { +text }
|
unsafe { +transformText(text, meta, page) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,10 +51,10 @@ internal object SnarkMarkdownParser : SnarkTextParser<HtmlFragment>() {
|
|||||||
val parsedTree = markdownParser.buildMarkdownTreeFromString(text)
|
val parsedTree = markdownParser.buildMarkdownTreeFromString(text)
|
||||||
val htmlString = HtmlGenerator(text, parsedTree, markdownFlavor).generateHtml()
|
val htmlString = HtmlGenerator(text, parsedTree, markdownFlavor).generateHtml()
|
||||||
|
|
||||||
return {
|
return HtmlFragment { page ->
|
||||||
div {
|
div {
|
||||||
unsafe {
|
unsafe {
|
||||||
+htmlString
|
+SnarkHtmlParser.transformText(htmlString, meta, page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ package space.kscience.snark
|
|||||||
import kotlinx.html.HTML
|
import kotlinx.html.HTML
|
||||||
import kotlinx.html.html
|
import kotlinx.html.html
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.data.DataTree
|
import space.kscience.dataforge.data.DataTree
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.withDefault
|
import space.kscience.dataforge.meta.withDefault
|
||||||
@ -67,7 +66,8 @@ internal class StaticSiteBuilder(
|
|||||||
inner class StaticPageBuilder : PageBuilder {
|
inner class StaticPageBuilder : PageBuilder {
|
||||||
override val data: DataTree<*> get() = this@StaticSiteBuilder.data
|
override val data: DataTree<*> get() = this@StaticSiteBuilder.data
|
||||||
override val meta: Meta get() = this@StaticSiteBuilder.meta
|
override val meta: Meta get() = this@StaticSiteBuilder.meta
|
||||||
override val context: Context get() = this@StaticSiteBuilder.context
|
override val snark: SnarkPlugin get() = this@StaticSiteBuilder.snark
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun resolveRef(ref: String): String = resolveRef(baseUrl, ref)
|
override fun resolveRef(ref: String): String = resolveRef(baseUrl, ref)
|
||||||
|
22
src/main/kotlin/space/kscience/snark/TextTransformation.kt
Normal file
22
src/main/kotlin/space/kscience/snark/TextTransformation.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package space.kscience.snark
|
||||||
|
|
||||||
|
import space.kscience.dataforge.misc.Type
|
||||||
|
import space.kscience.dataforge.names.NameToken
|
||||||
|
|
||||||
|
@Type(TextTransformation.TYPE)
|
||||||
|
fun interface TextTransformation {
|
||||||
|
context(PageBuilder) fun transform(text: String): String
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TYPE = "snark.textTransformation"
|
||||||
|
val TEXT_TRANSFORMATION_KEY = NameToken("transformation")
|
||||||
|
|
||||||
|
val replaceLinks: TextTransformation = object : TextTransformation {
|
||||||
|
context(PageBuilder) override fun transform(text: String): String {
|
||||||
|
return text.replace("\${homeRef}", homeRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user