diff --git a/model/config/ktlint/baseline.xml b/model/config/ktlint/baseline.xml
index de9b1c9..4a8cf06 100644
--- a/model/config/ktlint/baseline.xml
+++ b/model/config/ktlint/baseline.xml
@@ -703,25 +703,50 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -796,7 +821,4 @@
-
-
-
diff --git a/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/FromDom.kt b/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/FromDom.kt
index 1ab92c1..29513cd 100644
--- a/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/FromDom.kt
+++ b/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/FromDom.kt
@@ -323,7 +323,7 @@ class DOMParser(
// or has an '=' sign after the prop, followed by the given
// value.
style.length > prop.length &&
- (style[prop.length] != 61.toChar() || style.slice(prop.length + 1 until style.length) != value)
+ (style[prop.length] != '=' || style.slice(prop.length + 1 until style.length) != value)
) {
continue
}
diff --git a/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/ToDom.kt b/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/ToDom.kt
index 5e20548..e3cffef 100644
--- a/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/ToDom.kt
+++ b/model/src/commonMain/kotlin/com/atlassian/prosemirror/model/ToDom.kt
@@ -164,7 +164,7 @@ open class DOMSerializer(
if (space > 0) {
dom.attr(
// name.slice(0 until space),
- name.slice(space + 1..space + 1),
+ name.substring(space + 1),
attrs[name].toString()
)
} else {
diff --git a/model/src/commonTest/kotlin/com/atlassian/prosemirror/model/DomTest.kt b/model/src/commonTest/kotlin/com/atlassian/prosemirror/model/DomTest.kt
index fd9f3da..d388349 100644
--- a/model/src/commonTest/kotlin/com/atlassian/prosemirror/model/DomTest.kt
+++ b/model/src/commonTest/kotlin/com/atlassian/prosemirror/model/DomTest.kt
@@ -7,48 +7,30 @@ import com.atlassian.prosemirror.testbuilder.NodeBuildCompanion
import com.atlassian.prosemirror.testbuilder.NodeBuilder
import com.atlassian.prosemirror.testbuilder.NodeSpecImpl
import com.atlassian.prosemirror.testbuilder.PMNodeBuilder.Companion.doc
-import kotlin.test.Ignore
import kotlin.test.Test
import com.atlassian.prosemirror.testbuilder.schema as testSchema
-
-val schemaWithComment = Schema(
- SchemaSpec(
- nodes = testSchema.spec.nodes + mapOf(
- "doc" to (testSchema.spec.nodes["doc"] as NodeSpecImpl).copy(marks = "comment")
- ),
- marks = testSchema.spec.marks + mapOf(
- "comment" to MarkSpecImpl(
- parseDOM = listOf(ParseRuleImpl(tag = "div.comment")),
- toDOM = { _, _ ->
- DOMOutputSpec.ArrayDOMOutputSpec(listOf("div", mapOf("class" to "comment"), 0))
- }
- )
- )
- )
-)
-
-fun NodeBuilder.comment(func: NodeBuilder.() -> Unit) = mark("comment", func)
+import com.atlassian.prosemirror.testbuilder.AttributeSpecImpl
+import com.atlassian.prosemirror.testbuilder.PMNodeBuilder
class CommentNodeBuilder(
pos: Int = 0,
marks: List = emptyList(),
- override val schema: Schema = schemaWithComment
+ override val schema: Schema = testSchema
) : NodeBuilder(pos, marks, schema) {
-
override val checked: Boolean
get() = false
override fun create(pos: Int, marks: List, schema: Schema): NodeBuilder {
return CommentNodeBuilder(pos, marks, schema)
}
+}
- companion object : NodeBuildCompanion(schemaWithComment) {
- override val checked: Boolean
- get() = false
+class CustomNodeBuildCompanion(schema: Schema): NodeBuildCompanion(schema) {
+ override val checked: Boolean
+ get() = false
- override fun create(): CommentNodeBuilder {
- return CommentNodeBuilder()
- }
+ override fun create(): CommentNodeBuilder {
+ return CommentNodeBuilder(schema = schema)
}
}
@@ -87,11 +69,23 @@ class DomTest {
)
}
- @Ignore // "This test is failing - fix code"
@Test
fun `can represent links`() {
+ // custom link mark that has a title=null attribute
+ fun NodeBuilder.aWithTitle(href: String = "foo", func: NodeBuilder.() -> Unit) =
+ mark("link", func, attrs = mapOf("href" to href, "title" to null))
+
test(
- doc { p { +"a " + a(href = "foo") { +"big " + a(href = "bar") { +"nested" } + " link" } } },
+ // TypeScript code: doc(p("a ", a({href: "foo"}, "big ", a({href: "bar"}, "nested"), " link")))
+ // converts to the code below because each node cannot have more than 1 Link mark
+ doc {
+ p {
+ +"a " +
+ aWithTitle(href = "foo") { +"big "} +
+ aWithTitle(href = "bar") { +"nested" } +
+ aWithTitle(href = "foo") { +" link" }
+ }
+ },
"a big nested link
"
)
}
@@ -184,7 +178,26 @@ class DomTest {
@Test
fun `can parse marks on block nodes`() {
- val doc = CommentNodeBuilder.doc {
+ val schemaWithComment = Schema(
+ SchemaSpec(
+ nodes = testSchema.spec.nodes + mapOf(
+ "doc" to (testSchema.spec.nodes["doc"] as NodeSpecImpl).copy(marks = "comment")
+ ),
+ marks = testSchema.spec.marks + mapOf(
+ "comment" to MarkSpecImpl(
+ parseDOM = listOf(ParseRuleImpl(tag = "div.comment")),
+ toDOM = { _, _ ->
+ DOMOutputSpec.ArrayDOMOutputSpec(listOf("div", mapOf("class" to "comment"), 0))
+ }
+ )
+ )
+ )
+ )
+
+ fun NodeBuilder.comment(func: NodeBuilder.() -> Unit) =
+ mark("comment", func)
+
+ val doc = CustomNodeBuildCompanion(schemaWithComment).doc {
p { +"one" } + this.comment { p { +"two" } + p { strong { +"three" } } } + p { +"four" }
}
test(
@@ -193,46 +206,88 @@ class DomTest {
)
}
- // TODO convert tests below
-// it("parses unique, non-exclusive, same-typed marks", () => {
-// let commentSchema = new Schema({
-// nodes: schema.spec.nodes,
-// marks: schema.spec.marks.update("comment", {
-// attrs: { id: { default: null }},
-// parseDOM: [{
-// tag: "span.comment",
-// getAttrs(dom) { return { id: parseInt((dom as HTMLElement).getAttribute('data-id')!, 10) } }
-// }],
-// excludes: '',
-// toDOM(mark: Mark) { return ["span", {class: "comment", "data-id": mark.attrs.id }, 0] }
-// })
-// })
-// let b = builders(commentSchema)
-// test(b.schema.nodes.doc.createAndFill(undefined, [
-// b.schema.nodes.paragraph.createAndFill(undefined, [
-// b.schema.text('double comment', [
-// b.schema.marks.comment.create({ id: 1 }),
-// b.schema.marks.comment.create({ id: 2 })
-// ])!
-// ])!
-// ])!,
-// "")()
-// })
-//
-// it("serializes non-spanning marks correctly", () => {
-// let markSchema = new Schema({
-// nodes: schema.spec.nodes,
-// marks: schema.spec.marks.update("test", {
-// parseDOM: [{tag: "test"}],
-// toDOM() { return ["test", 0] },
-// spanning: false
-// })
-// })
-// let b = builders(markSchema) as any
-// test(b.doc(b.paragraph(b.test("a", b.image({src: "x"}), "b"))),
-// "ab
")()
-// })
-//
+ @Test
+ fun `parses unique non-exclusive same-typed marks`() {
+ val commentSchema = Schema(
+ SchemaSpec(
+ nodes = testSchema.spec.nodes,
+ marks = testSchema.spec.marks + mapOf(
+ "comment" to MarkSpecImpl(
+ attrs = mapOf("id" to AttributeSpecImpl(default = null)),
+ parseDOM = listOf(
+ ParseRuleImpl(
+ tag = "span.comment",
+ getNodeAttrs = { dom ->
+ val id = dom.attribute("data-id")?.int() ?: 10
+ ParseRuleMatch(mapOf("id" to id))
+ },
+ )
+ ),
+ excludes = "",
+ toDOM = { mark, _ ->
+ DOMOutputSpec.ArrayDOMOutputSpec(
+ listOf(
+ "span",
+ mapOf("class" to "comment", "data-id" to mark.attrs["id"]),
+ 0
+ )
+ )
+ }
+ )
+ )
+ )
+ )
+ val doc = commentSchema.nodes["doc"]!!.createAndFill(
+ attrs = null,
+ content = listOf(
+ commentSchema.nodes["paragraph"]!!.createAndFill(
+ attrs = null,
+ content = listOf(
+ commentSchema.text(
+ text = "double comment",
+ marks = listOf(
+ commentSchema.marks["comment"]!!.create(mapOf("id" to 1)),
+ commentSchema.marks["comment"]!!.create(mapOf("id" to 2))
+ )
+ )
+ ),
+ marks = null
+ )!!
+ ),
+ marks = null
+ )!!
+ test(
+ doc,
+ ""
+ )
+ }
+
+ @Test
+ fun `serializes non-spanning marks correctly`() {
+ val markSchema = Schema(
+ SchemaSpec(
+ nodes = testSchema.spec.nodes,
+ marks = testSchema.spec.marks + mapOf(
+ "test" to MarkSpecImpl(
+ parseDOM = listOf(ParseRuleImpl(tag = "test")),
+ toDOM = { _, _ -> DOMOutputSpec.ArrayDOMOutputSpec(listOf("test", 0)) },
+ spanning = false
+ )
+ )
+ )
+ )
+ val b = CustomNodeBuildCompanion(markSchema)
+
+ fun NodeBuilder.test(func: NodeBuilder.() -> Unit) =
+ mark("test", func)
+
+ test(
+ b.doc { p { test { +"a" + img(mapOf("src" to "x")) {} + "b" } } },
+ "ab
"
+ )
+ }
+
+ // Skipping the following tests because we don't support them yet
// it("serializes an element and an attribute with XML namespace", () => {
// let xmlnsSchema = new Schema({
// nodes: {
@@ -430,12 +485,16 @@ class DomTest {
doc { p { em { +"Hello" } + "!" } }
)
}
+
+ @Test
+ fun `interprets font-weight bold as strong`() {
+ recover(
+ "Hello
",
+ doc { p { strong { +"Hello" } } }
+ )
+ }
}
-// it("interprets font-weight: bold as strong",
-// recover("Hello
",
-// doc(p(strong("Hello")))))
-//
// it("allows clearing of pending marks",
// recover("One
Two
foobar
baz
quuxxyz")
// })
// })
+
+fun com.fleeksoft.ksoup.nodes.Attribute?.int(default: Int? = null): Int? {
+ return try {
+ this?.value?.takeIf { it.isNotBlank() }?.toInt()
+ } catch (ex: NumberFormatException) {
+ default
+ }
+}
diff --git a/test-builder/config/ktlint/baseline.xml b/test-builder/config/ktlint/baseline.xml
index 1ed1fab..c64df6f 100644
--- a/test-builder/config/ktlint/baseline.xml
+++ b/test-builder/config/ktlint/baseline.xml
@@ -80,43 +80,43 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test-builder/src/commonMain/kotlin/com/atlassian/prosemirror/testbuilder/SchemaBasic.kt b/test-builder/src/commonMain/kotlin/com/atlassian/prosemirror/testbuilder/SchemaBasic.kt
index 37cd0dc..f284772 100644
--- a/test-builder/src/commonMain/kotlin/com/atlassian/prosemirror/testbuilder/SchemaBasic.kt
+++ b/test-builder/src/commonMain/kotlin/com/atlassian/prosemirror/testbuilder/SchemaBasic.kt
@@ -13,6 +13,7 @@ import com.atlassian.prosemirror.model.ParseRuleMatch
import com.atlassian.prosemirror.model.PreserveWhitespace
import com.atlassian.prosemirror.model.Schema
import com.atlassian.prosemirror.model.SchemaSpec
+import com.fleeksoft.ksoup.nodes.Element
val pDOM: DOMOutputSpec = DOMOutputSpec.ArrayDOMOutputSpec(listOf("p", 0))
val blockquoteDOM: DOMOutputSpec = DOMOutputSpec.ArrayDOMOutputSpec(listOf("blockquote", 0))
@@ -200,11 +201,12 @@ val marks = mapOf(
// pasted content will be inexplicably wrapped in ``
// tags with a font-weight normal.
ParseRuleImpl(tag = "b", getNodeAttrs = { node ->
- // TODO find a way to parse css
- // node.style.fontWeight != "normal" && null
- ParseRuleMatch(null)
+ ParseRuleMatch(null, node.styles()["font-weight"] != "normal")
}),
-// {style: "font-weight", getAttrs: (value: string) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null}
+ ParseRuleImpl(style = "font-weight", getStyleAttrs = { value ->
+ val regex = "^bold(er)?|[5-9]\\d{2,}".toRegex()
+ ParseRuleMatch(null, regex.matches(value))
+ })
),
toDOM = { _, _ -> strongDOM }
),
@@ -232,3 +234,11 @@ val marks = mapOf(
// To reuse elements from this schema, extend or read from its `spec.nodes` and `spec.marks`
// [properties](#model.Schema.spec).
val schemaBasic = Schema(SchemaSpec(nodes = nodes, marks = marks))
+
+fun Element.styles(): Map {
+ val style = attribute("style")?.value ?: return emptyMap()
+ return style.split(";").associate {
+ val (key, value) = it.split(":")
+ key.trim() to value.trim()
+ }
+}