@fiduswriter/document 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +16 -0
- package/jest.config.js +23 -0
- package/package.json +59 -0
- package/schema.json +1 -0
- package/scripts/export-schema.js +16 -0
- package/src/bibliography/common.js +92 -0
- package/src/bibliography/csl_bib.js +139 -0
- package/src/citations/citeproc_sys.js +42 -0
- package/src/citations/format.js +194 -0
- package/src/common/blob.js +10 -0
- package/src/common/file.js +25 -0
- package/src/common/index.js +12 -0
- package/src/common/network.js +79 -0
- package/src/common/text.js +44 -0
- package/src/editor/e2ee/encryptor.js +228 -0
- package/src/exporter/docx/citations.js +177 -0
- package/src/exporter/docx/comments.js +165 -0
- package/src/exporter/docx/footnotes.js +240 -0
- package/src/exporter/docx/images.js +101 -0
- package/src/exporter/docx/index.js +185 -0
- package/src/exporter/docx/lists.js +260 -0
- package/src/exporter/docx/math.js +46 -0
- package/src/exporter/docx/metadata.js +289 -0
- package/src/exporter/docx/rels.js +193 -0
- package/src/exporter/docx/render.js +941 -0
- package/src/exporter/docx/richtext.js +1182 -0
- package/src/exporter/docx/tables.js +112 -0
- package/src/exporter/docx/tools.js +50 -0
- package/src/exporter/epub/index.js +142 -0
- package/src/exporter/epub/templates.js +140 -0
- package/src/exporter/epub/tools.js +96 -0
- package/src/exporter/html/citations.js +121 -0
- package/src/exporter/html/convert.js +813 -0
- package/src/exporter/html/index.js +192 -0
- package/src/exporter/html/templates.js +34 -0
- package/src/exporter/html/tools.js +50 -0
- package/src/exporter/jats/bibliography.js +183 -0
- package/src/exporter/jats/citations.js +109 -0
- package/src/exporter/jats/convert.js +871 -0
- package/src/exporter/jats/index.js +92 -0
- package/src/exporter/jats/templates.js +35 -0
- package/src/exporter/jats/text.js +72 -0
- package/src/exporter/latex/convert.js +934 -0
- package/src/exporter/latex/escape_latex.js +21 -0
- package/src/exporter/latex/index.js +74 -0
- package/src/exporter/latex/readme.js +22 -0
- package/src/exporter/native/shrink.js +132 -0
- package/src/exporter/odt/citations.js +101 -0
- package/src/exporter/odt/footnotes.js +147 -0
- package/src/exporter/odt/images.js +115 -0
- package/src/exporter/odt/index.js +156 -0
- package/src/exporter/odt/math.js +57 -0
- package/src/exporter/odt/metadata.js +251 -0
- package/src/exporter/odt/render.js +806 -0
- package/src/exporter/odt/richtext.js +865 -0
- package/src/exporter/odt/styles.js +387 -0
- package/src/exporter/odt/track.js +68 -0
- package/src/exporter/pandoc/citations.js +98 -0
- package/src/exporter/pandoc/convert.js +1017 -0
- package/src/exporter/pandoc/index.js +92 -0
- package/src/exporter/pandoc/readme.js +8 -0
- package/src/exporter/pandoc/tools.js +51 -0
- package/src/exporter/print/index.js +177 -0
- package/src/exporter/tools/doc_content.js +144 -0
- package/src/exporter/tools/file.js +9 -0
- package/src/exporter/tools/json.js +73 -0
- package/src/exporter/tools/svg.js +29 -0
- package/src/exporter/tools/xml.js +531 -0
- package/src/exporter/tools/xml_zip.js +95 -0
- package/src/exporter/tools/zip.js +90 -0
- package/src/exporter/tools/zotero_csl.js +93 -0
- package/src/importer/citations.js +129 -0
- package/src/importer/docx/citations.js +123 -0
- package/src/importer/docx/convert.js +1427 -0
- package/src/importer/docx/helpers.js +9 -0
- package/src/importer/docx/omml2mathml.js +1448 -0
- package/src/importer/docx/parse.js +735 -0
- package/src/importer/native/get_images.js +76 -0
- package/src/importer/native/update.js +29 -0
- package/src/importer/odt/citations.js +87 -0
- package/src/importer/odt/convert.js +1855 -0
- package/src/importer/pandoc/convert.js +884 -0
- package/src/importer/pandoc/helpers.js +84 -0
- package/src/importer/zip_analyzer.js +102 -0
- package/src/index.js +1 -0
- package/src/mathlive/opf_includes.js +24 -0
- package/src/schema/common/annotate.js +76 -0
- package/src/schema/common/base.js +118 -0
- package/src/schema/common/citation.js +62 -0
- package/src/schema/common/equation.js +31 -0
- package/src/schema/common/figure.js +190 -0
- package/src/schema/common/heading.js +43 -0
- package/src/schema/common/index.js +40 -0
- package/src/schema/common/list.js +95 -0
- package/src/schema/common/reference.js +100 -0
- package/src/schema/common/table.js +103 -0
- package/src/schema/common/track.js +190 -0
- package/src/schema/const.js +58 -0
- package/src/schema/convert.js +1272 -0
- package/src/schema/document/content.js +187 -0
- package/src/schema/document/index.js +117 -0
- package/src/schema/document/structure.js +452 -0
- package/src/schema/export.js +21 -0
- package/src/schema/footnotes.js +126 -0
- package/src/schema/footnotes_convert.js +31 -0
- package/src/schema/i18n.js +595 -0
- package/src/schema/index.js +5 -0
- package/src/schema/mini_json.js +61 -0
- package/src/schema/text.js +22 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const applyMarkToNodes = (nodes, markType, attrs = null) => {
|
|
2
|
+
return nodes.map(node => {
|
|
3
|
+
if (node.type === "text") {
|
|
4
|
+
const mark = attrs ? {type: markType, attrs} : {type: markType}
|
|
5
|
+
return {
|
|
6
|
+
...node,
|
|
7
|
+
marks: [...(node.marks || []), mark]
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return node
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const mergeTextNodes = nodes => {
|
|
15
|
+
const mergedNodes = []
|
|
16
|
+
let currentNode = null
|
|
17
|
+
|
|
18
|
+
const areSameMarks = (marks1 = [], marks2 = []) => {
|
|
19
|
+
if (marks1.length !== marks2.length) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
// Sort marks by type to ensure consistent comparison
|
|
23
|
+
const sortedMarks1 = [...marks1].sort((a, b) =>
|
|
24
|
+
a.type.localeCompare(b.type)
|
|
25
|
+
)
|
|
26
|
+
const sortedMarks2 = [...marks2].sort((a, b) =>
|
|
27
|
+
a.type.localeCompare(b.type)
|
|
28
|
+
)
|
|
29
|
+
return sortedMarks1.every((mark, index) => {
|
|
30
|
+
const mark2 = sortedMarks2[index]
|
|
31
|
+
if (mark.type !== mark2.type) {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
if (!mark.attrs && !mark2.attrs) {
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
return JSON.stringify(mark.attrs) === JSON.stringify(mark2.attrs)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
nodes.forEach(node => {
|
|
42
|
+
if (node.type === "text") {
|
|
43
|
+
if (
|
|
44
|
+
currentNode &&
|
|
45
|
+
currentNode.type === "text" &&
|
|
46
|
+
areSameMarks(currentNode.marks, node.marks)
|
|
47
|
+
) {
|
|
48
|
+
// Merge with previous node
|
|
49
|
+
currentNode.text += node.text
|
|
50
|
+
} else {
|
|
51
|
+
// Start new node
|
|
52
|
+
if (currentNode) {
|
|
53
|
+
mergedNodes.push(currentNode)
|
|
54
|
+
}
|
|
55
|
+
currentNode = {...node}
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
if (currentNode) {
|
|
59
|
+
mergedNodes.push(currentNode)
|
|
60
|
+
}
|
|
61
|
+
mergedNodes.push(node)
|
|
62
|
+
currentNode = null
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (currentNode) {
|
|
67
|
+
mergedNodes.push(currentNode)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return mergedNodes
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const applyAnnotation = (nodes, type) => {
|
|
74
|
+
return nodes.map(node => ({
|
|
75
|
+
...node,
|
|
76
|
+
marks: [
|
|
77
|
+
...(node.marks || []),
|
|
78
|
+
{
|
|
79
|
+
type: "annotation_tag",
|
|
80
|
+
attrs: {type, key: "", value: ""}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}))
|
|
84
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export class ZipAnalyzer {
|
|
2
|
+
constructor(zip, formats = []) {
|
|
3
|
+
this.zip = zip
|
|
4
|
+
this.formats = formats
|
|
5
|
+
|
|
6
|
+
this.analysis = null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
analyze() {
|
|
10
|
+
if (this.analysis) {
|
|
11
|
+
return this.analysis
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let convertibleFile = null
|
|
15
|
+
const imageFiles = []
|
|
16
|
+
let bibFile = null
|
|
17
|
+
|
|
18
|
+
// Analyze all files in the ZIP
|
|
19
|
+
this.zip.forEach((relativePath, zipEntry) => {
|
|
20
|
+
if (!zipEntry.dir) {
|
|
21
|
+
const fileName = relativePath.split("/").pop()
|
|
22
|
+
const extension = fileName.split(".").pop().toLowerCase()
|
|
23
|
+
|
|
24
|
+
if (extension === "bib") {
|
|
25
|
+
bibFile = zipEntry
|
|
26
|
+
} else if (
|
|
27
|
+
[
|
|
28
|
+
"avif",
|
|
29
|
+
"avifs",
|
|
30
|
+
"png",
|
|
31
|
+
"jpg",
|
|
32
|
+
"jpeg",
|
|
33
|
+
"gif",
|
|
34
|
+
"svg",
|
|
35
|
+
"webp"
|
|
36
|
+
].includes(extension)
|
|
37
|
+
) {
|
|
38
|
+
imageFiles.push({path: relativePath, entry: zipEntry})
|
|
39
|
+
} else if (this.formats.includes(extension)) {
|
|
40
|
+
// Store the first convertible file found
|
|
41
|
+
if (!convertibleFile) {
|
|
42
|
+
convertibleFile = {
|
|
43
|
+
path: relativePath,
|
|
44
|
+
entry: zipEntry,
|
|
45
|
+
fileName,
|
|
46
|
+
format: extension
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
this.analysis = {
|
|
54
|
+
hasConvertible: Boolean(convertibleFile),
|
|
55
|
+
format: convertibleFile ? convertibleFile.format : null,
|
|
56
|
+
convertibleFile,
|
|
57
|
+
imageFiles,
|
|
58
|
+
bibFile
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return this.analysis
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getContents() {
|
|
65
|
+
if (!this.analysis) {
|
|
66
|
+
await this.analyze()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const contents = {
|
|
70
|
+
images: {},
|
|
71
|
+
bibliography: null,
|
|
72
|
+
mainContent: null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Load main content file
|
|
76
|
+
if (this.analysis.hasConvertible) {
|
|
77
|
+
const mainBlob =
|
|
78
|
+
await this.analysis.convertibleFile.entry.async("blob")
|
|
79
|
+
contents.mainContent = new File(
|
|
80
|
+
[mainBlob],
|
|
81
|
+
this.analysis.convertibleFile.fileName
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Load images
|
|
86
|
+
const imagePromises = this.analysis.imageFiles.map(
|
|
87
|
+
async ({path, entry}) => {
|
|
88
|
+
const blob = await entry.async("blob")
|
|
89
|
+
contents.images[path] = blob
|
|
90
|
+
return {filename: path, blob}
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
await Promise.all(imagePromises)
|
|
94
|
+
|
|
95
|
+
// Load bibliography if present
|
|
96
|
+
if (this.analysis.bibFile) {
|
|
97
|
+
contents.bibliography = await this.analysis.bibFile.async("text")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return contents
|
|
101
|
+
}
|
|
102
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {FW_DOCUMENT_VERSION} from "./schema/index.js"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// This file is auto-generated. CHANGES WILL BE OVERWRITTEN! Re-generate by running ./manage.py bundle_mathlive.
|
|
2
|
+
export const mathliveOpfIncludes = `
|
|
3
|
+
<item id="mathlive-0" href="css/mathlive.css" media-type="text/css" />
|
|
4
|
+
<item id="mathlive-1" href="css/media/KaTeX_Caligraphic-Bold.woff2" media-type="font/woff2" />
|
|
5
|
+
<item id="mathlive-2" href="css/media/KaTeX_Fraktur-Bold.woff2" media-type="font/woff2" />
|
|
6
|
+
<item id="mathlive-3" href="css/media/KaTeX_SansSerif-Bold.woff2" media-type="font/woff2" />
|
|
7
|
+
<item id="mathlive-4" href="css/media/KaTeX_Size1-Regular.woff2" media-type="font/woff2" />
|
|
8
|
+
<item id="mathlive-5" href="css/media/KaTeX_Fraktur-Regular.woff2" media-type="font/woff2" />
|
|
9
|
+
<item id="mathlive-6" href="css/media/KaTeX_SansSerif-Regular.woff2" media-type="font/woff2" />
|
|
10
|
+
<item id="mathlive-7" href="css/media/KaTeX_Main-Italic.woff2" media-type="font/woff2" />
|
|
11
|
+
<item id="mathlive-8" href="css/media/KaTeX_Size4-Regular.woff2" media-type="font/woff2" />
|
|
12
|
+
<item id="mathlive-9" href="css/media/KaTeX_Caligraphic-Regular.woff2" media-type="font/woff2" />
|
|
13
|
+
<item id="mathlive-10" href="css/media/KaTeX_AMS-Regular.woff2" media-type="font/woff2" />
|
|
14
|
+
<item id="mathlive-11" href="css/media/KaTeX_SansSerif-Italic.woff2" media-type="font/woff2" />
|
|
15
|
+
<item id="mathlive-12" href="css/media/KaTeX_Size3-Regular.woff2" media-type="font/woff2" />
|
|
16
|
+
<item id="mathlive-13" href="css/media/KaTeX_Size2-Regular.woff2" media-type="font/woff2" />
|
|
17
|
+
<item id="mathlive-14" href="css/media/KaTeX_Script-Regular.woff2" media-type="font/woff2" />
|
|
18
|
+
<item id="mathlive-15" href="css/media/KaTeX_Main-Bold.woff2" media-type="font/woff2" />
|
|
19
|
+
<item id="mathlive-16" href="css/media/KaTeX_Math-BoldItalic.woff2" media-type="font/woff2" />
|
|
20
|
+
<item id="mathlive-17" href="css/media/KaTeX_Main-BoldItalic.woff2" media-type="font/woff2" />
|
|
21
|
+
<item id="mathlive-18" href="css/media/KaTeX_Main-Regular.woff2" media-type="font/woff2" />
|
|
22
|
+
<item id="mathlive-19" href="css/media/KaTeX_Math-Italic.woff2" media-type="font/woff2" />
|
|
23
|
+
<item id="mathlive-20" href="css/media/KaTeX_Typewriter-Regular.woff2" media-type="font/woff2" />
|
|
24
|
+
`
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Annotation tag is not used by the core Fidus Writer editor, but can be used by plugins that need to add annotation capability.
|
|
2
|
+
export const annotation_tag = {
|
|
3
|
+
attrs: {
|
|
4
|
+
type: {
|
|
5
|
+
default: "" // Make this a string unique to your plugin so that you avoid handling tags of other plugins. For example 'rdfa' for an rdfa-tagging plugin.
|
|
6
|
+
},
|
|
7
|
+
key: {
|
|
8
|
+
default: "" // key or variable/tag name
|
|
9
|
+
},
|
|
10
|
+
value: {
|
|
11
|
+
default: "" // value of variable/tag
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
inclusive: false,
|
|
15
|
+
excludes: "", // allows several tags on the same content.
|
|
16
|
+
group: "annotation",
|
|
17
|
+
parseDOM: [
|
|
18
|
+
{
|
|
19
|
+
tag: "span.annotation-tag[data-type]",
|
|
20
|
+
getAttrs(dom) {
|
|
21
|
+
return {
|
|
22
|
+
type: dom.dataset.type,
|
|
23
|
+
key: dom.dataset.key ? dom.dataset.key : "",
|
|
24
|
+
value: dom.dataset.value ? dom.dataset.value : ""
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
toDOM(node) {
|
|
30
|
+
const attrs = {
|
|
31
|
+
class: "annotation-tag",
|
|
32
|
+
"data-type": node.attrs.type
|
|
33
|
+
}
|
|
34
|
+
if (node.attrs.key?.length) {
|
|
35
|
+
attrs["data-key"] = node.attrs.key
|
|
36
|
+
}
|
|
37
|
+
if (node.attrs.value?.length) {
|
|
38
|
+
attrs["data-value"] = node.attrs.value
|
|
39
|
+
}
|
|
40
|
+
return ["span", attrs]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const comment = {
|
|
45
|
+
attrs: {
|
|
46
|
+
id: {
|
|
47
|
+
default: false
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
inclusive: false,
|
|
51
|
+
excludes: "",
|
|
52
|
+
group: "annotation",
|
|
53
|
+
parseDOM: [
|
|
54
|
+
{
|
|
55
|
+
tag: "span.comment[data-id]",
|
|
56
|
+
getAttrs(dom) {
|
|
57
|
+
return {
|
|
58
|
+
id: Number.parseInt(dom.dataset.id)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
toDOM(node) {
|
|
64
|
+
return [
|
|
65
|
+
"span",
|
|
66
|
+
{
|
|
67
|
+
class: "comment",
|
|
68
|
+
"data-id": node.attrs.id
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function randomCommentId() {
|
|
75
|
+
return String(Math.floor(Math.random() * 0xffffffff))
|
|
76
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {parseTracks} from "./track.js"
|
|
2
|
+
|
|
3
|
+
// :: NodeSpec A plain paragraph textblock. Represented in the DOM
|
|
4
|
+
// as a `<p>` element.
|
|
5
|
+
export const paragraph = {
|
|
6
|
+
group: "block",
|
|
7
|
+
content: "inline*",
|
|
8
|
+
attrs: {
|
|
9
|
+
track: {
|
|
10
|
+
default: []
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
parseDOM: [
|
|
14
|
+
{
|
|
15
|
+
tag: "p",
|
|
16
|
+
getAttrs(dom) {
|
|
17
|
+
return {
|
|
18
|
+
track: parseTracks(dom.dataset.track)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
toDOM(node) {
|
|
24
|
+
const attrs =
|
|
25
|
+
node.attrs.track && node.attrs.track.length
|
|
26
|
+
? {"data-track": JSON.stringify(node.attrs.track)}
|
|
27
|
+
: {}
|
|
28
|
+
return ["p", attrs, 0]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
|
|
33
|
+
export const blockquote = {
|
|
34
|
+
content: "block+",
|
|
35
|
+
group: "block",
|
|
36
|
+
attrs: {
|
|
37
|
+
track: {
|
|
38
|
+
default: []
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
marks: "annotation",
|
|
42
|
+
defining: true,
|
|
43
|
+
parseDOM: [
|
|
44
|
+
{
|
|
45
|
+
tag: "blockquote",
|
|
46
|
+
getAttrs(dom) {
|
|
47
|
+
return {
|
|
48
|
+
track: parseTracks(dom.dataset.track)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
toDOM(node) {
|
|
54
|
+
const attrs =
|
|
55
|
+
node.attrs.track && node.attrs.track.length
|
|
56
|
+
? {"data-track": JSON.stringify(node.attrs.track)}
|
|
57
|
+
: {}
|
|
58
|
+
return ["blockquote", attrs, 0]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// :: NodeSpec A horizontal rule (`<hr>`).
|
|
63
|
+
export const horizontal_rule = {
|
|
64
|
+
group: "block",
|
|
65
|
+
attrs: {
|
|
66
|
+
track: {
|
|
67
|
+
default: []
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
parseDOM: [
|
|
71
|
+
{
|
|
72
|
+
tag: "hr",
|
|
73
|
+
getAttrs(dom) {
|
|
74
|
+
return {
|
|
75
|
+
track: parseTracks(dom.dataset.track)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
toDOM(node) {
|
|
81
|
+
const attrs =
|
|
82
|
+
node.attrs.track && node.attrs.track.length
|
|
83
|
+
? {"data-track": JSON.stringify(node.attrs.track)}
|
|
84
|
+
: {}
|
|
85
|
+
return ["hr", attrs]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const underline = {
|
|
90
|
+
parseDOM: [{tag: "span.underline"}],
|
|
91
|
+
toDOM() {
|
|
92
|
+
return ["span", {class: "underline"}, 0]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const sup = {
|
|
97
|
+
parseDOM: [{tag: "sup"}],
|
|
98
|
+
toDOM() {
|
|
99
|
+
return ["sup", 0]
|
|
100
|
+
},
|
|
101
|
+
excludes: "sub"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const sub = {
|
|
105
|
+
parseDOM: [{tag: "sub"}],
|
|
106
|
+
toDOM() {
|
|
107
|
+
return ["sub", 0]
|
|
108
|
+
},
|
|
109
|
+
excludes: "sup"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const code = {
|
|
113
|
+
parseDOM: [{tag: "code"}],
|
|
114
|
+
toDOM() {
|
|
115
|
+
return ["code", 0]
|
|
116
|
+
},
|
|
117
|
+
excludes: "strong em underline link sup sub"
|
|
118
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function parseReferences(str) {
|
|
2
|
+
if (!str) {
|
|
3
|
+
return []
|
|
4
|
+
}
|
|
5
|
+
let references
|
|
6
|
+
try {
|
|
7
|
+
references = JSON.parse(str)
|
|
8
|
+
} catch (_error) {
|
|
9
|
+
return []
|
|
10
|
+
}
|
|
11
|
+
if (!Array.isArray(references)) {
|
|
12
|
+
return []
|
|
13
|
+
}
|
|
14
|
+
return references
|
|
15
|
+
.filter(
|
|
16
|
+
ref => ref.hasOwnProperty("id") // ensure there is an id.
|
|
17
|
+
)
|
|
18
|
+
.map(ref => {
|
|
19
|
+
const mRef = {id: ref.id}
|
|
20
|
+
if (ref.locator) {
|
|
21
|
+
mRef.locator = ref.locator
|
|
22
|
+
}
|
|
23
|
+
if (ref.prefix) {
|
|
24
|
+
mRef.prefix = ref.prefix
|
|
25
|
+
}
|
|
26
|
+
return mRef
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const citation = {
|
|
31
|
+
inline: true,
|
|
32
|
+
group: "inline",
|
|
33
|
+
attrs: {
|
|
34
|
+
format: {
|
|
35
|
+
default: "autocite" // "autocite" or "textcite"
|
|
36
|
+
},
|
|
37
|
+
references: {
|
|
38
|
+
default: [] // array of {id[, locator][, prefix]}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
parseDOM: [
|
|
42
|
+
{
|
|
43
|
+
tag: "span.citation",
|
|
44
|
+
getAttrs(dom) {
|
|
45
|
+
return {
|
|
46
|
+
format: dom.dataset.format || "",
|
|
47
|
+
references: parseReferences(dom.dataset.references)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
toDOM(node) {
|
|
53
|
+
return [
|
|
54
|
+
"span",
|
|
55
|
+
{
|
|
56
|
+
class: "citation",
|
|
57
|
+
"data-format": node.attrs.format,
|
|
58
|
+
"data-references": JSON.stringify(node.attrs.references)
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const equation = {
|
|
2
|
+
inline: true,
|
|
3
|
+
group: "inline",
|
|
4
|
+
attrs: {
|
|
5
|
+
equation: {
|
|
6
|
+
default: ""
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
parseDOM: [
|
|
10
|
+
{
|
|
11
|
+
tag: "span.equation",
|
|
12
|
+
getAttrs(dom) {
|
|
13
|
+
return {
|
|
14
|
+
equation: dom.dataset.equation
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
toDOM(node) {
|
|
20
|
+
const dom = document.createElement("span")
|
|
21
|
+
dom.dataset.equation = node.attrs.equation
|
|
22
|
+
dom.classList.add("equation")
|
|
23
|
+
import("mathlive").then(MathLive => {
|
|
24
|
+
dom.innerHTML = MathLive.convertLatexToMarkup(node.attrs.equation, {
|
|
25
|
+
mathstyle: "textstyle"
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
dom.setAttribute("contenteditable", "false")
|
|
29
|
+
return dom
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {parseTracks} from "./track.js"
|
|
2
|
+
|
|
3
|
+
export function randomFigureId() {
|
|
4
|
+
return "F" + Math.round(Math.random() * 10000000) + 1
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let imageDBBroken = false
|
|
8
|
+
|
|
9
|
+
export const figure = {
|
|
10
|
+
inline: false,
|
|
11
|
+
allowGapCursor: false,
|
|
12
|
+
selectable: true,
|
|
13
|
+
group: "block",
|
|
14
|
+
attrs: {
|
|
15
|
+
category: {default: "none"},
|
|
16
|
+
caption: {default: false},
|
|
17
|
+
id: {default: false},
|
|
18
|
+
track: {default: []},
|
|
19
|
+
aligned: {default: "center"},
|
|
20
|
+
width: {default: "100"}
|
|
21
|
+
},
|
|
22
|
+
content:
|
|
23
|
+
"figure_caption image|figure_caption figure_equation|image figure_caption|figure_equation figure_caption",
|
|
24
|
+
parseDOM: [
|
|
25
|
+
{
|
|
26
|
+
tag: "figure",
|
|
27
|
+
getAttrs(dom) {
|
|
28
|
+
return {
|
|
29
|
+
category: dom.dataset.category,
|
|
30
|
+
caption: !!dom.dataset.captionHidden,
|
|
31
|
+
id: dom.id,
|
|
32
|
+
track: parseTracks(dom.dataset.track),
|
|
33
|
+
aligned: dom.dataset.aligned,
|
|
34
|
+
width: dom.dataset.width,
|
|
35
|
+
diff: dom.dataset.diff
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
toDOM(node) {
|
|
41
|
+
const attrs = {
|
|
42
|
+
id: node.attrs.id,
|
|
43
|
+
class: `aligned-${node.attrs.aligned} image-width-${node.attrs.width}`,
|
|
44
|
+
"data-aligned": node.attrs.aligned,
|
|
45
|
+
"data-width": node.attrs.width,
|
|
46
|
+
"data-category": node.attrs.category
|
|
47
|
+
}
|
|
48
|
+
if (!node.attrs.caption) {
|
|
49
|
+
attrs["data-caption-hidden"] = true
|
|
50
|
+
}
|
|
51
|
+
if (node.attrs.track?.length) {
|
|
52
|
+
attrs["data-track"] = JSON.stringify(node.attrs.track)
|
|
53
|
+
}
|
|
54
|
+
return ["figure", attrs, 0]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const image = {
|
|
59
|
+
selectable: false,
|
|
60
|
+
draggable: false,
|
|
61
|
+
attrs: {
|
|
62
|
+
image: {default: false}
|
|
63
|
+
},
|
|
64
|
+
parseDOM: [
|
|
65
|
+
{
|
|
66
|
+
tag: "img",
|
|
67
|
+
getAttrs(dom) {
|
|
68
|
+
const image = Number.parseInt(dom.dataset.image)
|
|
69
|
+
return {
|
|
70
|
+
image: isNaN(image) ? false : image
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
toDOM(node) {
|
|
76
|
+
const dom = document.createElement("img")
|
|
77
|
+
if (node.attrs.image !== false) {
|
|
78
|
+
dom.dataset.image = node.attrs.image
|
|
79
|
+
const imageDB = node.type.schema.cached.imageDB
|
|
80
|
+
if (imageDB) {
|
|
81
|
+
const imageEntry = imageDB.db[node.attrs.image]
|
|
82
|
+
if (imageEntry?.image) {
|
|
83
|
+
const isEncrypted =
|
|
84
|
+
imageEntry.file_type === "application/octet-stream"
|
|
85
|
+
if (isEncrypted) {
|
|
86
|
+
const editor = imageDB.mod?.editor
|
|
87
|
+
const key = editor?.e2ee?.key
|
|
88
|
+
if (key) {
|
|
89
|
+
import("../../editor/e2ee/encryptor.js").then(
|
|
90
|
+
({E2EEEncryptor}) => {
|
|
91
|
+
E2EEEncryptor.decryptImageToUrl(
|
|
92
|
+
imageEntry.image,
|
|
93
|
+
key,
|
|
94
|
+
imageEntry.original_file_type ||
|
|
95
|
+
"image/png"
|
|
96
|
+
)
|
|
97
|
+
.then(url => {
|
|
98
|
+
dom.setAttribute("src", url)
|
|
99
|
+
dom.dataset.imageSrc = url
|
|
100
|
+
})
|
|
101
|
+
.catch(() => {
|
|
102
|
+
dom.setAttribute(
|
|
103
|
+
"src",
|
|
104
|
+
(typeof staticUrl !== "undefined" ? staticUrl("img/error.avif") : "/static/img/error.avif")
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
} else {
|
|
110
|
+
dom.setAttribute("src", (typeof staticUrl !== "undefined" ? staticUrl("img/error.avif") : "/static/img/error.avif"))
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
const imgSrc = imageEntry.image
|
|
114
|
+
dom.setAttribute("src", imgSrc)
|
|
115
|
+
dom.dataset.imageSrc = imgSrc
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
/* The image was not present in the imageDB -- possibly because a collaborator just added it.
|
|
119
|
+
Try to reload the imageDB, but only once. If the image cannot be found in the updated
|
|
120
|
+
imageDB, do not attempt at reloading the imageDB if an image cannot be
|
|
121
|
+
found. */
|
|
122
|
+
if (imageDBBroken) {
|
|
123
|
+
dom.setAttribute("src", (typeof staticUrl !== "undefined" ? staticUrl("img/error.avif") : "/static/img/error.avif"))
|
|
124
|
+
} else {
|
|
125
|
+
imageDB.getDB().then(() => {
|
|
126
|
+
const refreshedEntry = imageDB.db[node.attrs.image]
|
|
127
|
+
if (refreshedEntry?.image) {
|
|
128
|
+
dom.setAttribute("src", refreshedEntry.image)
|
|
129
|
+
dom.dataset.imageSrc = refreshedEntry.image
|
|
130
|
+
} else {
|
|
131
|
+
imageDBBroken = true
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return dom
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const figure_equation = {
|
|
143
|
+
selectable: false,
|
|
144
|
+
draggable: false,
|
|
145
|
+
attrs: {
|
|
146
|
+
equation: {
|
|
147
|
+
default: false
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
parseDOM: [
|
|
151
|
+
{
|
|
152
|
+
tag: "div.figure-equation[data-equation]",
|
|
153
|
+
getAttrs(dom) {
|
|
154
|
+
return {
|
|
155
|
+
equation: dom.dataset.equation
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
toDOM(node) {
|
|
161
|
+
const dom = document.createElement("div")
|
|
162
|
+
dom.dataset.equation = node.attrs.equation
|
|
163
|
+
dom.classList.add("figure-equation")
|
|
164
|
+
if (node.attrs.equation !== false) {
|
|
165
|
+
import("mathlive").then(MathLive => {
|
|
166
|
+
dom.innerHTML = MathLive.convertLatexToMarkup(
|
|
167
|
+
node.attrs.equation,
|
|
168
|
+
{
|
|
169
|
+
mathstyle: "displaystyle"
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
return dom
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const figure_caption = {
|
|
179
|
+
isolating: true,
|
|
180
|
+
defining: true,
|
|
181
|
+
content: "inline*",
|
|
182
|
+
parseDOM: [{tag: "figcaption span.text"}],
|
|
183
|
+
toDOM() {
|
|
184
|
+
return [
|
|
185
|
+
"figcaption",
|
|
186
|
+
["span", {class: "label"}],
|
|
187
|
+
["span", {class: "text"}, 0]
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}
|