@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.
Files changed (110) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +16 -0
  3. package/jest.config.js +23 -0
  4. package/package.json +59 -0
  5. package/schema.json +1 -0
  6. package/scripts/export-schema.js +16 -0
  7. package/src/bibliography/common.js +92 -0
  8. package/src/bibliography/csl_bib.js +139 -0
  9. package/src/citations/citeproc_sys.js +42 -0
  10. package/src/citations/format.js +194 -0
  11. package/src/common/blob.js +10 -0
  12. package/src/common/file.js +25 -0
  13. package/src/common/index.js +12 -0
  14. package/src/common/network.js +79 -0
  15. package/src/common/text.js +44 -0
  16. package/src/editor/e2ee/encryptor.js +228 -0
  17. package/src/exporter/docx/citations.js +177 -0
  18. package/src/exporter/docx/comments.js +165 -0
  19. package/src/exporter/docx/footnotes.js +240 -0
  20. package/src/exporter/docx/images.js +101 -0
  21. package/src/exporter/docx/index.js +185 -0
  22. package/src/exporter/docx/lists.js +260 -0
  23. package/src/exporter/docx/math.js +46 -0
  24. package/src/exporter/docx/metadata.js +289 -0
  25. package/src/exporter/docx/rels.js +193 -0
  26. package/src/exporter/docx/render.js +941 -0
  27. package/src/exporter/docx/richtext.js +1182 -0
  28. package/src/exporter/docx/tables.js +112 -0
  29. package/src/exporter/docx/tools.js +50 -0
  30. package/src/exporter/epub/index.js +142 -0
  31. package/src/exporter/epub/templates.js +140 -0
  32. package/src/exporter/epub/tools.js +96 -0
  33. package/src/exporter/html/citations.js +121 -0
  34. package/src/exporter/html/convert.js +813 -0
  35. package/src/exporter/html/index.js +192 -0
  36. package/src/exporter/html/templates.js +34 -0
  37. package/src/exporter/html/tools.js +50 -0
  38. package/src/exporter/jats/bibliography.js +183 -0
  39. package/src/exporter/jats/citations.js +109 -0
  40. package/src/exporter/jats/convert.js +871 -0
  41. package/src/exporter/jats/index.js +92 -0
  42. package/src/exporter/jats/templates.js +35 -0
  43. package/src/exporter/jats/text.js +72 -0
  44. package/src/exporter/latex/convert.js +934 -0
  45. package/src/exporter/latex/escape_latex.js +21 -0
  46. package/src/exporter/latex/index.js +74 -0
  47. package/src/exporter/latex/readme.js +22 -0
  48. package/src/exporter/native/shrink.js +132 -0
  49. package/src/exporter/odt/citations.js +101 -0
  50. package/src/exporter/odt/footnotes.js +147 -0
  51. package/src/exporter/odt/images.js +115 -0
  52. package/src/exporter/odt/index.js +156 -0
  53. package/src/exporter/odt/math.js +57 -0
  54. package/src/exporter/odt/metadata.js +251 -0
  55. package/src/exporter/odt/render.js +806 -0
  56. package/src/exporter/odt/richtext.js +865 -0
  57. package/src/exporter/odt/styles.js +387 -0
  58. package/src/exporter/odt/track.js +68 -0
  59. package/src/exporter/pandoc/citations.js +98 -0
  60. package/src/exporter/pandoc/convert.js +1017 -0
  61. package/src/exporter/pandoc/index.js +92 -0
  62. package/src/exporter/pandoc/readme.js +8 -0
  63. package/src/exporter/pandoc/tools.js +51 -0
  64. package/src/exporter/print/index.js +177 -0
  65. package/src/exporter/tools/doc_content.js +144 -0
  66. package/src/exporter/tools/file.js +9 -0
  67. package/src/exporter/tools/json.js +73 -0
  68. package/src/exporter/tools/svg.js +29 -0
  69. package/src/exporter/tools/xml.js +531 -0
  70. package/src/exporter/tools/xml_zip.js +95 -0
  71. package/src/exporter/tools/zip.js +90 -0
  72. package/src/exporter/tools/zotero_csl.js +93 -0
  73. package/src/importer/citations.js +129 -0
  74. package/src/importer/docx/citations.js +123 -0
  75. package/src/importer/docx/convert.js +1427 -0
  76. package/src/importer/docx/helpers.js +9 -0
  77. package/src/importer/docx/omml2mathml.js +1448 -0
  78. package/src/importer/docx/parse.js +735 -0
  79. package/src/importer/native/get_images.js +76 -0
  80. package/src/importer/native/update.js +29 -0
  81. package/src/importer/odt/citations.js +87 -0
  82. package/src/importer/odt/convert.js +1855 -0
  83. package/src/importer/pandoc/convert.js +884 -0
  84. package/src/importer/pandoc/helpers.js +84 -0
  85. package/src/importer/zip_analyzer.js +102 -0
  86. package/src/index.js +1 -0
  87. package/src/mathlive/opf_includes.js +24 -0
  88. package/src/schema/common/annotate.js +76 -0
  89. package/src/schema/common/base.js +118 -0
  90. package/src/schema/common/citation.js +62 -0
  91. package/src/schema/common/equation.js +31 -0
  92. package/src/schema/common/figure.js +190 -0
  93. package/src/schema/common/heading.js +43 -0
  94. package/src/schema/common/index.js +40 -0
  95. package/src/schema/common/list.js +95 -0
  96. package/src/schema/common/reference.js +100 -0
  97. package/src/schema/common/table.js +103 -0
  98. package/src/schema/common/track.js +190 -0
  99. package/src/schema/const.js +58 -0
  100. package/src/schema/convert.js +1272 -0
  101. package/src/schema/document/content.js +187 -0
  102. package/src/schema/document/index.js +117 -0
  103. package/src/schema/document/structure.js +452 -0
  104. package/src/schema/export.js +21 -0
  105. package/src/schema/footnotes.js +126 -0
  106. package/src/schema/footnotes_convert.js +31 -0
  107. package/src/schema/i18n.js +595 -0
  108. package/src/schema/index.js +5 -0
  109. package/src/schema/mini_json.js +61 -0
  110. package/src/schema/text.js +22 -0
@@ -0,0 +1,1272 @@
1
+ /* To convert to and from how the document is stored in the database to how ProseMirror expects it.
2
+ We use the DOM import for ProseMirror as the JSON we store in the database is really jsonized HTML.
3
+ */
4
+ import deepEqual from "fast-deep-equal"
5
+ import {
6
+ randomFigureId,
7
+ randomHeadingId,
8
+ randomListId,
9
+ randomTableId
10
+ } from "./common/index.js"
11
+
12
+ export const getSettings = pmDoc => {
13
+ const settings = JSON.parse(JSON.stringify(pmDoc.attrs))
14
+ return settings
15
+ }
16
+
17
+ export const updateDoc = (doc, docVersion, bibliography = false) => {
18
+ /* This is to clean documents taking all the accepted formatting from older
19
+ versions and outputting the current version of the doc format.
20
+ Notice that the docVersion isn't the same as the version of the FW export
21
+ file in Fidus Writer < 3.2 (docVersion/FW file versions versions -1.X).
22
+ While the FW file version also says something about what files could be
23
+ available inside the FW zip, the doc_version refers to how the data is
24
+ stored in those files.
25
+ In general, an update to the doc_version will likely also trigger an
26
+ update to the version of the FW export file, the reverse is not always
27
+ true.
28
+ */
29
+
30
+ switch (docVersion) {
31
+ // Import from versions up to 3.0 no longer supported starting with Fidus Writer 3.5
32
+ case 1: // Fidus Writer 3.1 prerelease
33
+ doc = convertDocV1(doc)
34
+ doc = convertDocV11(doc)
35
+ doc = convertDocV12(doc)
36
+ doc = convertDocV13(doc, bibliography)
37
+ doc = convertDocV20(doc)
38
+ doc = convertDocV21(doc)
39
+ doc = convertDocV22(doc)
40
+ doc = convertDocV23(doc)
41
+ doc = convertDocV30(doc)
42
+ doc = convertDocV31(doc)
43
+ doc = convertDocV32(doc)
44
+ doc = convertDocV33(doc)
45
+ doc = convertDocV34(doc)
46
+ doc = convertDocV35(doc)
47
+ break
48
+ case 1.1: // Fidus Writer 3.1
49
+ doc = convertDocV11(doc)
50
+ doc = convertDocV12(doc)
51
+ doc = convertDocV13(doc, bibliography)
52
+ doc = convertDocV20(doc)
53
+ doc = convertDocV21(doc)
54
+ doc = convertDocV22(doc)
55
+ doc = convertDocV23(doc)
56
+ doc = convertDocV30(doc)
57
+ doc = convertDocV31(doc)
58
+ doc = convertDocV32(doc)
59
+ doc = convertDocV33(doc)
60
+ doc = convertDocV34(doc)
61
+ doc = convertDocV35(doc)
62
+ break
63
+ case 1.2: // Fidus Writer 3.2
64
+ doc = convertDocV12(doc)
65
+ doc = convertDocV13(doc, bibliography)
66
+ doc = convertDocV20(doc)
67
+ doc = convertDocV21(doc)
68
+ doc = convertDocV22(doc)
69
+ doc = convertDocV23(doc)
70
+ doc = convertDocV30(doc)
71
+ doc = convertDocV31(doc)
72
+ doc = convertDocV32(doc)
73
+ doc = convertDocV33(doc)
74
+ doc = convertDocV34(doc)
75
+ doc = convertDocV35(doc)
76
+ break
77
+ case 1.3: // Fidus Writer 3.3 prerelease
78
+ doc = convertDocV13(doc, bibliography)
79
+ doc = convertDocV20(doc)
80
+ doc = convertDocV21(doc)
81
+ doc = convertDocV22(doc)
82
+ doc = convertDocV23(doc)
83
+ doc = convertDocV30(doc)
84
+ doc = convertDocV31(doc)
85
+ doc = convertDocV32(doc)
86
+ doc = convertDocV33(doc)
87
+ doc = convertDocV34(doc)
88
+ doc = convertDocV35(doc)
89
+ break
90
+ case 2.0: // Fidus Writer 3.3
91
+ doc = convertDocV20(doc)
92
+ doc = convertDocV21(doc)
93
+ doc = convertDocV22(doc)
94
+ doc = convertDocV23(doc)
95
+ doc = convertDocV30(doc)
96
+ doc = convertDocV31(doc)
97
+ doc = convertDocV32(doc)
98
+ doc = convertDocV33(doc)
99
+ doc = convertDocV34(doc)
100
+ doc = convertDocV35(doc)
101
+ break
102
+ case 2.1: // Fidus Writer 3.4
103
+ doc = convertDocV21(doc)
104
+ doc = convertDocV22(doc)
105
+ doc = convertDocV23(doc)
106
+ doc = convertDocV30(doc)
107
+ doc = convertDocV31(doc)
108
+ doc = convertDocV32(doc)
109
+ doc = convertDocV33(doc)
110
+ doc = convertDocV34(doc)
111
+ doc = convertDocV35(doc)
112
+ break
113
+ case 2.2: // Fidus Writer 3.5.7
114
+ doc = convertDocV22(doc)
115
+ doc = convertDocV23(doc)
116
+ doc = convertDocV30(doc)
117
+ doc = convertDocV31(doc)
118
+ doc = convertDocV32(doc)
119
+ doc = convertDocV33(doc)
120
+ doc = convertDocV34(doc)
121
+ doc = convertDocV35(doc)
122
+ break
123
+ case 2.3: // Fidus Writer 3.5.10
124
+ doc = convertDocV23(doc)
125
+ doc = convertDocV30(doc)
126
+ doc = convertDocV31(doc)
127
+ doc = convertDocV32(doc)
128
+ doc = convertDocV33(doc)
129
+ doc = convertDocV34(doc)
130
+ doc = convertDocV35(doc)
131
+ break
132
+ case 3.0: // Fidus Writer 3.6
133
+ doc = convertDocV30(doc)
134
+ doc = convertDocV31(doc)
135
+ doc = convertDocV32(doc)
136
+ doc = convertDocV33(doc)
137
+ doc = convertDocV34(doc)
138
+ doc = convertDocV35(doc)
139
+ break
140
+ case 3.1: // Fidus Writer 3.7
141
+ doc = convertDocV31(doc)
142
+ doc = convertDocV32(doc)
143
+ doc = convertDocV33(doc)
144
+ doc = convertDocV34(doc)
145
+ doc = convertDocV35(doc)
146
+ break
147
+ case 3.2: // Fidus Writer 3.8
148
+ doc = convertDocV32(doc)
149
+ doc = convertDocV33(doc)
150
+ doc = convertDocV34(doc)
151
+ doc = convertDocV35(doc)
152
+ break
153
+ case 3.3: // Fidus Writer 3.9
154
+ doc = convertDocV33(doc)
155
+ doc = convertDocV34(doc)
156
+ doc = convertDocV35(doc)
157
+ break
158
+ case 3.4: // Fidus Writer 3.10
159
+ doc = convertDocV34(doc)
160
+ doc = convertDocV35(doc)
161
+ break
162
+ case 3.5: // Fidus Writer 4.0
163
+ doc = convertDocV35(doc)
164
+ break
165
+ case 3.6: // Fidus Writer 4.1
166
+ break
167
+ }
168
+ return doc
169
+ }
170
+
171
+ const convertDocV1 = doc => {
172
+ const returnDoc = JSON.parse(JSON.stringify(doc))
173
+ convertNodeV1(returnDoc.content)
174
+ return returnDoc
175
+ }
176
+
177
+ const convertNodeV1 = node => {
178
+ let prefixes, locators, ids, references
179
+ switch (node.type) {
180
+ case "citation":
181
+ prefixes = node.attrs.bibBefore
182
+ ? node.attrs.bibBefore.split(",,,")
183
+ : []
184
+ locators = node.attrs.bibPage ? node.attrs.bibPage.split(",,,") : []
185
+ ids = node.attrs.bibEntry ? node.attrs.bibEntry.split(",") : []
186
+ references = ids.map((id, index) => {
187
+ const returnObj = {id: Number.parseInt(id)}
188
+ if (prefixes[index] !== "") {
189
+ returnObj["prefix"] = prefixes[index]
190
+ }
191
+ if (locators[index] !== "") {
192
+ returnObj["locator"] = locators[index]
193
+ }
194
+ return returnObj
195
+ })
196
+ node.attrs = {
197
+ format: node.attrs.bibFormat,
198
+ references
199
+ }
200
+ break
201
+ case "footnote":
202
+ if (node.attrs?.footnote) {
203
+ node.attrs.footnote.forEach(childNode => {
204
+ convertNodeV1(childNode)
205
+ })
206
+ }
207
+ break
208
+ }
209
+ if (node.content) {
210
+ node.content.forEach(childNode => {
211
+ convertNodeV1(childNode)
212
+ })
213
+ }
214
+ }
215
+
216
+ const convertDocV11 = doc => {
217
+ const returnDoc = JSON.parse(JSON.stringify(doc))
218
+ convertNodeV11(returnDoc.content)
219
+ return returnDoc
220
+ }
221
+
222
+ const convertNodeV11 = (node, ids = []) => {
223
+ let blockId
224
+ switch (node.type) {
225
+ case "heading":
226
+ blockId = node.attrs.id
227
+ while (!blockId || ids.includes(blockId)) {
228
+ blockId = randomHeadingId()
229
+ }
230
+ node.attrs.id = blockId
231
+ ids.push(blockId)
232
+ break
233
+ }
234
+ if (node.content) {
235
+ node.content.forEach(childNode => {
236
+ convertNodeV11(childNode, ids)
237
+ })
238
+ }
239
+ }
240
+
241
+ const convertDocV12 = doc => {
242
+ const returnDoc = JSON.parse(JSON.stringify(doc))
243
+ convertNodeV12(returnDoc.content)
244
+ return returnDoc
245
+ }
246
+
247
+ const convertNodeV12 = (node, ids = []) => {
248
+ let blockId
249
+ switch (node.type) {
250
+ case "figure":
251
+ blockId = node.attrs.id
252
+ while (!blockId || ids.includes(blockId)) {
253
+ blockId = randomFigureId()
254
+ }
255
+ node.attrs.id = blockId
256
+ ids.push(blockId)
257
+ break
258
+ }
259
+ if (node.content) {
260
+ node.content.forEach(childNode => {
261
+ convertNodeV12(childNode, ids)
262
+ })
263
+ }
264
+ }
265
+
266
+ const convertDocV13 = (doc, bibliography) => {
267
+ const returnDoc = JSON.parse(JSON.stringify(doc))
268
+ delete returnDoc.settings
269
+ delete returnDoc.metadata
270
+ returnDoc.bibliography = {}
271
+ returnDoc.imageIds = []
272
+ convertNodeV13(
273
+ returnDoc.content,
274
+ returnDoc.bibliography,
275
+ bibliography,
276
+ returnDoc.imageIds
277
+ )
278
+ return returnDoc
279
+ }
280
+
281
+ const convertNodeV13 = (node, shrunkBib, fullBib, imageIds) => {
282
+ let authorsText, keywordsText
283
+ switch (node.type) {
284
+ case "article":
285
+ node.attrs.language = "en-US"
286
+ break
287
+ case "authors":
288
+ authorsText = node.content
289
+ ? node.content.reduce(
290
+ (text, item) =>
291
+ item.type === "text" ? text + item.text : text,
292
+ ""
293
+ )
294
+ : ""
295
+ node.content = authorsText
296
+ .split(/[,;]/g)
297
+ .map(authorString => {
298
+ const author = authorString.trim()
299
+ if (!author.length) {
300
+ return false
301
+ }
302
+ const authorParts = author.split(" ")
303
+ return {
304
+ type: "author",
305
+ attrs: {
306
+ firstname:
307
+ authorParts.length > 1
308
+ ? authorParts.shift()
309
+ : false,
310
+ lastname: authorParts.join(" "),
311
+ institution: false,
312
+ email: false
313
+ }
314
+ }
315
+ })
316
+ .filter(authorObj => authorObj)
317
+ if (!node.content.length) {
318
+ delete node.content
319
+ }
320
+ break
321
+ case "citation":
322
+ node.attrs.references.forEach(ref => {
323
+ let item = fullBib[ref.id]
324
+ if (!item) {
325
+ item = {
326
+ fields: {title: [{type: "text", text: "Deleted"}]},
327
+ bib_type: "misc",
328
+ entry_key: "FidusWriter"
329
+ }
330
+ }
331
+ item = Object.assign({}, item)
332
+ delete item.cats
333
+ shrunkBib[ref.id] = item
334
+ })
335
+ break
336
+ case "keywords":
337
+ keywordsText = node.content
338
+ ? node.content.reduce(
339
+ (text, item) =>
340
+ item.type === "text" ? text + item.text : text,
341
+ ""
342
+ )
343
+ : ""
344
+ node.content = keywordsText
345
+ .split(/[,;]/g)
346
+ .map(keywordString => {
347
+ const keyword = keywordString.trim()
348
+ if (!keyword.length) {
349
+ return false
350
+ }
351
+ return {
352
+ type: "keyword",
353
+ attrs: {
354
+ keyword
355
+ }
356
+ }
357
+ })
358
+ .filter(keywordObj => keywordObj)
359
+ if (!node.content.length) {
360
+ delete node.content
361
+ }
362
+ break
363
+ case "figure":
364
+ if (isNaN(Number.parseInt(node.attrs.image))) {
365
+ node.attrs.image = false
366
+ } else {
367
+ imageIds.push(Number.parseInt(node.attrs.image))
368
+ }
369
+ break
370
+ }
371
+ if (node.content) {
372
+ node.content.forEach(childNode => {
373
+ convertNodeV13(childNode, shrunkBib, fullBib, imageIds)
374
+ })
375
+ }
376
+ }
377
+
378
+ const convertDocV20 = doc => {
379
+ const returnDoc = JSON.parse(JSON.stringify(doc))
380
+ delete returnDoc.added
381
+ delete returnDoc.is_owner
382
+ delete returnDoc.revisions
383
+ delete returnDoc.rights
384
+ delete returnDoc.updated
385
+ if (returnDoc.content.attrs) {
386
+ returnDoc.content.attrs.tracked = false
387
+ }
388
+ Object.values(returnDoc.comments).forEach(comment => {
389
+ comment.username = comment.userName
390
+ comment.isMajor = comment["review:isMajor"]
391
+ delete comment.userAvatar
392
+ delete comment.userName
393
+ delete comment["review:isMajor"]
394
+ if (comment.answers) {
395
+ comment.answers.forEach(answer => {
396
+ answer.username = answer.userName
397
+ delete answer.userAvatar
398
+ delete answer.userName
399
+ })
400
+ }
401
+ })
402
+ return returnDoc
403
+ }
404
+
405
+ const convertNodeV21 = node => {
406
+ let commentMark
407
+ if (
408
+ node.marks &&
409
+ (commentMark = node.marks.find(mark => mark.type === "comment"))
410
+ ) {
411
+ commentMark.attrs.id = String(commentMark.attrs.id)
412
+ }
413
+ if (node.content) {
414
+ node.content.forEach(childNode => convertNodeV21(childNode))
415
+ }
416
+ }
417
+
418
+ const convertDocV21 = doc => {
419
+ const returnDoc = JSON.parse(JSON.stringify(doc))
420
+ convertNodeV21(returnDoc.content)
421
+ Object.entries(returnDoc.comment).forEach(([commentId, comment]) => {
422
+ delete comment.id
423
+ comment.assignedUser = false
424
+ comment.assignedUsername = false
425
+ comment.resolved = false
426
+ comment.comment = comment.comment.split("\n").map(text => ({
427
+ type: "paragraph",
428
+ content: [{type: "text", text}]
429
+ }))
430
+ if (comment.answers) {
431
+ comment.answers.forEach(answer => {
432
+ answer.id = answer.answerId
433
+ ? String(answer.answerId)
434
+ : answer.id && String(answer.id) !== String(commentId)
435
+ ? String(answer.id)
436
+ : String(Math.floor(Math.random() * 0xffffffff))
437
+ delete answer.answerId
438
+ answer.answer = answer.answer.split("\n").map(text => ({
439
+ type: "paragraph",
440
+ content: [{type: "text", text}]
441
+ }))
442
+ })
443
+ }
444
+ })
445
+ return returnDoc
446
+ }
447
+
448
+ const convertNodeV22 = (node, imageIds) => {
449
+ switch (node.type) {
450
+ case "figure":
451
+ if (!isNaN(Number.parseInt(node.attrs.image))) {
452
+ imageIds.push(Number.parseInt(node.attrs.image))
453
+ }
454
+ break
455
+ default:
456
+ break
457
+ }
458
+ if (node.content) {
459
+ const deleteChildren = []
460
+ node.content.forEach(childNode => {
461
+ if (childNode.type === "text" && !childNode.text.length) {
462
+ deleteChildren.push(childNode)
463
+ } else {
464
+ convertNodeV22(childNode, imageIds)
465
+ }
466
+ })
467
+ node.content = node.content.filter(
468
+ childNode => !deleteChildren.includes(childNode)
469
+ )
470
+ }
471
+ }
472
+
473
+ const convertDocV22 = doc => {
474
+ const returnDoc = JSON.parse(JSON.stringify(doc))
475
+ returnDoc.imageIds = []
476
+ convertNodeV22(returnDoc.content, returnDoc.imageIds)
477
+ Object.entries(returnDoc.comment).forEach(([_commentId, comment]) => {
478
+ comment.comment.forEach(commentNode =>
479
+ convertNodeV22(commentNode, returnDoc.imageIds)
480
+ )
481
+ if (comment.answers) {
482
+ comment.answers.forEach(answer => {
483
+ answer.answer.forEach(answerNode =>
484
+ convertNodeV22(answerNode, returnDoc.imageIds)
485
+ )
486
+ })
487
+ }
488
+ })
489
+ return returnDoc
490
+ }
491
+
492
+ const v23ExtraAttrs = {
493
+ languages: [
494
+ "af-ZA",
495
+ "sq-AL",
496
+ "ar",
497
+ "ast",
498
+ "be",
499
+ "br",
500
+ "bg",
501
+ "ca",
502
+ "ca-ES-Valencia",
503
+ "zh-CN",
504
+ "da",
505
+ "nl",
506
+ "en-AU",
507
+ "en-CA",
508
+ "en-NZ",
509
+ "en-ZA",
510
+ "en-GB",
511
+ "en-US",
512
+ "eo",
513
+ "fr",
514
+ "gl",
515
+ "de-DE",
516
+ "de-AU",
517
+ "de-CH",
518
+ "el",
519
+ "he",
520
+ "is",
521
+ "it",
522
+ "ja",
523
+ "km",
524
+ "lt",
525
+ "ml",
526
+ "nb-NO",
527
+ "nn-NO",
528
+ "fa",
529
+ "pl",
530
+ "pt-BR",
531
+ "pt-PT",
532
+ "ro",
533
+ "ru",
534
+ "tr",
535
+ "sr-SP-Cy",
536
+ "sr-SP-Lt",
537
+ "sk",
538
+ "sl",
539
+ "es",
540
+ "sv",
541
+ "ta",
542
+ "tl",
543
+ "uk"
544
+ ],
545
+ papersizes: ["A4", "US Letter"],
546
+ footnote_marks: ["strong", "em", "link", "anchor"],
547
+ footnote_elements: [
548
+ "paragraph",
549
+ "heading1",
550
+ "heading2",
551
+ "heading3",
552
+ "heading4",
553
+ "heading5",
554
+ "heading6",
555
+ "figure",
556
+ "ordered_list",
557
+ "bullet_list",
558
+ "horizontal_rule",
559
+ "equation",
560
+ "citation",
561
+ "blockquote",
562
+ "table"
563
+ ],
564
+ template: "Standard Article"
565
+ }
566
+
567
+ const convertNodeV23 = node => {
568
+ switch (node.type) {
569
+ case "article":
570
+ node.attrs = Object.assign({}, node.attrs, v23ExtraAttrs)
571
+ break
572
+ case "title":
573
+ node.attrs = {
574
+ title: "Title",
575
+ id: "title"
576
+ }
577
+ break
578
+ case "subtitle":
579
+ node.type = "heading_part"
580
+ node.attrs = {
581
+ title: "Subtitle",
582
+ id: "subtitle",
583
+ locking: false,
584
+ language: false,
585
+ optional: "hidden",
586
+ hidden: node.attrs.hidden,
587
+ help: false,
588
+ deleted: false,
589
+ elements: ["heading1"],
590
+ marks: ["strong", "em", "link", "anchor"]
591
+ }
592
+ node.content = [
593
+ {
594
+ type: "heading1",
595
+ attrs: {
596
+ id: "H5302207",
597
+ track: []
598
+ },
599
+ content: node.content
600
+ }
601
+ ]
602
+ break
603
+ case "authors":
604
+ node.type = "contributors_part"
605
+ node.attrs = {
606
+ title: "Authors",
607
+ id: "authors",
608
+ locking: false,
609
+ language: false,
610
+ optional: "hidden",
611
+ hidden: node.attrs.hidden,
612
+ help: false,
613
+ deleted: false,
614
+ item_title: "Author"
615
+ }
616
+ break
617
+ case "author":
618
+ node.type = "contributor"
619
+ break
620
+ case "abstract":
621
+ node.type = "richtext_part"
622
+ node.attrs = {
623
+ title: "Abstract",
624
+ id: "abstract",
625
+ locking: false,
626
+ language: false,
627
+ optional: "hidden",
628
+ hidden: node.attrs.hidden,
629
+ help: false,
630
+ deleted: false,
631
+ elements: [
632
+ "paragraph",
633
+ "heading1",
634
+ "heading2",
635
+ "heading3",
636
+ "heading4",
637
+ "heading5",
638
+ "heading6",
639
+ "figure",
640
+ "ordered_list",
641
+ "bullet_list",
642
+ "horizontal_rule",
643
+ "equation",
644
+ "citation",
645
+ "blockquote",
646
+ "footnote",
647
+ "table"
648
+ ],
649
+ marks: ["strong", "em", "link", "anchor"]
650
+ }
651
+ break
652
+ case "keywords":
653
+ node.type = "tags_part"
654
+ node.attrs = {
655
+ title: "Keywords",
656
+ id: "keywords",
657
+ locking: false,
658
+ language: false,
659
+ optional: "hidden",
660
+ hidden: node.attrs.hidden,
661
+ help: false,
662
+ deleted: false,
663
+ item_title: "Keyword"
664
+ }
665
+ break
666
+ case "keyword":
667
+ node.type = "tag"
668
+ node.attrs = {
669
+ tag: node.attrs.keyword
670
+ }
671
+ break
672
+ case "body":
673
+ node.type = "richtext_part"
674
+ node.attrs = {
675
+ title: "Body",
676
+ id: "body",
677
+ locking: false,
678
+ language: false,
679
+ optional: false,
680
+ hidden: false,
681
+ help: false,
682
+ deleted: false,
683
+ elements: [
684
+ "paragraph",
685
+ "heading1",
686
+ "heading2",
687
+ "heading3",
688
+ "heading4",
689
+ "heading5",
690
+ "heading6",
691
+ "figure",
692
+ "ordered_list",
693
+ "bullet_list",
694
+ "horizontal_rule",
695
+ "equation",
696
+ "citation",
697
+ "blockquote",
698
+ "footnote",
699
+ "table"
700
+ ],
701
+ marks: ["strong", "em", "link", "anchor"]
702
+ }
703
+ break
704
+ case "heading":
705
+ node.type = `heading${node.attrs.level}`
706
+ delete node.attrs.level
707
+ break
708
+ default:
709
+ break
710
+ }
711
+ if (node.content) {
712
+ node.content.forEach(childNode => {
713
+ convertNodeV23(childNode)
714
+ })
715
+ }
716
+ }
717
+
718
+ const convertDocV23 = doc => {
719
+ const returnDoc = JSON.parse(JSON.stringify(doc))
720
+ convertNodeV23(returnDoc.content)
721
+ returnDoc.settings = Object.assign({}, returnDoc.settings, v23ExtraAttrs)
722
+ return returnDoc
723
+ }
724
+
725
+ const convertNodeV30 = node => {
726
+ if (node.attrs?.marks && node.attrs.marks.filter) {
727
+ node.attrs.marks = node.attrs.marks.filter(mark => mark !== "anchor")
728
+ }
729
+ if (node.attrs?.footnote_marks) {
730
+ node.attrs.footnote_marks = node.attrs.footnote_marks.filter(
731
+ mark => mark !== "anchor"
732
+ )
733
+ }
734
+ let attrs
735
+ switch (node.type) {
736
+ case "article":
737
+ attrs = {
738
+ documentstyle: "",
739
+ tracked: false,
740
+ citationstyle: "apa",
741
+ language: "en-US",
742
+ languages: [
743
+ "af-ZA",
744
+ "sq-AL",
745
+ "ar",
746
+ "ast",
747
+ "be",
748
+ "br",
749
+ "bg",
750
+ "ca",
751
+ "ca-ES-Valencia",
752
+ "zh-CN",
753
+ "da",
754
+ "nl",
755
+ "en-AU",
756
+ "en-CA",
757
+ "en-NZ",
758
+ "en-ZA",
759
+ "en-GB",
760
+ "en-US",
761
+ "eo",
762
+ "fr",
763
+ "gl",
764
+ "de-DE",
765
+ "de-AU",
766
+ "de-CH",
767
+ "el",
768
+ "he",
769
+ "is",
770
+ "it",
771
+ "ja",
772
+ "km",
773
+ "lt",
774
+ "ml",
775
+ "nb-NO",
776
+ "nn-NO",
777
+ "fa",
778
+ "pl",
779
+ "pt-BR",
780
+ "pt-PT",
781
+ "ro",
782
+ "ru",
783
+ "tr",
784
+ "sr-SP-Cy",
785
+ "sr-SP-Lt",
786
+ "sk",
787
+ "sl",
788
+ "es",
789
+ "sv",
790
+ "ta",
791
+ "tl",
792
+ "uk"
793
+ ],
794
+ papersize: "A4",
795
+ papersizes: ["A4", "US Letter"],
796
+ footnote_marks: ["strong", "em", "link"],
797
+ footnote_elements: [
798
+ "paragraph",
799
+ "heading1",
800
+ "heading2",
801
+ "heading3",
802
+ "heading4",
803
+ "heading5",
804
+ "heading6",
805
+ "figure",
806
+ "ordered_list",
807
+ "bullet_list",
808
+ "horizontal_rule",
809
+ "equation",
810
+ "citation",
811
+ "blockquote",
812
+ "table"
813
+ ]
814
+ }
815
+ break
816
+ case "richtext_part":
817
+ attrs = {
818
+ title: "",
819
+ id: "",
820
+ locking: false,
821
+ language: false,
822
+ optional: false,
823
+ hidden: false,
824
+ help: false,
825
+ initial: false,
826
+ deleted: false,
827
+ elements: [
828
+ "paragraph",
829
+ "heading1",
830
+ "heading2",
831
+ "heading3",
832
+ "heading4",
833
+ "heading5",
834
+ "heading6",
835
+ "figure",
836
+ "ordered_list",
837
+ "bullet_list",
838
+ "horizontal_rule",
839
+ "equation",
840
+ "citation",
841
+ "blockquote",
842
+ "footnote",
843
+ "table"
844
+ ],
845
+ marks: ["strong", "em", "link"],
846
+ metadata: false
847
+ }
848
+ break
849
+ case "heading_part":
850
+ attrs = {
851
+ title: "",
852
+ id: "",
853
+ locking: false,
854
+ language: false,
855
+ optional: false,
856
+ hidden: false,
857
+ help: false,
858
+ initial: false,
859
+ deleted: false,
860
+ elements: ["heading1"],
861
+ marks: ["strong", "em", "link"],
862
+ metadata: false
863
+ }
864
+ break
865
+ case "contributors_part":
866
+ attrs = {
867
+ title: "",
868
+ id: "",
869
+ locking: false,
870
+ language: false,
871
+ optional: false,
872
+ hidden: false,
873
+ help: false,
874
+ initial: false,
875
+ deleted: false,
876
+ item_title: "Contributor",
877
+ metadata: false
878
+ }
879
+ break
880
+ case "tags_part":
881
+ attrs = {
882
+ title: "",
883
+ id: "",
884
+ locking: false,
885
+ language: false,
886
+ optional: false,
887
+ hidden: false,
888
+ help: false,
889
+ initial: false,
890
+ deleted: false,
891
+ item_title: "Tag",
892
+ metadata: false
893
+ }
894
+ break
895
+ case "table_part":
896
+ attrs = {
897
+ title: "",
898
+ id: "",
899
+ locking: false,
900
+ language: false,
901
+ optional: false,
902
+ hidden: false,
903
+ help: false,
904
+ initial: false,
905
+ deleted: false,
906
+ elements: [
907
+ "paragraph",
908
+ "heading1",
909
+ "heading2",
910
+ "heading3",
911
+ "heading4",
912
+ "heading5",
913
+ "heading6",
914
+ "figure",
915
+ "ordered_list",
916
+ "bullet_list",
917
+ "horizontal_rule",
918
+ "equation",
919
+ "citation",
920
+ "blockquote",
921
+ "footnote"
922
+ ],
923
+ marks: ["strong", "em", "link"],
924
+ metadata: false
925
+ }
926
+ break
927
+ case "table_of_contents":
928
+ attrs = {
929
+ title: "Table of Contents",
930
+ id: "toc",
931
+ optional: false,
932
+ hidden: false
933
+ }
934
+ break
935
+ case "separator_part":
936
+ attrs = {
937
+ id: "separator"
938
+ }
939
+ break
940
+ case "title":
941
+ attrs = {
942
+ id: "title"
943
+ }
944
+ break
945
+ case "contributor":
946
+ attrs = {
947
+ firstname: false,
948
+ lastname: false,
949
+ email: false,
950
+ institution: false
951
+ }
952
+ break
953
+ case "tag":
954
+ attrs = {
955
+ tag: ""
956
+ }
957
+ break
958
+ case "footnote":
959
+ attrs = {
960
+ footnote: [
961
+ {
962
+ type: "paragraph"
963
+ }
964
+ ]
965
+ }
966
+ break
967
+ case "code_block":
968
+ case "paragraph":
969
+ case "blockquote":
970
+ case "horizontal_rule":
971
+ case "bullet_list":
972
+ case "list_item":
973
+ attrs = {
974
+ track: []
975
+ }
976
+ break
977
+ case "ordered_list":
978
+ attrs = {
979
+ order: 1,
980
+ track: []
981
+ }
982
+ break
983
+ case "citation":
984
+ attrs = {
985
+ format: "autocite",
986
+ references: []
987
+ }
988
+ break
989
+ case "equation":
990
+ attrs = {
991
+ equation: ""
992
+ }
993
+ break
994
+ case "figure":
995
+ attrs = {
996
+ equation: "",
997
+ image: false,
998
+ figureCategory: "",
999
+ caption: "",
1000
+ id: false,
1001
+ track: [],
1002
+ aligned: "center",
1003
+ width: "100"
1004
+ }
1005
+ break
1006
+ case "heading1":
1007
+ case "heading2":
1008
+ case "heading3":
1009
+ case "heading4":
1010
+ case "heading5":
1011
+ case "heading6":
1012
+ attrs = {
1013
+ id: false,
1014
+ track: []
1015
+ }
1016
+ break
1017
+ default:
1018
+ break
1019
+ }
1020
+
1021
+ if (attrs && node.attrs) {
1022
+ for (const attr in attrs) {
1023
+ if (
1024
+ attr in node.attrs &&
1025
+ deepEqual(node.attrs[attr], attrs[attr])
1026
+ ) {
1027
+ delete node.attrs[attr]
1028
+ }
1029
+ }
1030
+ switch (node.type) {
1031
+ case "article": {
1032
+ if (node.attrs.language === "") {
1033
+ delete node.attrs.language
1034
+ }
1035
+ const template = node.attrs.template || "default"
1036
+ node.attrs.import_id = template
1037
+ .normalize("NFKC")
1038
+ .replace(/[^\w\s-]/g, "")
1039
+ .toLowerCase()
1040
+ .trim()
1041
+ .replace(/[-\s]+/g, "-")
1042
+ switch (node.attrs.citationstyle) {
1043
+ case "harvard1":
1044
+ node.attrs.citationstyle = "harvard-cite-them-right"
1045
+ break
1046
+ case "mla":
1047
+ node.attrs.citationstyle = "modern-language-association"
1048
+ break
1049
+ case "american-anthropological-association":
1050
+ case "chicago-author-date":
1051
+ case "chicago-note-bibliography":
1052
+ case "oxford-university-press-humsoc":
1053
+ case "nature":
1054
+ break
1055
+ default:
1056
+ delete node.attrs.citationstyle
1057
+ }
1058
+ break
1059
+ }
1060
+ case "title":
1061
+ delete node.attrs.title
1062
+ break
1063
+ default:
1064
+ break
1065
+ }
1066
+ }
1067
+
1068
+ if (node.marks) {
1069
+ for (const mark in node.marks) {
1070
+ let attrs
1071
+ switch (mark.type) {
1072
+ case "comment":
1073
+ attrs = {
1074
+ id: false
1075
+ }
1076
+ break
1077
+ case "annotation_tag":
1078
+ attrs = {
1079
+ type: "",
1080
+ key: "",
1081
+ value: ""
1082
+ }
1083
+ break
1084
+ case "anchor":
1085
+ attrs = {
1086
+ id: false
1087
+ }
1088
+ break
1089
+ case "deletion":
1090
+ attrs = {
1091
+ user: 0,
1092
+ username: "",
1093
+ date: 0
1094
+ }
1095
+ break
1096
+ case "insertion":
1097
+ attrs = {
1098
+ user: 0,
1099
+ username: "",
1100
+ date: 0,
1101
+ approved: true
1102
+ }
1103
+ break
1104
+ case "format_change":
1105
+ attrs = {
1106
+ user: 0,
1107
+ username: "",
1108
+ date: 0,
1109
+ before: [],
1110
+ after: []
1111
+ }
1112
+ break
1113
+ }
1114
+ if (attrs && mark.attrs) {
1115
+ for (const attr in attrs) {
1116
+ if (
1117
+ attr in mark.attrs &&
1118
+ deepEqual(mark.attrs[attr], attrs[attr])
1119
+ ) {
1120
+ delete mark.attrs[attr]
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+
1127
+ if (node.content) {
1128
+ node.content.forEach(childNode => {
1129
+ convertNodeV30(childNode)
1130
+ })
1131
+ }
1132
+ }
1133
+
1134
+ const convertDocV30 = doc => {
1135
+ const returnDoc = JSON.parse(JSON.stringify(doc))
1136
+ convertNodeV30(returnDoc.content)
1137
+ return returnDoc
1138
+ }
1139
+
1140
+ const convertDocV31 = doc => {
1141
+ // Conversion adds no new requirements. Version update is required so that
1142
+ // users don't try to open file in a previous FW file. That won't work as
1143
+ // additional syntax has been added (copyright + cross references).
1144
+ const returnDoc = JSON.parse(JSON.stringify(doc))
1145
+ return returnDoc
1146
+ }
1147
+
1148
+ const convertNodeV32 = (node, ids = []) => {
1149
+ let blockId, attrs
1150
+ switch (node.type) {
1151
+ case "table":
1152
+ attrs = node.attrs || {}
1153
+ blockId = attrs.id
1154
+ while (!blockId || ids.includes(blockId)) {
1155
+ blockId = randomTableId()
1156
+ }
1157
+ attrs.id = blockId
1158
+ attrs.caption = false
1159
+ node.attrs = attrs
1160
+ ids.push(blockId)
1161
+ node.content = [
1162
+ {type: "table_caption"},
1163
+ {
1164
+ type: "table_body",
1165
+ content: node.content
1166
+ }
1167
+ ]
1168
+ break
1169
+ case "table_cell":
1170
+ if (!node.content || !node.content.length) {
1171
+ node.content = [{type: "paragraph"}]
1172
+ }
1173
+ break
1174
+ case "table_header":
1175
+ if (!node.content || !node.content.length) {
1176
+ node.content = [{type: "paragraph"}]
1177
+ }
1178
+ break
1179
+ case "bullet_list":
1180
+ case "ordered_list":
1181
+ attrs = node.attrs || {}
1182
+ blockId = attrs.id
1183
+ while (!blockId || ids.includes(blockId)) {
1184
+ blockId = randomListId()
1185
+ }
1186
+ attrs.id = blockId
1187
+ node.attrs = attrs
1188
+ ids.push(blockId)
1189
+ break
1190
+ case "figure": {
1191
+ attrs = node.attrs || {}
1192
+ if (attrs.figureCategory) {
1193
+ attrs.category = attrs.figureCategory
1194
+ delete attrs.figureCategory
1195
+ }
1196
+ node.content = []
1197
+ if (attrs.image) {
1198
+ node.content.push({type: "image", attrs: {image: attrs.image}})
1199
+ } else {
1200
+ node.content.push({
1201
+ type: "figure_equation",
1202
+ attrs: {equation: attrs.equation || ""}
1203
+ })
1204
+ }
1205
+ delete attrs.image
1206
+ delete attrs.equation
1207
+
1208
+ const caption = {type: "figure_caption"}
1209
+ if (attrs.caption) {
1210
+ if (attrs.caption.length) {
1211
+ caption.content = [{type: "text", text: attrs.caption}]
1212
+ attrs.caption = true
1213
+ } else {
1214
+ attrs.caption = false
1215
+ }
1216
+ } else {
1217
+ attrs.caption = false
1218
+ }
1219
+ if (attrs.category === "table") {
1220
+ node.content.unshift(caption)
1221
+ } else {
1222
+ node.content.push(caption)
1223
+ }
1224
+ node.attrs = attrs
1225
+ break
1226
+ }
1227
+ case "footnote":
1228
+ if (node.attrs?.footnote) {
1229
+ node.attrs.footnote.forEach(childNode => {
1230
+ convertNodeV32(childNode, ids)
1231
+ })
1232
+ }
1233
+ break
1234
+ }
1235
+ if (node.content) {
1236
+ node.content.forEach(childNode => {
1237
+ convertNodeV32(childNode, ids)
1238
+ })
1239
+ }
1240
+ if (node.attrs?.initial) {
1241
+ node.attrs.initial.forEach(childNode => {
1242
+ convertNodeV32(childNode, ids)
1243
+ })
1244
+ }
1245
+ }
1246
+
1247
+ const convertDocV32 = doc => {
1248
+ const returnDoc = JSON.parse(JSON.stringify(doc))
1249
+ convertNodeV32(returnDoc.content)
1250
+ return returnDoc
1251
+ }
1252
+
1253
+ const convertDocV33 = doc => {
1254
+ // We just need to increase the version number so that documents cannot
1255
+ // be moved from a 3.10 to an 3.9 system, but 3.3 files should be readable
1256
+ // as 3.4 files.
1257
+ return JSON.parse(JSON.stringify(doc))
1258
+ }
1259
+
1260
+ const convertDocV34 = doc => {
1261
+ // The top node needs to be changed from "article" to "doc".
1262
+ const returnDoc = JSON.parse(JSON.stringify(doc))
1263
+ returnDoc.content.type = "doc"
1264
+ return returnDoc
1265
+ }
1266
+
1267
+ const convertDocV35 = doc => {
1268
+ // We just need to increase the version number so that documents cannot
1269
+ // be moved from a 4.1 to an 4.0 system, but 3.5 files should be readable
1270
+ // as 3.6 files.
1271
+ return JSON.parse(JSON.stringify(doc))
1272
+ }