bookish-press 0.4.0 → 0.4.16

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 (394) hide show
  1. package/README.md +3 -1
  2. package/components/Auth.svelte +19 -14
  3. package/components/Auth.svelte.d.ts +4 -4
  4. package/components/Authentication.d.ts +2 -2
  5. package/components/BookPreview.svelte +77 -59
  6. package/components/BookPreview.svelte.d.ts +5 -5
  7. package/components/External.svelte +1 -1
  8. package/components/External.svelte.d.ts +3 -3
  9. package/components/Link.svelte +49 -3
  10. package/components/Link.svelte.d.ts +3 -4
  11. package/components/app/BookList.svelte +19 -0
  12. package/components/app/BookList.svelte.d.ts +18 -0
  13. package/components/app/Button.svelte +49 -0
  14. package/components/app/Button.svelte.d.ts +22 -0
  15. package/components/app/Feedback.svelte +45 -0
  16. package/components/app/Feedback.svelte.d.ts +18 -0
  17. package/components/app/Head.svelte +12 -0
  18. package/components/app/Head.svelte.d.ts +27 -0
  19. package/components/app/Large.svelte +8 -0
  20. package/components/app/Large.svelte.d.ts +27 -0
  21. package/components/app/Lead.svelte +12 -0
  22. package/components/app/Lead.svelte.d.ts +27 -0
  23. package/components/app/Link.svelte +24 -0
  24. package/components/app/Link.svelte.d.ts +20 -0
  25. package/components/app/Options.svelte +24 -0
  26. package/components/app/Options.svelte.d.ts +19 -0
  27. package/components/app/Paragraph.svelte +19 -0
  28. package/components/app/Paragraph.svelte.d.ts +18 -0
  29. package/components/app/Table.svelte +32 -0
  30. package/components/app/Table.svelte.d.ts +27 -0
  31. package/components/app/TextInput.svelte +37 -0
  32. package/components/app/TextInput.svelte.d.ts +22 -0
  33. package/components/app/Writing.svelte +13 -0
  34. package/components/app/Writing.svelte.d.ts +27 -0
  35. package/components/chapter/Atom.svelte +20 -6
  36. package/components/chapter/Atom.svelte.d.ts +3 -3
  37. package/components/chapter/Block.svelte +23 -20
  38. package/components/chapter/Block.svelte.d.ts +4 -4
  39. package/components/chapter/Callout.svelte +38 -7
  40. package/components/chapter/Callout.svelte.d.ts +4 -4
  41. package/components/chapter/CaptionedCode.svelte +66 -23
  42. package/components/chapter/CaptionedCode.svelte.d.ts +4 -4
  43. package/components/chapter/ChapterBody.svelte +61 -9
  44. package/components/chapter/ChapterBody.svelte.d.ts +4 -4
  45. package/components/chapter/Citations.svelte +86 -20
  46. package/components/chapter/Citations.svelte.d.ts +4 -4
  47. package/components/chapter/Code.svelte +152 -21
  48. package/components/chapter/Code.svelte.d.ts +3 -3
  49. package/components/chapter/Comment.svelte +38 -16
  50. package/components/chapter/Comment.svelte.d.ts +3 -3
  51. package/components/chapter/Definition.svelte +52 -18
  52. package/components/chapter/Definition.svelte.d.ts +5 -8
  53. package/components/chapter/Embed.svelte +154 -66
  54. package/components/chapter/Embed.svelte.d.ts +5 -4
  55. package/components/chapter/ErrorMessage.svelte +7 -2
  56. package/components/chapter/ErrorMessage.svelte.d.ts +4 -4
  57. package/components/chapter/ExtendedPrism.d.ts +2 -0
  58. package/components/chapter/ExtendedPrism.js +69 -0
  59. package/components/chapter/Figure.svelte +90 -0
  60. package/components/chapter/Figure.svelte.d.ts +24 -0
  61. package/components/chapter/Footnote.svelte +75 -12
  62. package/components/chapter/Footnote.svelte.d.ts +4 -4
  63. package/components/chapter/Format.svelte +60 -37
  64. package/components/chapter/Format.svelte.d.ts +4 -4
  65. package/components/chapter/InlineCode.svelte +15 -5
  66. package/components/chapter/InlineCode.svelte.d.ts +4 -4
  67. package/components/chapter/Label.svelte +37 -8
  68. package/components/chapter/Label.svelte.d.ts +4 -4
  69. package/components/chapter/Link.svelte +23 -18
  70. package/components/chapter/Link.svelte.d.ts +4 -4
  71. package/components/chapter/List.svelte +75 -8
  72. package/components/chapter/List.svelte.d.ts +4 -4
  73. package/components/chapter/Marginal.svelte +169 -27
  74. package/components/chapter/Marginal.svelte.d.ts +6 -3
  75. package/components/chapter/Paragraph.svelte +74 -6
  76. package/components/chapter/Paragraph.svelte.d.ts +4 -4
  77. package/components/chapter/Positioned.svelte +33 -0
  78. package/components/chapter/Positioned.svelte.d.ts +19 -0
  79. package/components/chapter/Problem.svelte +15 -0
  80. package/components/chapter/Problem.svelte.d.ts +27 -0
  81. package/components/chapter/Python.svelte +68 -21
  82. package/components/chapter/Python.svelte.d.ts +4 -4
  83. package/components/chapter/Quote.svelte +64 -11
  84. package/components/chapter/Quote.svelte.d.ts +4 -4
  85. package/components/chapter/Rule.svelte +12 -1
  86. package/components/chapter/Rule.svelte.d.ts +4 -4
  87. package/components/chapter/Table.svelte +36 -25
  88. package/components/chapter/Table.svelte.d.ts +4 -4
  89. package/components/chapter/Text.svelte +61 -36
  90. package/components/chapter/Text.svelte.d.ts +5 -4
  91. package/components/editor/BookishEditor.svelte +343 -237
  92. package/components/editor/BookishEditor.svelte.d.ts +5 -5
  93. package/components/editor/CalloutEditor.svelte +5 -5
  94. package/components/editor/CalloutEditor.svelte.d.ts +4 -4
  95. package/components/editor/CaptionedCodeEditor.svelte +16 -12
  96. package/components/editor/CaptionedCodeEditor.svelte.d.ts +4 -4
  97. package/components/editor/CaretContext.d.ts +48 -16
  98. package/components/editor/CaretState.d.ts +16 -44
  99. package/components/editor/CaretUtilities.d.ts +2 -2
  100. package/components/editor/CaretView.svelte +80 -0
  101. package/components/editor/CaretView.svelte.d.ts +24 -0
  102. package/components/editor/CitationsEditor.svelte +36 -25
  103. package/components/editor/CitationsEditor.svelte.d.ts +4 -4
  104. package/components/editor/Clipboard.d.ts +2 -2
  105. package/components/editor/Command.d.ts +13 -12
  106. package/components/editor/Commands.d.ts +1 -1
  107. package/components/editor/Commands.js +1114 -571
  108. package/components/editor/ConfirmButton.svelte +18 -12
  109. package/components/editor/ConfirmButton.svelte.d.ts +5 -4
  110. package/components/editor/DefinitionEditor.svelte +29 -21
  111. package/components/editor/DefinitionEditor.svelte.d.ts +4 -4
  112. package/components/editor/EmbedEditor.svelte +93 -43
  113. package/components/editor/EmbedEditor.svelte.d.ts +3 -3
  114. package/components/editor/Icon.svelte +17 -0
  115. package/components/editor/Icon.svelte.d.ts +16 -0
  116. package/components/editor/ImageChooser.svelte +53 -34
  117. package/components/editor/ImageChooser.svelte.d.ts +3 -3
  118. package/components/editor/InlineCodeEditor.svelte +7 -4
  119. package/components/editor/InlineCodeEditor.svelte.d.ts +4 -4
  120. package/components/editor/LabelEditor.svelte +20 -11
  121. package/components/editor/LabelEditor.svelte.d.ts +4 -4
  122. package/components/editor/LanguageEditor.svelte +10 -7
  123. package/components/editor/LanguageEditor.svelte.d.ts +3 -3
  124. package/components/editor/LinkEditor.svelte +64 -39
  125. package/components/editor/LinkEditor.svelte.d.ts +4 -4
  126. package/components/editor/Note.svelte +3 -3
  127. package/components/editor/Note.svelte.d.ts +6 -2
  128. package/components/editor/PositionEditor.svelte +9 -6
  129. package/components/editor/PositionEditor.svelte.d.ts +4 -4
  130. package/components/editor/QuoteEditor.svelte +7 -2
  131. package/components/editor/QuoteEditor.svelte.d.ts +4 -4
  132. package/components/editor/Switch.svelte +55 -28
  133. package/components/editor/Switch.svelte.d.ts +4 -4
  134. package/components/editor/TableEditor.svelte +10 -0
  135. package/components/editor/TableEditor.svelte.d.ts +17 -0
  136. package/components/editor/TextEditor.svelte +83 -45
  137. package/components/editor/TextEditor.svelte.d.ts +5 -4
  138. package/components/editor/Toggle.svelte +11 -8
  139. package/components/editor/Toggle.svelte.d.ts +4 -4
  140. package/components/editor/Toolbar.svelte +192 -144
  141. package/components/editor/Toolbar.svelte.d.ts +5 -8
  142. package/components/editor/ToolbarGroup.svelte +18 -23
  143. package/components/editor/ToolbarGroup.svelte.d.ts +3 -4
  144. package/components/editor/ToolbarSpacer.svelte +8 -1
  145. package/components/editor/ToolbarSpacer.svelte.d.ts +6 -2
  146. package/components/editor/URLEditor.svelte +6 -8
  147. package/components/editor/URLEditor.svelte.d.ts +3 -3
  148. package/components/editor/UndoState.d.ts +3 -3
  149. package/components/editor/icons/annotate.svg +1 -0
  150. package/components/editor/icons/block.svg +1 -0
  151. package/components/editor/icons/bold.svg +1 -0
  152. package/components/editor/icons/bullets.svg +1 -0
  153. package/components/editor/icons/callout.svg +1 -0
  154. package/components/editor/icons/citation.svg +8 -0
  155. package/components/editor/icons/clear.svg +1 -0
  156. package/components/editor/icons/clipboard.svg +1 -0
  157. package/components/editor/icons/code.svg +1 -0
  158. package/components/editor/icons/columnafter.svg +1 -0
  159. package/components/editor/icons/columnbefore.svg +1 -0
  160. package/components/editor/icons/comment.svg +1 -0
  161. package/components/editor/icons/copy.svg +1 -0
  162. package/components/editor/icons/cut.svg +1 -0
  163. package/components/editor/icons/define.svg +7 -0
  164. package/components/editor/icons/deletecolumn.svg +1 -0
  165. package/components/editor/icons/deleterow.svg +1 -0
  166. package/components/editor/icons/footnote.svg +8 -0
  167. package/components/editor/icons/header1.svg +7 -0
  168. package/components/editor/icons/header2.svg +7 -0
  169. package/components/editor/icons/header3.svg +7 -0
  170. package/components/editor/icons/home.svg +1 -0
  171. package/components/editor/icons/indent.svg +1 -0
  172. package/components/editor/icons/italic.svg +1 -0
  173. package/components/editor/icons/label.svg +1 -0
  174. package/components/editor/icons/left.svg +1 -0
  175. package/components/editor/icons/level.svg +1 -0
  176. package/components/editor/icons/link.svg +1 -0
  177. package/components/editor/icons/list.svg +1 -0
  178. package/components/editor/icons/media.svg +1 -0
  179. package/components/editor/icons/middle.svg +1 -0
  180. package/components/editor/icons/numbers.svg +10 -0
  181. package/components/editor/icons/paragraph.svg +1 -0
  182. package/components/editor/icons/paste.svg +10 -0
  183. package/components/editor/icons/quote.svg +1 -0
  184. package/components/editor/icons/redo.svg +1 -0
  185. package/components/editor/icons/right.svg +1 -0
  186. package/components/editor/icons/rowabove.svg +1 -0
  187. package/components/editor/icons/rowbelow.svg +1 -0
  188. package/components/editor/icons/rule.svg +1 -0
  189. package/components/editor/icons/sub.svg +1 -0
  190. package/components/editor/icons/super.svg +1 -0
  191. package/components/editor/icons/table.svg +1 -0
  192. package/components/editor/icons/text.svg +1 -0
  193. package/components/editor/icons/time.svg +1 -0
  194. package/components/editor/icons/undo.svg +1 -0
  195. package/components/editor/icons/unindent.svg +1 -0
  196. package/components/editor/icons/unlink.svg +1 -0
  197. package/components/page/Acknowledgements.svelte +31 -22
  198. package/components/page/Acknowledgements.svelte.d.ts +4 -4
  199. package/components/page/Authors.svelte +34 -14
  200. package/components/page/Authors.svelte.d.ts +6 -6
  201. package/components/page/BulkReferenceEditor.svelte +53 -22
  202. package/components/page/BulkReferenceEditor.svelte.d.ts +4 -4
  203. package/components/page/Chapter.svelte +196 -141
  204. package/components/page/Chapter.svelte.d.ts +3 -3
  205. package/components/page/ChapterContext.d.ts +4 -4
  206. package/components/page/ChapterNumber.svelte +9 -0
  207. package/components/page/ChapterNumber.svelte.d.ts +27 -0
  208. package/components/page/ChapterTitle.svelte +16 -0
  209. package/components/page/ChapterTitle.svelte.d.ts +18 -0
  210. package/components/page/Contexts.d.ts +31 -20
  211. package/components/page/Contexts.js +47 -17
  212. package/components/page/DefinitionView.svelte +115 -86
  213. package/components/page/DefinitionView.svelte.d.ts +3 -3
  214. package/components/page/Description.svelte +27 -18
  215. package/components/page/Description.svelte.d.ts +4 -4
  216. package/components/page/Edition.svelte +127 -74
  217. package/components/page/Edition.svelte.d.ts +4 -4
  218. package/components/page/Editions.svelte +144 -0
  219. package/components/page/Editions.svelte.d.ts +14 -0
  220. package/components/page/Glossary.svelte +57 -48
  221. package/components/page/Glossary.svelte.d.ts +4 -4
  222. package/components/page/Header.svelte +129 -44
  223. package/components/page/Header.svelte.d.ts +6 -6
  224. package/components/page/Index.svelte +114 -80
  225. package/components/page/Index.svelte.d.ts +4 -4
  226. package/components/page/Instructions.svelte +18 -16
  227. package/components/page/Instructions.svelte.d.ts +4 -4
  228. package/components/page/License.svelte +29 -21
  229. package/components/page/License.svelte.d.ts +4 -4
  230. package/components/page/Loading.svelte +102 -12
  231. package/components/page/Loading.svelte.d.ts +6 -2
  232. package/components/page/Media.svelte +77 -51
  233. package/components/page/Media.svelte.d.ts +4 -4
  234. package/components/page/MediaPreview.svelte +26 -8
  235. package/components/page/MediaPreview.svelte.d.ts +3 -3
  236. package/components/page/Muted.svelte +10 -0
  237. package/components/page/Muted.svelte.d.ts +27 -0
  238. package/components/page/Outline.svelte +279 -59
  239. package/components/page/Outline.svelte.d.ts +3 -3
  240. package/components/page/Page.svelte +40 -9
  241. package/components/page/Page.svelte.d.ts +3 -3
  242. package/components/page/PageHeader.svelte +14 -0
  243. package/components/page/PageHeader.svelte.d.ts +18 -0
  244. package/components/page/PageParagraph.svelte +18 -0
  245. package/components/page/PageParagraph.svelte.d.ts +27 -0
  246. package/components/page/PossibleChapter.svelte +7 -7
  247. package/components/page/PossibleChapter.svelte.d.ts +4 -4
  248. package/components/page/PossibleReference.svelte +6 -11
  249. package/components/page/PossibleReference.svelte.d.ts +6 -7
  250. package/components/page/Print.svelte +27 -3
  251. package/components/page/Print.svelte.d.ts +4 -4
  252. package/components/page/Reference.svelte +256 -113
  253. package/components/page/Reference.svelte.d.ts +5 -5
  254. package/components/page/References.svelte +39 -47
  255. package/components/page/References.svelte.d.ts +4 -4
  256. package/components/page/Rows.svelte +54 -0
  257. package/components/page/Rows.svelte.d.ts +27 -0
  258. package/components/page/Search.svelte +86 -63
  259. package/components/page/Search.svelte.d.ts +4 -4
  260. package/components/page/Status.svelte +43 -35
  261. package/components/page/Status.svelte.d.ts +4 -9
  262. package/components/page/SubdomainEditor.svelte +40 -27
  263. package/components/page/SubdomainEditor.svelte.d.ts +4 -4
  264. package/components/page/TableOfContents.svelte +225 -190
  265. package/components/page/TableOfContents.svelte.d.ts +4 -4
  266. package/components/page/TableOfContentsImage.svelte +8 -8
  267. package/components/page/TableOfContentsImage.svelte.d.ts +3 -3
  268. package/components/page/TableOfContentsRow.svelte +96 -33
  269. package/components/page/TableOfContentsRow.svelte.d.ts +4 -4
  270. package/components/page/ThemeEditor.svelte +174 -39
  271. package/components/page/ThemeEditor.svelte.d.ts +4 -4
  272. package/components/page/ThemeEditorPreview.svelte +35 -23
  273. package/components/page/ThemeEditorPreview.svelte.d.ts +5 -5
  274. package/components/page/ThemeSetEditor.svelte +15 -13
  275. package/components/page/ThemeSetEditor.svelte.d.ts +4 -4
  276. package/components/page/ThemeVariableEditor.svelte +24 -15
  277. package/components/page/ThemeVariableEditor.svelte.d.ts +3 -3
  278. package/components/page/Title.svelte +12 -0
  279. package/components/page/Title.svelte.d.ts +27 -0
  280. package/components/page/Unknown.svelte +25 -21
  281. package/components/page/Unknown.svelte.d.ts +4 -4
  282. package/components/page/margins.js +50 -33
  283. package/index.d.ts +14 -0
  284. package/index.js +14 -0
  285. package/models/CRUD.d.ts +17 -0
  286. package/models/CRUD.js +194 -0
  287. package/models/Firebase.js +17 -15
  288. package/models/book/Book.d.ts +24 -28
  289. package/models/book/Book.js +120 -113
  290. package/models/book/BookMedia.d.ts +3 -3
  291. package/models/book/BookMedia.js +22 -16
  292. package/models/book/BookSaveStatus.d.ts +7 -0
  293. package/models/book/BookSaveStatus.js +8 -0
  294. package/models/book/Chapter.d.ts +46 -39
  295. package/models/book/Chapter.js +180 -198
  296. package/models/book/ChapterID.js +7 -7
  297. package/models/book/Definition.d.ts +1 -1
  298. package/models/book/Edition.d.ts +62 -60
  299. package/models/book/Edition.js +385 -303
  300. package/models/book/Reference.d.ts +24 -0
  301. package/models/book/Reference.js +78 -0
  302. package/models/book/Schema.d.ts +169 -0
  303. package/models/book/Schema.js +114 -0
  304. package/models/book/Theme.d.ts +44 -37
  305. package/models/book/Theme.js +452 -52
  306. package/models/book/ThemeColors.d.ts +1 -1
  307. package/models/book/Words.d.ts +2 -0
  308. package/models/book/Words.js +110 -0
  309. package/models/chapter/AtomNode.d.ts +4 -4
  310. package/models/chapter/AtomNode.js +20 -13
  311. package/models/chapter/BlockNode.d.ts +9 -10
  312. package/models/chapter/BlockNode.js +32 -13
  313. package/models/chapter/BlocksNode.d.ts +10 -10
  314. package/models/chapter/BlocksNode.js +315 -120
  315. package/models/chapter/BlocksNode.test.js +494 -228
  316. package/models/chapter/Bookkeeping.d.ts +1 -1
  317. package/models/chapter/CalloutNode.d.ts +3 -3
  318. package/models/chapter/CalloutNode.js +34 -12
  319. package/models/chapter/Caret.d.ts +9 -7
  320. package/models/chapter/Caret.js +11 -6
  321. package/models/chapter/ChapterNode.d.ts +11 -11
  322. package/models/chapter/ChapterNode.js +70 -33
  323. package/models/chapter/CitationsNode.d.ts +3 -3
  324. package/models/chapter/CitationsNode.js +31 -12
  325. package/models/chapter/CodeNode.d.ts +11 -6
  326. package/models/chapter/CodeNode.js +143 -34
  327. package/models/chapter/CommentNode.d.ts +5 -5
  328. package/models/chapter/CommentNode.js +29 -13
  329. package/models/chapter/DefinitionNode.d.ts +2 -2
  330. package/models/chapter/DefinitionNode.js +15 -7
  331. package/models/chapter/Edit.d.ts +3 -3
  332. package/models/chapter/EmbedNode.d.ts +9 -5
  333. package/models/chapter/EmbedNode.js +108 -35
  334. package/models/chapter/ErrorNode.d.ts +2 -2
  335. package/models/chapter/ErrorNode.js +34 -12
  336. package/models/chapter/FootnoteNode.d.ts +5 -5
  337. package/models/chapter/FootnoteNode.js +34 -16
  338. package/models/chapter/FormatNode.d.ts +10 -10
  339. package/models/chapter/FormatNode.js +237 -106
  340. package/models/chapter/FormatNode.test.js +87 -45
  341. package/models/chapter/InlineCodeNode.d.ts +2 -2
  342. package/models/chapter/InlineCodeNode.js +22 -9
  343. package/models/chapter/LabelNode.d.ts +4 -4
  344. package/models/chapter/LabelNode.js +31 -13
  345. package/models/chapter/LinkNode.d.ts +2 -2
  346. package/models/chapter/LinkNode.js +21 -9
  347. package/models/chapter/ListNode.d.ts +8 -8
  348. package/models/chapter/ListNode.js +99 -38
  349. package/models/chapter/ListNode.test.js +29 -24
  350. package/models/chapter/MetadataNode.d.ts +3 -3
  351. package/models/chapter/MetadataNode.js +13 -5
  352. package/models/chapter/Node.d.ts +5 -3
  353. package/models/chapter/Node.js +76 -29
  354. package/models/chapter/Node.test.js +20 -18
  355. package/models/chapter/ParagraphNode.d.ts +6 -6
  356. package/models/chapter/ParagraphNode.js +84 -29
  357. package/models/chapter/ParagraphNode.test.js +22 -13
  358. package/models/chapter/Parser.d.ts +25 -25
  359. package/models/chapter/Parser.js +201 -189
  360. package/models/chapter/Parser.test.js +16 -16
  361. package/models/chapter/Position.d.ts +1 -1
  362. package/models/chapter/QuoteNode.d.ts +6 -6
  363. package/models/chapter/QuoteNode.js +61 -23
  364. package/models/chapter/RootNode.d.ts +4 -4
  365. package/models/chapter/RuleNode.d.ts +2 -2
  366. package/models/chapter/RuleNode.js +31 -11
  367. package/models/chapter/TableNode.d.ts +8 -6
  368. package/models/chapter/TableNode.js +89 -34
  369. package/models/chapter/TableNode.test.js +22 -24
  370. package/models/chapter/TextNode.d.ts +6 -6
  371. package/models/chapter/TextNode.js +72 -32
  372. package/models/chapter/TextNode.test.js +4 -4
  373. package/models/loadEdition.d.ts +1 -1
  374. package/models/loadEdition.js +10 -6
  375. package/package.json +104 -25
  376. package/util/getSubdomain.js +8 -6
  377. package/util/isMobile.js +1 -1
  378. package/util/mineReference.d.ts +3 -2
  379. package/util/mineReference.js +67 -29
  380. package/util/scrollToEyeLevel.js +6 -1
  381. package/components/chapter/renderPosition.d.ts +0 -3
  382. package/components/chapter/renderPosition.js +0 -4
  383. package/components/editor/ToolbarIcon.svelte +0 -22
  384. package/components/editor/ToolbarIcon.svelte.d.ts +0 -16
  385. package/components/page/Alert.svelte +0 -11
  386. package/components/page/Alert.svelte.d.ts +0 -23
  387. package/components/page/Revisions.svelte +0 -124
  388. package/components/page/Revisions.svelte.d.ts +0 -14
  389. package/models/BookLoader.d.ts +0 -2
  390. package/models/BookLoader.js +0 -67
  391. package/models/Firestore.d.ts +0 -22
  392. package/models/Firestore.js +0 -194
  393. package/models/chapter/ReferenceNode.d.ts +0 -29
  394. package/models/chapter/ReferenceNode.js +0 -48
@@ -1,314 +1,353 @@
1
1
  import Chapter from './Chapter';
2
- import Parser from "../chapter/Parser";
3
- import EmbedNode from "../chapter/EmbedNode";
4
- import { addChapterInFirestore, removeChapterFromEditionInFirestore, updateEditionInFirestore } from "../Firestore";
2
+ import Parser from '../chapter/Parser';
3
+ import EmbedNode from '../chapter/EmbedNode';
4
+ import Reference from './Reference';
5
5
  import ChapterIDs from './ChapterID.js';
6
- export var BookSaveStatus;
7
- (function (BookSaveStatus) {
8
- BookSaveStatus[BookSaveStatus["Changed"] = 0] = "Changed";
9
- BookSaveStatus[BookSaveStatus["Saving"] = 1] = "Saving";
10
- BookSaveStatus[BookSaveStatus["Saved"] = 2] = "Saved";
11
- BookSaveStatus[BookSaveStatus["Error"] = 3] = "Error";
12
- })(BookSaveStatus || (BookSaveStatus = {}));
13
6
  export default class Edition {
14
- specification;
15
- book;
7
+ bookRef;
16
8
  editionRef;
9
+ uids;
10
+ title;
11
+ authors;
12
+ images;
13
+ description;
17
14
  chapters;
18
- chaptersByID;
19
- listeners;
20
- // A list of requested edits, stored as promises, resolved
21
- // when the book is ready to save.
22
- edits = [];
23
- lastEdit = 0;
15
+ license;
16
+ acknowledgements;
17
+ tags;
18
+ sources;
19
+ references;
20
+ symbols;
21
+ glossary;
22
+ theme;
24
23
  // Given an object with a valid specification and an object mapping chapter IDs to chapter text,
25
24
  // construct an object representing a book.
26
- constructor(book, editionRef, specification) {
27
- if (typeof specification !== "object" && specification !== undefined)
28
- throw Error("Expected a book specification object, but received " + specification);
29
- this.book = book;
25
+ constructor(bookRef, editionRef, uids, title, authors, images, description, chapters, license, acknowledgements, tags, sources, references, symbols, glossary, theme) {
26
+ this.bookRef = bookRef;
30
27
  this.editionRef = editionRef;
31
- // Copy all of the specification metadata to fields.
32
- // Choose suitable defaults if the spec is lacking a field.
33
- this.specification = {
34
- title: specification ? specification.title : "Untitled",
35
- symbols: specification && specification.symbols ? specification.symbols : {},
36
- tags: specification && specification.tags ? specification.tags : [],
37
- license: specification ? specification.license : "All rights reserved.",
38
- references: specification && specification.references ? specification.references : {},
39
- glossary: specification && specification.glossary ? specification.glossary : {},
40
- authors: specification && specification.authors ? specification.authors : [],
41
- description: specification ? specification.description : "",
42
- acknowledgements: specification && specification.acknowledgements ? specification.acknowledgements : "",
43
- revisions: specification && specification.revisions ? specification.revisions : [],
44
- images: specification && specification.revisions ? specification.images : {},
45
- sources: specification && specification.revisions ? specification.sources : {},
46
- theme: specification && specification.theme ? specification.theme : null,
47
- uids: specification && specification.uids ? specification.uids : [],
48
- chapters: specification && specification.chapters ? specification.chapters : []
49
- };
50
- // No listeners yet
51
- this.listeners = new Set();
52
- // Create a list and dictionary of Chapter objects.
53
- this.chapters = [];
54
- this.chaptersByID = {};
55
- // If there's a spec and it has chapters, process them.
56
- if (this.specification.chapters.length > 0) {
57
- // Initialize the chapters dictionary since parsing depends this index to detect whether a chapter exists.
58
- this.specification.chapters.forEach(chapterSpec => this.chaptersByID[chapterSpec.id] = undefined);
59
- this.specification.chapters.forEach(chapterSpec => {
60
- const chap = new Chapter(this, chapterSpec);
61
- this.chaptersByID[chapterSpec.id] = chap;
62
- this.chapters.push(chap);
63
- });
28
+ this.uids = uids;
29
+ this.title = title;
30
+ this.authors = authors;
31
+ this.images = images;
32
+ this.description = description;
33
+ this.chapters = chapters.slice();
34
+ this.license = license;
35
+ this.acknowledgements = acknowledgements;
36
+ this.tags = tags.slice();
37
+ this.sources = Object.assign({}, sources);
38
+ this.references = {};
39
+ for (const [citationID, ref] of Object.entries(references)) {
40
+ this.references[citationID] =
41
+ ref instanceof Reference
42
+ ? ref
43
+ : Edition.parseReference(citationID, ref, this);
64
44
  }
45
+ this.symbols = Object.assign({}, symbols);
46
+ this.glossary = Object.assign({}, glossary);
47
+ this.theme = theme;
65
48
  }
66
- setRef(ref) {
67
- if (this.editionRef)
68
- throw Error("Can only set a ref of an edition when not yet set.");
69
- this.editionRef = ref;
70
- }
71
- async saveEdits() {
72
- // If it's been more than a second since our last edit and there
73
- // are edits that haven't been saved, try updating the book,
74
- // and if we succeed, resolve all of the edits, and if we fail,
75
- // then reject them.
76
- if (Date.now() - this.lastEdit > 1000 && this.edits.length > 0) {
77
- // Tell listeners that this book model changed.
78
- this.notifyListeners(BookSaveStatus.Saving);
79
- return updateEditionInFirestore(this)
80
- .then(() => {
81
- // Approve the edits.
82
- this.edits.forEach(edit => edit.resolve());
83
- this.notifyListeners(BookSaveStatus.Saved);
84
- })
85
- .catch(() => {
86
- // Reject the edits.
87
- this.edits.forEach(edit => edit.reject());
88
- this.notifyListeners(BookSaveStatus.Error);
89
- })
90
- .finally(() => {
91
- // Reset the edit queue.
92
- this.edits = [];
93
- });
49
+ static async fromJSON(editionRef, spec) {
50
+ // Copy all of the specification metadata to fields.
51
+ // Choose suitable defaults if the spec is lacking a field.
52
+ return new Edition(spec.bookRef, editionRef, spec.uids, spec.title, spec.authors, spec.images, spec.description, spec.chapters.map((chap) => Chapter.fromJSON(chap, '')), spec.license, spec.acknowledgements, spec.tags, spec.sources, spec.references, spec.symbols, spec.glossary, spec.theme);
53
+ }
54
+ static parseReference(citationID, ref, edition, short = false) {
55
+ if (typeof ref === 'string')
56
+ return Parser.parseFormat(edition, ref);
57
+ else {
58
+ // APA Format. Could eventually support multiple formats.
59
+ let [authors, year, title, source, url, summary] = ref;
60
+ if (source.charAt(0) === '#') {
61
+ let src = edition.getSource(source);
62
+ if (src !== null)
63
+ source = src;
64
+ }
65
+ return new Reference(citationID, authors ?? '', year ?? '', title ?? '', source ?? '', url ?? '', summary ?? '', short);
94
66
  }
95
67
  }
96
- getBook() { return this.book; }
97
- addListener(listener) { this.listeners.add(listener); }
98
- removeListener(listener) { this.listeners.delete(listener); }
99
- notifyListeners(status) { this.listeners.forEach(listener => listener.call(undefined, status)); }
100
- // Adds a save request to the queue, to be resolved later after a period of
101
- // inactivity. Returns a promise that will eventually be resolved.
102
- requestSave() {
103
- // Tell listeners that this book model changed.
104
- this.notifyListeners(BookSaveStatus.Changed);
105
- // Return a promise that will resolve or reject later after this model saves the edits to the database.
106
- this.lastEdit = Date.now();
107
- const promise = new Promise((resolve, reject) => {
108
- this.edits.push({ resolve: resolve, reject: reject });
109
- });
110
- return promise;
111
- }
112
68
  // Convert the book to an object for storage.
113
69
  toObject() {
114
- // Deep copy, then let the chapter models handle the chapters.
115
- const spec = JSON.parse(JSON.stringify(this.specification));
116
- spec.chapters = this.chapters.map(chapter => chapter.toObject());
117
- return spec;
118
- }
119
- getRef() { return this.editionRef; }
120
- addUserID(uid) {
121
- this.specification.uids.push(uid);
122
- }
123
- getTitle() { return this.specification.title; }
124
- setTitle(title) {
125
- // Update locally, then update on the server.
126
- this.specification.title = title;
127
- return this.requestSave();
128
- }
129
- getDescription() { return this.specification.description; }
130
- setDescription(text) {
131
- // Update locally, then update on the server.
132
- this.specification.description = text;
133
- return this.requestSave();
134
- }
135
- getAcknowledgements() { return this.specification.acknowledgements; }
136
- setAcknowledgements(text) {
137
- // Update locally, then update on the server.
138
- this.specification.acknowledgements = text;
139
- return this.requestSave();
140
- }
141
- getLicense() { return this.specification.license; }
142
- setLicense(text) {
143
- // Update locally, then update on the server.
144
- this.specification.license = text;
145
- return this.requestSave();
146
- }
147
- getChapters() { return this.chapters; }
148
- hasChapter(chapterID) { return chapterID in this.chaptersByID || ["references", "glossary", "index", "search", "media"].includes(chapterID); }
149
- getChapter(chapterID) { return this.hasChapter(chapterID) ? this.chaptersByID[chapterID] : undefined; }
70
+ const references = {};
71
+ for (const [citationID, ref] of Object.entries(this.references))
72
+ references[citationID] =
73
+ ref instanceof Reference ? ref.toList() : ref.toBookdown();
74
+ const editionJSON = {
75
+ title: this.title,
76
+ authors: this.authors,
77
+ images: Object.assign({}, this.images),
78
+ description: this.description,
79
+ chapters: this.chapters.map((chap) => chap.toJSON()),
80
+ license: this.license,
81
+ acknowledgements: this.acknowledgements,
82
+ tags: this.tags.slice(),
83
+ sources: Object.assign({}, this.sources),
84
+ references: references,
85
+ symbols: Object.assign({}, this.symbols),
86
+ glossary: Object.assign({}, this.glossary),
87
+ theme: this.theme,
88
+ uids: this.uids.slice(),
89
+ };
90
+ if (this.bookRef)
91
+ editionJSON.bookRef = this.bookRef;
92
+ return editionJSON;
93
+ }
94
+ getRef() {
95
+ return this.editionRef;
96
+ }
97
+ withRef(editionRef) {
98
+ return new Edition(this.bookRef, editionRef, this.uids, this.title, this.authors, this.images, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
99
+ }
100
+ getTitle() {
101
+ return this.title;
102
+ }
103
+ withTitle(title) {
104
+ return new Edition(this.bookRef, this.editionRef, this.uids, title, this.authors, this.images, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
105
+ }
106
+ getDescription() {
107
+ return this.description;
108
+ }
109
+ setDescription(description) {
110
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
111
+ }
112
+ getAcknowledgements() {
113
+ return this.acknowledgements;
114
+ }
115
+ setAcknowledgements(acks) {
116
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, this.chapters, this.license, acks, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
117
+ }
118
+ getLicense() {
119
+ return this.license;
120
+ }
121
+ setLicense(license) {
122
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, this.chapters, license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
123
+ }
124
+ getChapters() {
125
+ return this.chapters.slice();
126
+ }
127
+ hasChapter(chapterID) {
128
+ return (this.getChapter(chapterID) !== undefined ||
129
+ ['references', 'glossary', 'index', 'search', 'media'].includes(chapterID));
130
+ }
131
+ getChapter(chapterID) {
132
+ return this.chapters.find((chapter) => chapter.getID() === chapterID);
133
+ }
134
+ getChapterByRef(ref) {
135
+ return this.chapters.find((chap) => chap.ref !== undefined && chap.ref.id === ref.id);
136
+ }
150
137
  getChapterPosition(chapterID) {
151
138
  var position = 0;
152
139
  for (; position < this.chapters.length; position++)
153
- if (this.chapters[position].getChapterID() === chapterID)
140
+ if (this.chapters[position].getID() === chapterID)
154
141
  return position;
155
142
  return undefined;
156
143
  }
157
- getChapterCount() { return this.chapters.length; }
158
- async addChapter() {
159
- if (!this.editionRef)
160
- throw Error("No edition ID, can't add chapter");
161
- const editionID = this.editionRef;
144
+ getChapterCount() {
145
+ return this.chapters.length;
146
+ }
147
+ withChapters(chapters) {
148
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
149
+ }
150
+ withRevisedChapter(previous, edited) {
151
+ const index = this.chapters.indexOf(previous);
152
+ if (index < 0)
153
+ return this;
154
+ return this.withChapters([
155
+ ...this.chapters.slice(0, index),
156
+ edited,
157
+ ...this.chapters.slice(index + 1),
158
+ ]);
159
+ }
160
+ withoutRefs() {
161
+ return this.withRef(undefined).withChapters(this.chapters.map((chap) => chap.withoutRef()));
162
+ }
163
+ withNewChapter() {
162
164
  // Synthesize a chapter ID placeholder that doesn't overlap with existing chapter names
163
165
  let number = 1;
164
- while (this.hasChapter("chapter" + number))
166
+ while (this.hasChapter('chapter' + number))
165
167
  number++;
166
- const chapterRef = await addChapterInFirestore(editionID, { text: "" });
167
168
  // Create a default chapter on this model. Remember the document reference so we can modify it later.
168
- const emptyChapter = {
169
- ref: chapterRef,
170
- id: `chapter${number}`,
171
- title: "Untitled Chapter",
172
- authors: [],
173
- numbered: true,
174
- forthcoming: true,
175
- image: null
176
- };
177
- const chap = new Chapter(this, emptyChapter);
178
- this.chapters.push(chap);
179
- this.chaptersByID[emptyChapter.id] = chap;
180
- // Ask the database to create the chapter, returning the promise
181
- this.requestSave();
169
+ const newChapter = new Chapter(undefined, `chapter${number}`, 'Untitled Chapter', [], null, true, false, undefined, '');
170
+ return this.withChapters([...this.chapters, newChapter]);
182
171
  }
183
- moveChapter(chapterID, increment) {
184
- // Get the index of the chapter.
185
- let index = this.chapters.findIndex(chapter => chapter.getChapterID() === chapterID);
186
- if (index < 0)
187
- throw Error(`Chapter with ID ${chapterID} doesn't exist, can't move it.`);
188
- else if (index + increment < 0)
189
- throw Error(`Can't move this chapter ${increment} chapters earlier.`);
190
- else if (index + increment >= this.chapters.length)
191
- throw Error(`Can't move this chapter ${increment} chapters later.`);
192
- const temp = this.chapters[index + increment];
193
- this.chapters[index + increment] = this.chapters[index];
194
- this.chapters[index] = temp;
195
- // Ask the database to update with this new order.
196
- return this.requestSave();
197
- }
198
- async deleteChapter(chapterID) {
199
- let index = this.chapters.findIndex(chapter => chapter.getChapterID() === chapterID);
172
+ withoutChapter(chapterID) {
173
+ let index = this.chapters.findIndex((chapter) => chapter.getID() === chapterID);
200
174
  if (index < 0)
201
- throw Error(`Chapter with ID ${chapterID} doesn't exist, can't delete it.`);
202
- const chapter = this.chapters[index];
203
- this.chapters.splice(index, 1);
204
- // Ask the database to update the new book metadata then delete the chapter.
205
- return this.requestSave().then(() => removeChapterFromEditionInFirestore(chapter));
206
- }
207
- getSymbols() { return this.specification.symbols; }
208
- hasReferences() { return this.specification.references && Object.keys(this.specification.references).length > 0; }
209
- getReferences() { return this.specification.references; }
210
- getReference(citationID) { return citationID in this.specification.references ? this.specification.references[citationID] : undefined; }
211
- addReferences(references) {
212
- // Generate a unique ID for the reference.
213
- references.forEach(ref => this.specification.references[ref.citationID] = ref.toList());
214
- return this.requestSave();
175
+ return this;
176
+ return this.withChapters([
177
+ ...this.chapters.slice(0, index),
178
+ ...this.chapters.slice(index + 1),
179
+ ]);
215
180
  }
216
- editReference(ref) {
217
- if (!(ref.citationID in this.specification.references))
181
+ withMovedChapter(chapterID, increment) {
182
+ // Get the index of the chapter.
183
+ let index = this.chapters.findIndex((chapter) => chapter.getID() === chapterID);
184
+ if (index + increment < 0 || index + increment >= this.chapters.length)
185
+ return this;
186
+ const newChapters = this.chapters.slice();
187
+ const temp = newChapters[index + increment];
188
+ newChapters[index + increment] = newChapters[index];
189
+ newChapters[index] = temp;
190
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, newChapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
191
+ }
192
+ getSymbols() {
193
+ return this.symbols;
194
+ }
195
+ hasReferences() {
196
+ return Object.keys(this.references).length > 0;
197
+ }
198
+ getReferences() {
199
+ return Object.assign({}, this.references);
200
+ }
201
+ getReference(citationID) {
202
+ return citationID in this.references
203
+ ? this.references[citationID]
204
+ : undefined;
205
+ }
206
+ withReferences(newReferences) {
207
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, newReferences, this.symbols, this.glossary, this.theme);
208
+ }
209
+ withNewReferences(references) {
210
+ const newReferences = Object.assign({}, this.references);
211
+ // Generate a unique ID for the reference.
212
+ for (const newRef of references)
213
+ newReferences[newRef.citationID] = newRef;
214
+ return this.withReferences(newReferences);
215
+ }
216
+ withEditedReference(ref) {
217
+ if (!(ref.citationID in this.references))
218
+ return this;
219
+ const newReferences = Object.assign({}, this.references);
220
+ newReferences[ref.citationID] = ref;
221
+ return this.withReferences(newReferences);
222
+ }
223
+ withEditedReferenceID(newCitationID, ref) {
224
+ if (!(ref.citationID in this.references))
218
225
  return;
219
- this.specification.references[ref.citationID] = ref.toList();
220
- return this.requestSave();
226
+ const newReferences = Object.assign({}, this.references);
227
+ delete newReferences[ref.citationID];
228
+ newReferences[newCitationID] = ref.withCitationID(newCitationID);
229
+ return this.withReferences(newReferences);
221
230
  }
222
- editReferenceID(newCitationID, ref) {
223
- if (!(ref.citationID in this.specification.references))
224
- return;
225
- delete this.specification.references[ref.citationID];
226
- this.specification.references[newCitationID] = ref.withCitationID(newCitationID).toList();
227
- return this.requestSave();
231
+ withoutReference(citationID) {
232
+ if (!(citationID in this.references))
233
+ return this;
234
+ const newReferences = Object.assign({}, this.references);
235
+ delete newReferences[citationID];
236
+ return this.withReferences(newReferences);
228
237
  }
229
- removeReference(citationID) {
230
- if (!(citationID in this.specification.references))
231
- return;
232
- delete this.specification.references[citationID];
233
- return this.requestSave();
234
- }
235
- hasGlossary() { return this.specification.glossary && Object.keys(this.specification.glossary).length > 0; }
236
- getGlossary() { return this.specification.glossary; }
237
- hasDefinition(id) { return id in this.specification.glossary; }
238
- addDefinition(id, phrase, definition, synonyms) {
239
- this.specification.glossary[id] = {
238
+ hasGlossary() {
239
+ return Object.keys(this.glossary).length > 0;
240
+ }
241
+ getGlossary() {
242
+ return Object.assign({}, this.glossary);
243
+ }
244
+ hasDefinition(id) {
245
+ return id in this.glossary;
246
+ }
247
+ withGlossary(glossary) {
248
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, glossary, this.theme);
249
+ }
250
+ withDefinition(id, phrase, definition, synonyms) {
251
+ const newGlossary = Object.assign({}, this.glossary);
252
+ newGlossary[id] = {
240
253
  phrase: phrase,
241
254
  definition: definition,
242
- synonyms: synonyms
255
+ synonyms: synonyms,
243
256
  };
244
- return this.requestSave();
257
+ return this.withGlossary(newGlossary);
245
258
  }
246
- removeDefinition(id) {
247
- delete this.specification.glossary[id];
248
- return this.requestSave();
259
+ withoutDefinition(id) {
260
+ const newGlossary = Object.assign({}, this.glossary);
261
+ delete newGlossary[id];
262
+ return this.withGlossary(newGlossary);
249
263
  }
250
- editDefinition(id, definition) {
251
- this.specification.glossary[id] = definition;
252
- return this.requestSave();
264
+ withEditedDefinition(id, definition) {
265
+ const newGlossary = Object.assign({}, this.glossary);
266
+ newGlossary[id] = definition;
267
+ return this.withGlossary(newGlossary);
253
268
  }
254
- editDefinitionID(id, newID) {
255
- if (!(id in this.specification.glossary))
269
+ withEditedDefinitionID(id, newID) {
270
+ if (!(id in this.glossary))
256
271
  return;
257
- const definition = this.specification.glossary[id];
258
- delete this.specification.glossary[id];
259
- this.specification.glossary[newID] = definition;
260
- return this.requestSave();
261
- }
262
- getTags() { return this.specification.tags; }
263
- getTheme() { return this.specification.theme; }
264
- setTheme(theme) {
265
- this.specification.theme = theme;
266
- return this.requestSave();
267
- }
268
- setThemeValue(group, name, value) {
269
- if (this.specification.theme[group][name] === value)
270
- return;
271
- this.specification.theme[group][name] = value;
272
- return this.requestSave();
272
+ const newGlossary = Object.assign({}, this.glossary);
273
+ const definition = newGlossary[id];
274
+ delete newGlossary[id];
275
+ newGlossary[newID] = definition;
276
+ return this.withGlossary(newGlossary);
277
+ }
278
+ getTags() {
279
+ return this.tags;
280
+ }
281
+ getTheme() {
282
+ return this.theme;
283
+ }
284
+ withTheme(theme) {
285
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, this.images, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, theme);
286
+ }
287
+ withThemeValue(group, name, value) {
288
+ if (this.theme === null)
289
+ return this;
290
+ if (group === 'imports')
291
+ return this;
292
+ const newTheme = Object.assign({}, this.theme);
293
+ const newGroup = Object.assign({}, this.theme[group]);
294
+ newGroup[name] = value;
295
+ newTheme[group] = newGroup;
296
+ return this.withTheme(newTheme);
273
297
  }
274
298
  // Don't let callers get their sneaky hands on this mutable array...
275
- getAuthors() { return [...this.specification.authors]; }
276
- addAuthor(name) {
277
- this.specification.authors.push(name);
278
- return this.requestSave();
279
- }
280
- setAuthor(index, name) {
281
- if (index >= 0 && index < this.specification.authors.length)
282
- this.specification.authors[index] = name;
283
- return this.requestSave();
284
- }
285
- removeAuthor(index) {
286
- if (index >= 0 && index < this.specification.authors.length)
287
- this.specification.authors.splice(index, 1);
288
- return this.requestSave();
289
- }
290
- getRevisions() { return [...this.specification.revisions]; }
291
- hasImage(id) { return id in this.specification.images && this.specification.images[id] !== null; }
292
- getImage(id) { return this.specification.images[id] ?? null; }
293
- setImage(id, embed) {
294
- if (this.specification.images[id] === embed)
295
- return;
296
- this.specification.images[id] = embed ?? null;
297
- return this.requestSave();
299
+ getAuthors() {
300
+ return [...this.authors];
301
+ }
302
+ withAuthors(authors) {
303
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, authors, this.images, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
304
+ }
305
+ withAuthor(name) {
306
+ return this.withAuthors([...this.authors, name]);
307
+ }
308
+ withAuthorName(index, name) {
309
+ if (index < 0 || index >= this.authors.length)
310
+ return this;
311
+ return this.withAuthors([
312
+ ...this.authors.slice(0, index),
313
+ name,
314
+ ...this.authors.slice(index + 1),
315
+ ]);
316
+ }
317
+ withoutAuthor(index) {
318
+ if (index < 0 || index >= this.authors.length)
319
+ return this;
320
+ return this.withAuthors([
321
+ ...this.authors.slice(0, index),
322
+ ...this.authors.slice(index + 1),
323
+ ]);
324
+ }
325
+ hasImage(id) {
326
+ return this.images[id] !== null;
327
+ }
328
+ getImage(id) {
329
+ return this.images[id] ?? null;
330
+ }
331
+ withImage(id, embed) {
332
+ const newImages = Object.assign({}, this.images);
333
+ newImages[id] = embed;
334
+ return new Edition(this.bookRef, this.editionRef, this.uids, this.title, this.authors, newImages, this.description, this.chapters, this.license, this.acknowledgements, this.tags, this.sources, this.references, this.symbols, this.glossary, this.theme);
298
335
  }
299
336
  getBookReadingTime() {
300
337
  return this.chapters
301
- .map(chapter => chapter.getReadingTime())
302
- .reduce((total, time) => total === undefined ? 0 :
303
- time === undefined ?
304
- total : total + time, 0);
338
+ .map((chapter) => chapter.getReadingTime(this))
339
+ .reduce((total, time) => total === undefined
340
+ ? 0
341
+ : time === undefined
342
+ ? total
343
+ : total + time, 0);
305
344
  }
306
345
  getChapterNumber(chapterID) {
307
346
  let chapterNumber = 1;
308
347
  let match = null;
309
- this.chapters.forEach(chapter => {
348
+ this.chapters.forEach((chapter) => {
310
349
  // If we found a match...
311
- if (chapter.getChapterID() === chapterID) {
350
+ if (chapter.getID() === chapterID) {
312
351
  match = chapterNumber;
313
352
  // And it's an unnumbered chapter, set to null.
314
353
  if (!chapter.isNumbered())
@@ -325,37 +364,39 @@ export default class Edition {
325
364
  return undefined;
326
365
  }
327
366
  getSource(sourceID) {
328
- return sourceID.charAt(0) === "#" && sourceID.substring(1) in this.specification.sources ?
329
- this.specification.sources[sourceID.substring(1)] :
330
- null;
367
+ return sourceID.charAt(0) === '#' &&
368
+ sourceID.substring(1) in this.sources
369
+ ? this.sources[sourceID.substring(1)]
370
+ : null;
331
371
  }
332
372
  getChapterName(chapterID) {
333
373
  const chapter = this.getChapter(chapterID);
334
374
  return chapter?.getTitle();
335
375
  }
336
376
  getChapterSection(chapterID) {
337
- const chapter = this.chaptersByID[chapterID];
377
+ const chapter = this.getChapter(chapterID);
338
378
  return chapter?.getSection();
339
379
  }
340
380
  getFootnoteSymbol(number) {
341
- let symbols = "abcdefghijklmnopqrstuvwxyz";
381
+ let symbols = 'abcdefghijklmnopqrstuvwxyz';
342
382
  // Let's hope there are never more than 26^2 footnotes in a single chapter...
343
- return number < symbols.length ?
344
- symbols.charAt(number) :
345
- symbols.charAt(Math.floor(number / symbols.length) - 1) + symbols.charAt(number % symbols.length);
383
+ return number < symbols.length
384
+ ? symbols.charAt(number)
385
+ : symbols.charAt(Math.floor(number / symbols.length) - 1) +
386
+ symbols.charAt(number % symbols.length);
346
387
  }
347
388
  getBookIndex() {
348
389
  // A map from word to a map of chapters containing the word
349
390
  const bookIndex = {};
350
391
  // Construct the index by building a dictionary of chapters in which each word appears.
351
- this.chapters.forEach(chapter => {
352
- let index = chapter.getIndex();
392
+ this.chapters.forEach((chapter) => {
393
+ let index = chapter.getIndex(this);
353
394
  if (index)
354
- Object.keys(index).forEach(word => {
355
- if (word !== "" && word.length > 2) {
395
+ Object.keys(index).forEach((word) => {
396
+ if (word !== '' && word.length > 2) {
356
397
  if (!(word in bookIndex))
357
398
  bookIndex[word] = new Set();
358
- bookIndex[word].add(chapter.getChapterID());
399
+ bookIndex[word].add(chapter.getID());
359
400
  }
360
401
  });
361
402
  });
@@ -365,25 +406,39 @@ export default class Edition {
365
406
  getNextChapterID(chapterID) {
366
407
  // Handle back matter chapters.
367
408
  switch (chapterID) {
368
- case ChapterIDs.ReferencesID: return this.hasGlossary() ? ChapterIDs.GlossaryID : ChapterIDs.IndexID;
369
- case ChapterIDs.GlossaryID: return ChapterIDs.IndexID;
370
- case ChapterIDs.IndexID: return ChapterIDs.SearchID;
371
- case ChapterIDs.SearchID: return ChapterIDs.MediaID;
372
- case ChapterIDs.MediaID: return ChapterIDs.TableOfContentsID;
373
- case ChapterIDs.TableOfContentsID: return this.chapters.length > 0 ? this.chapters[0].getChapterID() : null;
409
+ case ChapterIDs.ReferencesID:
410
+ return this.hasGlossary()
411
+ ? ChapterIDs.GlossaryID
412
+ : ChapterIDs.IndexID;
413
+ case ChapterIDs.GlossaryID:
414
+ return ChapterIDs.IndexID;
415
+ case ChapterIDs.IndexID:
416
+ return ChapterIDs.SearchID;
417
+ case ChapterIDs.SearchID:
418
+ return ChapterIDs.MediaID;
419
+ case ChapterIDs.MediaID:
420
+ return ChapterIDs.TableOfContentsID;
421
+ case ChapterIDs.TableOfContentsID:
422
+ return this.chapters.length > 0
423
+ ? this.chapters[0].getID()
424
+ : null;
374
425
  default:
375
426
  let after = false;
376
427
  for (let i = 0; i < this.chapters.length; i++) {
377
428
  let chapter = this.chapters[i];
378
- if (chapter.getChapterID() === chapterID)
429
+ if (chapter.getID() === chapterID)
379
430
  after = true;
380
431
  // If we're after the given chapter and it's not forthcoming.
381
432
  else if (after && !chapter.isForthcoming())
382
- return chapter.getChapterID();
433
+ return chapter.getID();
383
434
  }
384
435
  // If the given ID was the last chapter, go to the next back matter chapter.
385
436
  if (after)
386
- return this.hasReferences() ? ChapterIDs.ReferencesID : this.hasGlossary() ? ChapterIDs.GlossaryID : ChapterIDs.IndexID;
437
+ return this.hasReferences()
438
+ ? ChapterIDs.ReferencesID
439
+ : this.hasGlossary()
440
+ ? ChapterIDs.GlossaryID
441
+ : ChapterIDs.IndexID;
387
442
  // Otherwise, it wasn't a valid ID
388
443
  else
389
444
  return null;
@@ -393,20 +448,37 @@ export default class Edition {
393
448
  getPreviousChapterID(chapterID) {
394
449
  switch (chapterID) {
395
450
  // Handle back matter chapters.
396
- case ChapterIDs.ReferencesID: return this.chapters.length > 0 ? this.chapters[this.chapters.length - 1].getChapterID() : null; // Last chapter of the book
397
- case ChapterIDs.GlossaryID: return this.hasReferences() ? ChapterIDs.ReferencesID : this.chapters.length > 0 ? this.chapters[this.chapters.length - 1].getChapterID() : ChapterIDs.TableOfContentsID;
398
- case ChapterIDs.IndexID: return this.hasGlossary() ? ChapterIDs.GlossaryID : this.hasReferences() ? ChapterIDs.ReferencesID : this.chapters.length > 0 ? this.chapters[this.chapters.length - 1].getChapterID() : ChapterIDs.TableOfContentsID;
399
- case ChapterIDs.SearchID: return ChapterIDs.IndexID;
400
- case ChapterIDs.MediaID: return ChapterIDs.SearchID;
451
+ case ChapterIDs.ReferencesID:
452
+ return this.chapters.length > 0
453
+ ? this.chapters[this.chapters.length - 1].getID()
454
+ : null; // Last chapter of the book
455
+ case ChapterIDs.GlossaryID:
456
+ return this.hasReferences()
457
+ ? ChapterIDs.ReferencesID
458
+ : this.chapters.length > 0
459
+ ? this.chapters[this.chapters.length - 1].getID()
460
+ : ChapterIDs.TableOfContentsID;
461
+ case ChapterIDs.IndexID:
462
+ return this.hasGlossary()
463
+ ? ChapterIDs.GlossaryID
464
+ : this.hasReferences()
465
+ ? ChapterIDs.ReferencesID
466
+ : this.chapters.length > 0
467
+ ? this.chapters[this.chapters.length - 1].getID()
468
+ : ChapterIDs.TableOfContentsID;
469
+ case ChapterIDs.SearchID:
470
+ return ChapterIDs.IndexID;
471
+ case ChapterIDs.MediaID:
472
+ return ChapterIDs.SearchID;
401
473
  default:
402
474
  let before = false;
403
475
  for (let i = this.chapters.length - 1; i >= 0; i--) {
404
476
  let chapter = this.chapters[i];
405
- if (chapter.getChapterID() === chapterID)
477
+ if (chapter.getID() === chapterID)
406
478
  before = true;
407
479
  // If we're before the given chapter and it's not forthcoming.
408
480
  else if (before && !chapter.isForthcoming())
409
- return chapter.getChapterID();
481
+ return chapter.getID();
410
482
  }
411
483
  // If the given ID was the last chapter, go to the next back matter chapter.
412
484
  if (before)
@@ -420,26 +492,26 @@ export default class Edition {
420
492
  getEmbeds() {
421
493
  let embeds = [];
422
494
  // Add the book cover
423
- const cover = this.getImage("cover");
495
+ const cover = this.getImage('cover');
424
496
  if (cover) {
425
497
  let coverNode = Parser.parseEmbed(this, cover);
426
498
  if (coverNode instanceof EmbedNode)
427
499
  embeds.push(coverNode);
428
500
  }
429
501
  // Add the cover and images from each chapter.
430
- this.getChapters().forEach(c => {
502
+ this.getChapters().forEach((c) => {
431
503
  const image = c.getImage();
432
504
  let cover = image === null ? undefined : Parser.parseEmbed(this, image);
433
505
  if (cover && cover instanceof EmbedNode)
434
506
  embeds.push(cover);
435
507
  // Get the chapter body's embeds
436
- let bodyEmbeds = c?.getAST()?.getEmbeds();
508
+ let bodyEmbeds = c?.getAST(this)?.getEmbeds();
437
509
  if (bodyEmbeds)
438
510
  bodyEmbeds.forEach((embed) => embeds.push(embed));
439
511
  });
440
512
  // Add the back matter covers
441
- let backmatter = ["references", "glossary", "index", "search", "media"];
442
- backmatter.forEach(id => {
513
+ let backmatter = ['references', 'glossary', 'index', 'search', 'media'];
514
+ backmatter.forEach((id) => {
443
515
  const img = this.getImage(id);
444
516
  if (img) {
445
517
  let image = Parser.parseEmbed(this, img);
@@ -449,13 +521,19 @@ export default class Edition {
449
521
  });
450
522
  return embeds;
451
523
  }
452
- isLatestPublishedEdition() {
453
- const latestEditionID = this.book?.getLatestPublishedEditionID();
454
- return latestEditionID !== undefined && latestEditionID === this.editionRef?.id;
524
+ isLatestEdition(book) {
525
+ const latestEditionID = book.getLatestEditionID();
526
+ return (latestEditionID !== undefined &&
527
+ latestEditionID === this.editionRef?.id);
528
+ }
529
+ isLatestPublishedEdition(book) {
530
+ const latestEditionID = book.getLatestPublishedEditionID();
531
+ return (latestEditionID !== undefined &&
532
+ latestEditionID === this.editionRef?.id);
455
533
  }
456
534
  /* Edition numbers are 1 to N */
457
- getEditionNumber() {
458
- const revisions = this.book?.getRevisions();
535
+ getEditionNumber(book) {
536
+ const revisions = book.getRevisions();
459
537
  if (revisions === undefined)
460
538
  return undefined;
461
539
  for (let i = 0; i < revisions.length; i++)
@@ -463,4 +541,8 @@ export default class Edition {
463
541
  return revisions.length - i;
464
542
  return undefined;
465
543
  }
544
+ getEditionLabel(book) {
545
+ const num = this.getEditionNumber(book);
546
+ return `${num}${num === 1 ? 'st' : num === 2 ? 'nd' : num === 3 ? 'rd' : 'th'}`;
547
+ }
466
548
  }