bookish-press 0.4.0

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 (309) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/components/Auth.svelte +52 -0
  4. package/components/Auth.svelte.d.ts +16 -0
  5. package/components/Authentication.d.ts +8 -0
  6. package/components/Authentication.js +1 -0
  7. package/components/BookPreview.svelte +87 -0
  8. package/components/BookPreview.svelte.d.ts +18 -0
  9. package/components/External.svelte +4 -0
  10. package/components/External.svelte.d.ts +18 -0
  11. package/components/Link.svelte +5 -0
  12. package/components/Link.svelte.d.ts +19 -0
  13. package/components/chapter/Atom.svelte +36 -0
  14. package/components/chapter/Atom.svelte.d.ts +19 -0
  15. package/components/chapter/Block.svelte +32 -0
  16. package/components/chapter/Block.svelte.d.ts +17 -0
  17. package/components/chapter/Callout.svelte +10 -0
  18. package/components/chapter/Callout.svelte.d.ts +17 -0
  19. package/components/chapter/CaptionedCode.svelte +34 -0
  20. package/components/chapter/CaptionedCode.svelte.d.ts +17 -0
  21. package/components/chapter/ChapterBody.svelte +23 -0
  22. package/components/chapter/ChapterBody.svelte.d.ts +18 -0
  23. package/components/chapter/Citations.svelte +66 -0
  24. package/components/chapter/Citations.svelte.d.ts +17 -0
  25. package/components/chapter/Code.svelte +35 -0
  26. package/components/chapter/Code.svelte.d.ts +22 -0
  27. package/components/chapter/Comment.svelte +46 -0
  28. package/components/chapter/Comment.svelte.d.ts +17 -0
  29. package/components/chapter/Definition.svelte +38 -0
  30. package/components/chapter/Definition.svelte.d.ts +20 -0
  31. package/components/chapter/Embed.svelte +122 -0
  32. package/components/chapter/Embed.svelte.d.ts +18 -0
  33. package/components/chapter/ErrorMessage.svelte +4 -0
  34. package/components/chapter/ErrorMessage.svelte.d.ts +17 -0
  35. package/components/chapter/Footnote.svelte +37 -0
  36. package/components/chapter/Footnote.svelte.d.ts +19 -0
  37. package/components/chapter/Format.svelte +57 -0
  38. package/components/chapter/Format.svelte.d.ts +18 -0
  39. package/components/chapter/InlineCode.svelte +12 -0
  40. package/components/chapter/InlineCode.svelte.d.ts +17 -0
  41. package/components/chapter/Label.svelte +20 -0
  42. package/components/chapter/Label.svelte.d.ts +17 -0
  43. package/components/chapter/Link.svelte +51 -0
  44. package/components/chapter/Link.svelte.d.ts +17 -0
  45. package/components/chapter/List.svelte +14 -0
  46. package/components/chapter/List.svelte.d.ts +17 -0
  47. package/components/chapter/Marginal.svelte +48 -0
  48. package/components/chapter/Marginal.svelte.d.ts +19 -0
  49. package/components/chapter/Paragraph.svelte +15 -0
  50. package/components/chapter/Paragraph.svelte.d.ts +18 -0
  51. package/components/chapter/Python.svelte +81 -0
  52. package/components/chapter/Python.svelte.d.ts +18 -0
  53. package/components/chapter/Quote.svelte +16 -0
  54. package/components/chapter/Quote.svelte.d.ts +17 -0
  55. package/components/chapter/Rule.svelte +4 -0
  56. package/components/chapter/Rule.svelte.d.ts +17 -0
  57. package/components/chapter/Table.svelte +29 -0
  58. package/components/chapter/Table.svelte.d.ts +17 -0
  59. package/components/chapter/Text.svelte +62 -0
  60. package/components/chapter/Text.svelte.d.ts +17 -0
  61. package/components/chapter/renderPosition.d.ts +3 -0
  62. package/components/chapter/renderPosition.js +4 -0
  63. package/components/editor/BookishEditor.svelte +755 -0
  64. package/components/editor/BookishEditor.svelte.d.ts +22 -0
  65. package/components/editor/CalloutEditor.svelte +10 -0
  66. package/components/editor/CalloutEditor.svelte.d.ts +17 -0
  67. package/components/editor/CaptionedCodeEditor.svelte +22 -0
  68. package/components/editor/CaptionedCodeEditor.svelte.d.ts +17 -0
  69. package/components/editor/CaretContext.d.ts +18 -0
  70. package/components/editor/CaretContext.js +1 -0
  71. package/components/editor/CaretState.d.ts +47 -0
  72. package/components/editor/CaretState.js +1 -0
  73. package/components/editor/CaretUtilities.d.ts +5 -0
  74. package/components/editor/CaretUtilities.js +1 -0
  75. package/components/editor/CitationsEditor.svelte +42 -0
  76. package/components/editor/CitationsEditor.svelte.d.ts +17 -0
  77. package/components/editor/Clipboard.d.ts +3 -0
  78. package/components/editor/Clipboard.js +1 -0
  79. package/components/editor/Command.d.ts +18 -0
  80. package/components/editor/Command.js +1 -0
  81. package/components/editor/Commands.d.ts +3 -0
  82. package/components/editor/Commands.js +951 -0
  83. package/components/editor/ConfirmButton.svelte +43 -0
  84. package/components/editor/ConfirmButton.svelte.d.ts +18 -0
  85. package/components/editor/DefinitionEditor.svelte +28 -0
  86. package/components/editor/DefinitionEditor.svelte.d.ts +17 -0
  87. package/components/editor/EmbedEditor.svelte +87 -0
  88. package/components/editor/EmbedEditor.svelte.d.ts +17 -0
  89. package/components/editor/ImageChooser.svelte +70 -0
  90. package/components/editor/ImageChooser.svelte.d.ts +18 -0
  91. package/components/editor/InlineCodeEditor.svelte +9 -0
  92. package/components/editor/InlineCodeEditor.svelte.d.ts +17 -0
  93. package/components/editor/LabelEditor.svelte +23 -0
  94. package/components/editor/LabelEditor.svelte.d.ts +17 -0
  95. package/components/editor/LanguageEditor.svelte +11 -0
  96. package/components/editor/LanguageEditor.svelte.d.ts +17 -0
  97. package/components/editor/LinkEditor.svelte +75 -0
  98. package/components/editor/LinkEditor.svelte.d.ts +17 -0
  99. package/components/editor/Note.svelte +9 -0
  100. package/components/editor/Note.svelte.d.ts +23 -0
  101. package/components/editor/PositionEditor.svelte +14 -0
  102. package/components/editor/PositionEditor.svelte.d.ts +18 -0
  103. package/components/editor/QuoteEditor.svelte +5 -0
  104. package/components/editor/QuoteEditor.svelte.d.ts +17 -0
  105. package/components/editor/Switch.svelte +56 -0
  106. package/components/editor/Switch.svelte.d.ts +20 -0
  107. package/components/editor/TextEditor.svelte +193 -0
  108. package/components/editor/TextEditor.svelte.d.ts +23 -0
  109. package/components/editor/Toggle.svelte +38 -0
  110. package/components/editor/Toggle.svelte.d.ts +19 -0
  111. package/components/editor/Toolbar.svelte +202 -0
  112. package/components/editor/Toolbar.svelte.d.ts +20 -0
  113. package/components/editor/ToolbarGroup.svelte +41 -0
  114. package/components/editor/ToolbarGroup.svelte.d.ts +19 -0
  115. package/components/editor/ToolbarIcon.svelte +22 -0
  116. package/components/editor/ToolbarIcon.svelte.d.ts +16 -0
  117. package/components/editor/ToolbarSpacer.svelte +1 -0
  118. package/components/editor/ToolbarSpacer.svelte.d.ts +19 -0
  119. package/components/editor/URLEditor.svelte +15 -0
  120. package/components/editor/URLEditor.svelte.d.ts +18 -0
  121. package/components/editor/UndoState.d.ts +8 -0
  122. package/components/editor/UndoState.js +1 -0
  123. package/components/page/Acknowledgements.svelte +29 -0
  124. package/components/page/Acknowledgements.svelte.d.ts +14 -0
  125. package/components/page/Alert.svelte +11 -0
  126. package/components/page/Alert.svelte.d.ts +23 -0
  127. package/components/page/Authors.svelte +62 -0
  128. package/components/page/Authors.svelte.d.ts +20 -0
  129. package/components/page/BulkReferenceEditor.svelte +32 -0
  130. package/components/page/BulkReferenceEditor.svelte.d.ts +14 -0
  131. package/components/page/Chapter.svelte +301 -0
  132. package/components/page/Chapter.svelte.d.ts +18 -0
  133. package/components/page/ChapterContext.d.ts +10 -0
  134. package/components/page/ChapterContext.js +1 -0
  135. package/components/page/Contexts.d.ts +27 -0
  136. package/components/page/Contexts.js +21 -0
  137. package/components/page/DefinitionView.svelte +129 -0
  138. package/components/page/DefinitionView.svelte.d.ts +18 -0
  139. package/components/page/Description.svelte +22 -0
  140. package/components/page/Description.svelte.d.ts +14 -0
  141. package/components/page/Edition.svelte +106 -0
  142. package/components/page/Edition.svelte.d.ts +21 -0
  143. package/components/page/Glossary.svelte +61 -0
  144. package/components/page/Glossary.svelte.d.ts +14 -0
  145. package/components/page/Header.svelte +105 -0
  146. package/components/page/Header.svelte.d.ts +27 -0
  147. package/components/page/Index.svelte +117 -0
  148. package/components/page/Index.svelte.d.ts +14 -0
  149. package/components/page/Instructions.svelte +30 -0
  150. package/components/page/Instructions.svelte.d.ts +16 -0
  151. package/components/page/License.svelte +29 -0
  152. package/components/page/License.svelte.d.ts +14 -0
  153. package/components/page/Loading.svelte +13 -0
  154. package/components/page/Loading.svelte.d.ts +19 -0
  155. package/components/page/Media.svelte +76 -0
  156. package/components/page/Media.svelte.d.ts +14 -0
  157. package/components/page/MediaPreview.svelte +11 -0
  158. package/components/page/MediaPreview.svelte.d.ts +19 -0
  159. package/components/page/Outline.svelte +173 -0
  160. package/components/page/Outline.svelte.d.ts +19 -0
  161. package/components/page/Page.svelte +53 -0
  162. package/components/page/Page.svelte.d.ts +19 -0
  163. package/components/page/PossibleChapter.svelte +14 -0
  164. package/components/page/PossibleChapter.svelte.d.ts +14 -0
  165. package/components/page/PossibleReference.svelte +16 -0
  166. package/components/page/PossibleReference.svelte.d.ts +19 -0
  167. package/components/page/Print.svelte +8 -0
  168. package/components/page/Print.svelte.d.ts +14 -0
  169. package/components/page/Reference.svelte +145 -0
  170. package/components/page/Reference.svelte.d.ts +17 -0
  171. package/components/page/References.svelte +58 -0
  172. package/components/page/References.svelte.d.ts +14 -0
  173. package/components/page/Revisions.svelte +124 -0
  174. package/components/page/Revisions.svelte.d.ts +14 -0
  175. package/components/page/Search.svelte +108 -0
  176. package/components/page/Search.svelte.d.ts +14 -0
  177. package/components/page/Status.svelte +49 -0
  178. package/components/page/Status.svelte.d.ts +19 -0
  179. package/components/page/SubdomainEditor.svelte +30 -0
  180. package/components/page/SubdomainEditor.svelte.d.ts +14 -0
  181. package/components/page/TableOfContents.svelte +234 -0
  182. package/components/page/TableOfContents.svelte.d.ts +14 -0
  183. package/components/page/TableOfContentsImage.svelte +15 -0
  184. package/components/page/TableOfContentsImage.svelte.d.ts +16 -0
  185. package/components/page/TableOfContentsRow.svelte +57 -0
  186. package/components/page/TableOfContentsRow.svelte.d.ts +24 -0
  187. package/components/page/ThemeEditor.svelte +52 -0
  188. package/components/page/ThemeEditor.svelte.d.ts +14 -0
  189. package/components/page/ThemeEditorPreview.svelte +37 -0
  190. package/components/page/ThemeEditorPreview.svelte.d.ts +17 -0
  191. package/components/page/ThemeSetEditor.svelte +17 -0
  192. package/components/page/ThemeSetEditor.svelte.d.ts +18 -0
  193. package/components/page/ThemeVariableEditor.svelte +28 -0
  194. package/components/page/ThemeVariableEditor.svelte.d.ts +18 -0
  195. package/components/page/Unknown.svelte +29 -0
  196. package/components/page/Unknown.svelte.d.ts +16 -0
  197. package/components/page/margins.d.ts +2 -0
  198. package/components/page/margins.js +132 -0
  199. package/models/BookLoader.d.ts +2 -0
  200. package/models/BookLoader.js +67 -0
  201. package/models/Firebase.d.ts +4 -0
  202. package/models/Firebase.js +26 -0
  203. package/models/Firestore.d.ts +22 -0
  204. package/models/Firestore.js +194 -0
  205. package/models/book/Book.d.ts +60 -0
  206. package/models/book/Book.js +135 -0
  207. package/models/book/BookMedia.d.ts +23 -0
  208. package/models/book/BookMedia.js +102 -0
  209. package/models/book/Chapter.d.ts +70 -0
  210. package/models/book/Chapter.js +221 -0
  211. package/models/book/ChapterID.d.ts +10 -0
  212. package/models/book/ChapterID.js +10 -0
  213. package/models/book/Definition.d.ts +6 -0
  214. package/models/book/Definition.js +1 -0
  215. package/models/book/Edition.d.ts +111 -0
  216. package/models/book/Edition.js +466 -0
  217. package/models/book/Theme.d.ts +106 -0
  218. package/models/book/Theme.js +76 -0
  219. package/models/book/ThemeColors.d.ts +15 -0
  220. package/models/book/ThemeColors.js +1 -0
  221. package/models/chapter/AtomNode.d.ts +18 -0
  222. package/models/chapter/AtomNode.js +31 -0
  223. package/models/chapter/BlockNode.d.ts +21 -0
  224. package/models/chapter/BlockNode.js +117 -0
  225. package/models/chapter/BlocksNode.d.ts +50 -0
  226. package/models/chapter/BlocksNode.js +709 -0
  227. package/models/chapter/BlocksNode.test.d.ts +1 -0
  228. package/models/chapter/BlocksNode.test.js +302 -0
  229. package/models/chapter/Bookkeeping.d.ts +4 -0
  230. package/models/chapter/Bookkeeping.js +1 -0
  231. package/models/chapter/CalloutNode.d.ts +17 -0
  232. package/models/chapter/CalloutNode.js +36 -0
  233. package/models/chapter/Caret.d.ts +21 -0
  234. package/models/chapter/Caret.js +69 -0
  235. package/models/chapter/ChapterNode.d.ts +34 -0
  236. package/models/chapter/ChapterNode.js +80 -0
  237. package/models/chapter/CitationsNode.d.ts +16 -0
  238. package/models/chapter/CitationsNode.js +17 -0
  239. package/models/chapter/CodeNode.d.ts +30 -0
  240. package/models/chapter/CodeNode.js +64 -0
  241. package/models/chapter/CommentNode.d.ts +18 -0
  242. package/models/chapter/CommentNode.js +32 -0
  243. package/models/chapter/DefinitionNode.d.ts +12 -0
  244. package/models/chapter/DefinitionNode.js +20 -0
  245. package/models/chapter/Edit.d.ts +7 -0
  246. package/models/chapter/Edit.js +1 -0
  247. package/models/chapter/EmbedNode.d.ts +40 -0
  248. package/models/chapter/EmbedNode.js +89 -0
  249. package/models/chapter/ErrorNode.d.ts +17 -0
  250. package/models/chapter/ErrorNode.js +21 -0
  251. package/models/chapter/FootnoteNode.d.ts +18 -0
  252. package/models/chapter/FootnoteNode.js +29 -0
  253. package/models/chapter/FormatNode.d.ts +60 -0
  254. package/models/chapter/FormatNode.js +614 -0
  255. package/models/chapter/FormatNode.test.d.ts +1 -0
  256. package/models/chapter/FormatNode.test.js +54 -0
  257. package/models/chapter/InlineCodeNode.d.ts +12 -0
  258. package/models/chapter/InlineCodeNode.js +18 -0
  259. package/models/chapter/LabelNode.d.ts +17 -0
  260. package/models/chapter/LabelNode.js +22 -0
  261. package/models/chapter/LinkNode.d.ts +12 -0
  262. package/models/chapter/LinkNode.js +16 -0
  263. package/models/chapter/ListNode.d.ts +51 -0
  264. package/models/chapter/ListNode.js +370 -0
  265. package/models/chapter/ListNode.test.d.ts +1 -0
  266. package/models/chapter/ListNode.test.js +49 -0
  267. package/models/chapter/MetadataNode.d.ts +17 -0
  268. package/models/chapter/MetadataNode.js +24 -0
  269. package/models/chapter/Node.d.ts +36 -0
  270. package/models/chapter/Node.js +173 -0
  271. package/models/chapter/Node.test.d.ts +1 -0
  272. package/models/chapter/Node.test.js +27 -0
  273. package/models/chapter/ParagraphNode.d.ts +33 -0
  274. package/models/chapter/ParagraphNode.js +93 -0
  275. package/models/chapter/ParagraphNode.test.d.ts +1 -0
  276. package/models/chapter/ParagraphNode.test.js +21 -0
  277. package/models/chapter/Parser.d.ts +75 -0
  278. package/models/chapter/Parser.js +824 -0
  279. package/models/chapter/Parser.test.d.ts +1 -0
  280. package/models/chapter/Parser.test.js +25 -0
  281. package/models/chapter/Position.d.ts +2 -0
  282. package/models/chapter/Position.js +1 -0
  283. package/models/chapter/QuoteNode.d.ts +25 -0
  284. package/models/chapter/QuoteNode.js +61 -0
  285. package/models/chapter/ReferenceNode.d.ts +29 -0
  286. package/models/chapter/ReferenceNode.js +48 -0
  287. package/models/chapter/RootNode.d.ts +5 -0
  288. package/models/chapter/RootNode.js +1 -0
  289. package/models/chapter/RuleNode.d.ts +15 -0
  290. package/models/chapter/RuleNode.js +16 -0
  291. package/models/chapter/TableNode.d.ts +35 -0
  292. package/models/chapter/TableNode.js +176 -0
  293. package/models/chapter/TableNode.test.d.ts +1 -0
  294. package/models/chapter/TableNode.test.js +31 -0
  295. package/models/chapter/TextNode.d.ts +36 -0
  296. package/models/chapter/TextNode.js +136 -0
  297. package/models/chapter/TextNode.test.d.ts +1 -0
  298. package/models/chapter/TextNode.test.js +6 -0
  299. package/models/loadEdition.d.ts +5 -0
  300. package/models/loadEdition.js +16 -0
  301. package/package.json +189 -0
  302. package/util/getSubdomain.d.ts +3 -0
  303. package/util/getSubdomain.js +22 -0
  304. package/util/isMobile.d.ts +2 -0
  305. package/util/isMobile.js +6 -0
  306. package/util/mineReference.d.ts +3 -0
  307. package/util/mineReference.js +62 -0
  308. package/util/scrollToEyeLevel.d.ts +2 -0
  309. package/util/scrollToEyeLevel.js +5 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,302 @@
1
+ import { test, expect } from 'vitest';
2
+ import CalloutNode from "./CalloutNode";
3
+ import ChapterNode from "./ChapterNode";
4
+ import CodeNode from "./CodeNode";
5
+ import EmbedNode from "./EmbedNode";
6
+ import FootnoteNode from "./FootnoteNode";
7
+ import FormatNode from "./FormatNode";
8
+ import ListNode from "./ListNode";
9
+ import ParagraphNode from "./ParagraphNode";
10
+ import Parser from './Parser';
11
+ import QuoteNode from "./QuoteNode";
12
+ import RuleNode from "./RuleNode";
13
+ import TableNode from "./TableNode";
14
+ import TextNode from "./TextNode";
15
+ const firstText = "First paragraph.";
16
+ const firstTextNode = new TextNode(firstText);
17
+ const firstFormat = new FormatNode("", [firstTextNode]);
18
+ const firstParagraph = new ParagraphNode(0, firstFormat);
19
+ const lastText = "Last paragraph.";
20
+ const lastTextNode = new TextNode(lastText);
21
+ const lastFormat = new FormatNode("", [lastTextNode]);
22
+ const lastParagraph = new ParagraphNode(0, lastFormat);
23
+ const footnoteText = new TextNode("This is my footnote.");
24
+ const footnote = new FootnoteNode(new FormatNode("", [footnoteText]));
25
+ const paragraphChapter = new ChapterNode([firstParagraph, lastParagraph]);
26
+ const numberedList = new ListNode([firstFormat, lastFormat], true);
27
+ const bulletedList = new ListNode([firstFormat.copy(), lastFormat.copy()], false);
28
+ const beforeBold = new TextNode("I am a ");
29
+ const bold = new TextNode("bold");
30
+ const afterBold = new TextNode(" word.");
31
+ const boldChapter = new ChapterNode([new ParagraphNode(0, new FormatNode("", [beforeBold, new FormatNode("*", [bold]), afterBold]))]);
32
+ test("Insert a block", () => {
33
+ expect(paragraphChapter.withBlockInserted(firstParagraph, new RuleNode(), true)?.toBookdown()).toBe(`\n\n-\n\n${firstText}\n\n${lastText}`);
34
+ expect(paragraphChapter.withBlockInserted(firstParagraph, new RuleNode(), false)?.toBookdown()).toBe(`${firstText}\n\n-\n\n${lastText}`);
35
+ expect(paragraphChapter.withBlockInserted(lastParagraph, new RuleNode(), false)?.toBookdown()).toBe(`${firstText}\n\n${lastText}\n\n-\n\n`);
36
+ });
37
+ test("Remove a block", () => {
38
+ expect(paragraphChapter.withoutBlock(firstParagraph)?.toBookdown()).toBe(lastText);
39
+ });
40
+ test("Merge adjascent lists", () => {
41
+ expect(new ChapterNode([numberedList, bulletedList]).toBookdown()).toBe(`\n\n1. ${firstText}\n2. ${lastText}\n\n\n\n* ${firstText}\n* ${lastText}\n\n`);
42
+ expect(new ChapterNode([numberedList, numberedList]).toBookdown()).toBe(`\n\n1. ${firstText}\n2. ${lastText}\n3. ${firstText}\n4. ${lastText}\n\n`);
43
+ expect(new ChapterNode([bulletedList, bulletedList]).toBookdown()).toBe(`\n\n* ${firstText}\n* ${lastText}\n* ${firstText}\n* ${lastText}\n\n`);
44
+ });
45
+ test("Format range", () => {
46
+ // Bold part of a paragraph.
47
+ expect(paragraphChapter.withRangeFormatted({ start: { node: firstTextNode, index: 6 }, end: { node: firstTextNode, index: 10 } }, "*")?.root.toBookdown())
48
+ .toBe(`First *para*graph.\n\n${lastText}`);
49
+ // Remove part of a paragraph.
50
+ expect(paragraphChapter.withRangeFormatted({ start: { node: firstTextNode, index: 6 }, end: { node: firstTextNode, index: 10 } }, undefined)?.root.toBookdown())
51
+ .toBe(`First graph.\n\n${lastText}`);
52
+ // Unbold the bold part of a paragraph with the selection outside the bold.
53
+ expect(boldChapter.withRangeFormatted({ start: { node: beforeBold, index: beforeBold.getLength() }, end: { node: afterBold, index: 0 } }, "")?.root.toBookdown())
54
+ .toBe(`I am a bold word.`);
55
+ // Unbold the bold part of a paragraph with the selection inside the bold.
56
+ expect(boldChapter.withRangeFormatted({ start: { node: bold, index: 0 }, end: { node: bold, index: bold.getLength() } }, "")?.root.toBookdown())
57
+ .toBe(`I am a bold word.`);
58
+ // Expand the bold part of a paragraph.
59
+ expect(boldChapter.withRangeFormatted({ start: { node: beforeBold, index: beforeBold.getLength() - 2 }, end: { node: afterBold, index: 0 } }, "*")?.root.toBookdown())
60
+ .toBe(`I am *a bold* word.`);
61
+ // Delete across a paragraph boundary.
62
+ expect(paragraphChapter.withoutRange({ start: { node: firstTextNode, index: 6 }, end: { node: lastTextNode, index: 5 } })?.root.toBookdown())
63
+ .toBe(`First paragraph.`);
64
+ // Bold across a paragraph boundary.
65
+ expect(paragraphChapter.withRangeFormatted({ start: { node: firstTextNode, index: 6 }, end: { node: lastTextNode, index: 4 } }, "*")?.root.toBookdown())
66
+ .toBe(`First *paragraph.*\n\n*Last* paragraph.`);
67
+ });
68
+ test("Split selection", () => {
69
+ const caret = { node: firstTextNode, index: 5 };
70
+ const chapter = new ChapterNode([firstParagraph]);
71
+ // Split a paragraph
72
+ const split = chapter.withSelectionSplit({ start: caret, end: caret });
73
+ expect(split?.root.toBookdown()).toBe("First\n\n paragraph.");
74
+ expect(split?.range.start.node.toBookdown()).toBe(" paragraph.");
75
+ expect(split?.range.start.index).toBe(0);
76
+ // Delete a selection and then split
77
+ const selectSplit = chapter.withSelectionSplit({ start: { node: firstTextNode, index: 5 }, end: { node: firstTextNode, index: 10 } });
78
+ expect(selectSplit?.root.toBookdown()).toBe("First\n\ngraph.");
79
+ // Split at the beginning
80
+ const startCaret = { node: firstTextNode, index: 0 };
81
+ const startSplit = chapter.withSelectionSplit({ start: startCaret, end: startCaret });
82
+ expect(startSplit?.root.toBookdown()).toBe(`\n\n${firstText}`);
83
+ // Split at the end
84
+ const endCaret = { node: firstTextNode, index: 16 };
85
+ const endSplit = chapter.withSelectionSplit({ start: endCaret, end: endCaret });
86
+ expect(endSplit?.root.toBookdown()).toBe(`${firstText}\n\n`);
87
+ });
88
+ test("Without atom", () => {
89
+ const chapterAtomEnd = new ChapterNode([new ParagraphNode(0, new FormatNode("", [firstFormat, footnote]))]);
90
+ expect(chapterAtomEnd.withoutAtom(footnote)?.root.toBookdown())
91
+ .toBe(`${firstText}`);
92
+ const chapterAtomBegin = new ChapterNode([new ParagraphNode(0, new FormatNode("", [footnote, firstFormat]))]);
93
+ expect(chapterAtomBegin.withoutAtom(footnote)?.root.toBookdown())
94
+ .toBe(`${firstText}`);
95
+ const chapterAtomMiddle = new ChapterNode([new ParagraphNode(0, new FormatNode("", [firstFormat, footnote, lastFormat]))]);
96
+ expect(chapterAtomMiddle.withoutAtom(footnote)?.root.toBookdown())
97
+ .toBe(`${firstText}${lastText}`);
98
+ });
99
+ test("Insert atom", () => {
100
+ const endOfFirst = { node: firstTextNode, index: firstTextNode.getLength() };
101
+ // Insert empty footnote
102
+ expect(paragraphChapter.withSegmentAtSelection({ start: endOfFirst, end: endOfFirst }, text => new FootnoteNode(new FormatNode("", [new TextNode(text)])))?.root.toBookdown())
103
+ .toBe(`${firstText}{}\n\n${lastText}`);
104
+ // Convert selection to footnote
105
+ expect(paragraphChapter.withSegmentAtSelection({ start: { node: firstTextNode, index: 6 }, end: { node: firstTextNode, index: 15 } }, text => new FootnoteNode(new FormatNode("", [new TextNode(text)])))?.root.toBookdown())
106
+ .toBe(`First {paragraph}.\n\n${lastText}`);
107
+ });
108
+ test("Convert paragraphs to lists", () => {
109
+ // Convert a single paragraph to a list item.
110
+ expect(paragraphChapter.withParagraphsAsLists({ start: { node: firstTextNode, index: 0 }, end: { node: firstTextNode, index: 0 } }, false)?.toBookdown())
111
+ .toBe(`\n\n* ${firstText}\n\n${lastText}`);
112
+ // Convert two paragraphs to a single list
113
+ expect(paragraphChapter.withParagraphsAsLists({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 0 } }, true)?.toBookdown())
114
+ .toBe(`\n\n1. ${firstText}\n2. ${lastText}\n\n`);
115
+ // Convert paragraphs that span a blocks boundary to separate lists.
116
+ expect(new ChapterNode([firstParagraph, new CalloutNode([lastParagraph])]).withParagraphsAsLists({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 0 } }, true)?.toBookdown())
117
+ .toBe(`\n\n1. ${firstText}\n\n\n\n=\n\n\n1. ${lastText}\n\n\n=\n\n`);
118
+ // Convert non-paragraphs to lists
119
+ expect(new ChapterNode([numberedList]).withParagraphsAsLists({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 0 } }, true))
120
+ .toBeUndefined();
121
+ });
122
+ test("Convert lists to paragraphs", () => {
123
+ // Convert lists to paragraphs
124
+ expect(new ChapterNode([numberedList]).withListsAsParagraphs({ start: { node: firstTextNode, index: 0 }, end: { node: firstTextNode, index: 5 } })?.toBookdown())
125
+ .toBe(`\n\n${firstText}\n\n${lastText}\n\n`);
126
+ // Convert both items to paragraphs.
127
+ expect(new ChapterNode([numberedList]).withListsAsParagraphs({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 5 } })?.toBookdown())
128
+ .toBe(`\n\n${firstText}\n\n${lastText}\n\n`);
129
+ // Convert non-list to paragraphs.
130
+ expect(paragraphChapter.withListsAsParagraphs({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 5 } })?.toBookdown())
131
+ .toBeUndefined();
132
+ // Convert two contiguous lists to paragraphs.
133
+ expect(new ChapterNode([numberedList, bulletedList]).withListsAsParagraphs({ start: { node: firstTextNode, index: 0 }, end: { node: bulletedList.getFirstItem()?.getFirstTextNode(), index: 0 } })?.toBookdown())
134
+ .toBe(`\n\n${firstText}\n\n${lastText}\n\n\n\n${firstText}\n\n${lastText}\n\n`);
135
+ // Convert two non-contiguous lists to paragraphs.
136
+ expect(new ChapterNode([numberedList, new ParagraphNode(0, new FormatNode("", [new TextNode("Intruder!")])), bulletedList]).withListsAsParagraphs({ start: { node: firstTextNode, index: 0 }, end: { node: bulletedList.getFirstItem()?.getFirstTextNode(), index: 0 } })?.toBookdown())
137
+ .toBe(`\n\n${firstText}\n\n${lastText}\n\nIntruder!\n\n${firstText}\n\n${lastText}\n\n`);
138
+ // Convert various subparts of a nested list into paragraphs.
139
+ const oneText = new TextNode("one");
140
+ const twoText = new TextNode("two");
141
+ const threeText = new TextNode("three");
142
+ const fourText = new TextNode("four");
143
+ const list = new ListNode([new FormatNode("", [oneText]), new ListNode([new FormatNode("", [twoText]), new FormatNode("", [threeText])], true), new FormatNode("", [fourText])], true);
144
+ expect(new ChapterNode([list]).withListsAsParagraphs({ start: { node: oneText, index: 0 }, end: { node: fourText, index: 0 } })?.toBookdown())
145
+ .toBe(`\n\n${oneText.getText()}\n\n${twoText.getText()}\n\n${threeText.getText()}\n\n${fourText.getText()}\n\n`);
146
+ expect(new ChapterNode([list]).withListsAsParagraphs({ start: { node: oneText, index: 0 }, end: { node: twoText, index: 0 } })?.toBookdown())
147
+ .toBe(`\n\n${oneText.getText()}\n\n${twoText.getText()}\n\n${threeText.getText()}\n\n${fourText.getText()}\n\n`);
148
+ expect(new ChapterNode([list]).withListsAsParagraphs({ start: { node: twoText, index: 0 }, end: { node: threeText, index: 0 } })?.toBookdown())
149
+ .toBe(`\n\n${oneText.getText()}\n\n${twoText.getText()}\n\n${threeText.getText()}\n\n${fourText.getText()}\n\n`);
150
+ });
151
+ test("Indent/unindent list items", () => {
152
+ expect(new ChapterNode([numberedList]).withListsIndented({ start: { node: firstTextNode, index: 0 }, end: { node: firstTextNode, index: 0 } }, true)?.toBookdown()).toBe("\n\n1.. First paragraph.\n2. Last paragraph.\n\n");
153
+ const indented = new ChapterNode([numberedList]).withListsIndented({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 0 } }, true);
154
+ expect(indented?.toBookdown()).toBe("\n\n1.. First paragraph.\n2.. Last paragraph.\n\n");
155
+ expect(indented?.withListsIndented({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 0 } }, false)?.toBookdown()).toBe("\n\n1. First paragraph.\n2. Last paragraph.\n\n");
156
+ });
157
+ test("Style lists", () => {
158
+ expect(new ChapterNode([numberedList]).withListAsStyle(numberedList, false)?.toBookdown())
159
+ .toBe(`\n\n* ${firstText}\n* ${lastText}\n\n`);
160
+ });
161
+ test("Backspace/delete", () => {
162
+ const chapter = new ChapterNode([firstParagraph, lastParagraph]);
163
+ expect(chapter.withoutAdjacentContent({ node: lastTextNode, index: 1 }, true)?.root.toBookdown())
164
+ .toBe(`${firstText}\n\nLst paragraph.`);
165
+ expect(chapter.withoutAdjacentContent({ node: lastTextNode, index: 1 }, false)?.root.toBookdown())
166
+ .toBe(`${firstText}\n\nast paragraph.`);
167
+ expect(chapter.withoutAdjacentContent({ node: lastTextNode, index: 0 }, false)?.root.toBookdown())
168
+ .toBe(`${firstText}${lastText}`);
169
+ expect(chapter.withoutAdjacentContent({ node: firstTextNode, index: firstTextNode.getLength() }, true)?.root.toBookdown())
170
+ .toBe(`${firstText}${lastText}`);
171
+ expect(chapter.withoutAdjacentContent({ node: firstTextNode, index: 0 }, false)?.root.toBookdown())
172
+ .toBeUndefined();
173
+ const beforeText = new TextNode("Before");
174
+ const beforeParagraph = new ParagraphNode(0, new FormatNode("", [beforeText]));
175
+ const afterText = new TextNode("After");
176
+ const afterParagraph = new ParagraphNode(0, new FormatNode("", [afterText]));
177
+ const listChapter = new ChapterNode([beforeParagraph, numberedList, afterParagraph]);
178
+ // Do nothing when backspacing at the beginning of a list.
179
+ expect(listChapter.withoutAdjacentContent({ node: firstTextNode, index: 0 }, false)?.root.toBookdown())
180
+ .toBeUndefined();
181
+ // Delete a character in a list
182
+ expect(listChapter.withoutAdjacentContent({ node: firstTextNode, index: 0 }, true)?.root.toBookdown())
183
+ .toBe(`Before\n\n1. irst paragraph.\n2. ${lastText}\n\nAfter`);
184
+ // Can't forward delete into a list
185
+ expect(listChapter.withoutAdjacentContent({ node: beforeText, index: beforeText.getLength() }, true)?.root.toBookdown())
186
+ .toBe(`Before${firstText}\n\n1. ${lastText}\n\nAfter`);
187
+ // Backspace a paragraph into the last list item
188
+ expect(listChapter.withoutAdjacentContent({ node: afterText, index: 0 }, false)?.root.toBookdown())
189
+ .toBe(`Before\n\n1. ${firstText}\n2. ${lastText}After\n\n`);
190
+ // Select two list items for deletion.
191
+ expect(listChapter.withoutRange({ start: { node: firstTextNode, index: 0 }, end: { node: lastTextNode, index: 5 } })?.root.toBookdown())
192
+ .toBe(`Before\n\n1. paragraph.\n\nAfter`);
193
+ // Backspace over a block
194
+ const quoteChapter = new ChapterNode([new QuoteNode([]), firstParagraph]);
195
+ expect(quoteChapter.withoutAdjacentContent({ node: firstTextNode, index: 0 }, false)?.root.toBookdown())
196
+ .toBe(`\n\n${firstText}`);
197
+ });
198
+ test("Copy/paste", () => {
199
+ // Build a document with everything.
200
+ const sentence1 = new TextNode("Hello, I am a sentence.");
201
+ const paragraph1 = new ParagraphNode(0, new FormatNode("", [sentence1]));
202
+ const sentence2 = new TextNode("Hello, I am another sentence.");
203
+ const boldSentence3 = new TextNode(" Can you believe my size?");
204
+ const paragraph2 = new ParagraphNode(0, new FormatNode("", [sentence2, new FormatNode("*", [boldSentence3])]));
205
+ const sentence3 = new TextNode("Lonely last.");
206
+ const paragraph3 = new ParagraphNode(0, new FormatNode("", [sentence3]));
207
+ const imageCaption = new TextNode("no image");
208
+ const imageCredit = new TextNode("Amy");
209
+ const image = new EmbedNode("nope", "nothing", new FormatNode("", [imageCaption]), new FormatNode("", [imageCredit]));
210
+ const js = new TextNode("let a = 1;");
211
+ const code = new CodeNode(js, "js", "|", new FormatNode("", [new TextNode("assignment")]));
212
+ const item1 = new TextNode("one");
213
+ const item1a = new TextNode("one a");
214
+ const item2 = new TextNode("two");
215
+ const item3 = new TextNode("three");
216
+ const list = new ListNode([new FormatNode("", [item1]), new ListNode([new FormatNode("", [item1a])], true), new FormatNode("", [item2]), new FormatNode("", [item3])], true);
217
+ const afterList = new TextNode("Post-list");
218
+ const afterListParagraph = new ParagraphNode(0, new FormatNode("", [afterList]));
219
+ const cellr1c1 = new TextNode("a");
220
+ const cellr1c2 = new TextNode("b");
221
+ const cellr1c3 = new TextNode("c");
222
+ const cellr2c1 = new TextNode("d");
223
+ const cellr2c2 = new TextNode("e");
224
+ const cellr2c3 = new TextNode("f");
225
+ const cellr3c1 = new TextNode("g");
226
+ const cellr3c2 = new TextNode("h");
227
+ const cellr3c3 = new TextNode("i");
228
+ const table = new TableNode([
229
+ [new FormatNode("", [cellr1c1]), new FormatNode("", [cellr1c2]), new FormatNode("", [cellr1c3])],
230
+ [new FormatNode("", [cellr2c1]), new FormatNode("", [cellr2c2]), new FormatNode("", [cellr2c3])],
231
+ [new FormatNode("", [cellr3c1]), new FormatNode("", [cellr3c2]), new FormatNode("", [cellr3c3])]
232
+ ]);
233
+ const calloutOne = new TextNode("one");
234
+ const calloutTwo = new TextNode("two");
235
+ const calloutThree = new TextNode("three");
236
+ const callout = new CalloutNode([new ParagraphNode(0, new FormatNode("", [calloutOne])), new ParagraphNode(0, new FormatNode("", [calloutTwo])), new ParagraphNode(0, new FormatNode("", [calloutThree]))]);
237
+ const quoteOne = new TextNode("I'm tired");
238
+ const quoteCredit = new TextNode("Boomy");
239
+ const quote = new QuoteNode([new ParagraphNode(0, new FormatNode("", [quoteOne]))], new FormatNode("", [quoteCredit]));
240
+ const chapter = new ChapterNode([paragraph1, paragraph2, paragraph3, image, code, list, afterListParagraph, table, callout, quote]);
241
+ const partialSegment = chapter.copyRange({ start: { node: sentence1, index: 12 }, end: { node: sentence1, index: 22 } });
242
+ const reversedPartialSegment = chapter.copyRange({ start: { node: sentence1, index: 22 }, end: { node: sentence1, index: 12 } });
243
+ const consecutivePartialParagraphs = chapter.copyRange({ start: { node: sentence1, index: 12 }, end: { node: sentence2, index: 5 } });
244
+ const threeParagraphs = chapter.copyRange({ start: { node: sentence1, index: sentence1.getLength() - 1 }, end: { node: sentence3, index: 6 } });
245
+ // Paragraphs
246
+ expect(partialSegment?.toBookdown())
247
+ .toBe("a sentence");
248
+ expect(reversedPartialSegment?.toBookdown())
249
+ .toBe("a sentence");
250
+ expect(consecutivePartialParagraphs?.toBookdown())
251
+ .toBe("a sentence.\n\nHello");
252
+ expect(threeParagraphs?.toBookdown())
253
+ .toBe(`.\n\n${sentence2.toBookdown()}*${boldSentence3.toBookdown()}*\n\nLonely`);
254
+ const partialImageCaption = chapter.copyRange({ start: { node: imageCaption, index: 3 }, end: { node: imageCaption, index: 8 } });
255
+ const partialImageCredit = chapter.copyRange({ start: { node: imageCredit, index: 2 }, end: { node: imageCredit, index: 3 } });
256
+ const imageCaptionAndCredit = chapter.copyRange({ start: { node: imageCaption, index: 3 }, end: { node: imageCredit, index: 1 } });
257
+ const paragraphAndImage = chapter.copyRange({ start: { node: sentence3, index: 7 }, end: { node: imageCaption, index: 2 } });
258
+ // Images
259
+ expect(partialImageCaption?.toBookdown())
260
+ .toBe(`image`);
261
+ expect(partialImageCredit?.toBookdown())
262
+ .toBe(`y`);
263
+ expect(imageCaptionAndCredit?.toBookdown())
264
+ .toBe(`|nope|nothing|no |my|`);
265
+ expect(paragraphAndImage?.toBookdown())
266
+ .toBe(`last.\n\n|nope|nothing|no|Amy|\n\n`);
267
+ const imageAndCode = chapter.copyRange({ start: { node: imageCredit, index: 0 }, end: { node: js, index: 3 } });
268
+ // Code
269
+ expect(imageAndCode?.toBookdown())
270
+ .toBe(`\n\n|nope|nothing|no image|Amy|\n\n\n\n\`js\nlet\n\`\n\n`);
271
+ const partialList = chapter.copyRange({ start: { node: item1a, index: 4 }, end: { node: item2, index: 1 } });
272
+ const partialListAndParagraph = chapter.copyRange({ start: { node: item2, index: 0 }, end: { node: afterList, index: 4 } });
273
+ // Lists
274
+ expect(partialList?.toBookdown())
275
+ .toBe(`1.. a\n2. t`);
276
+ expect(partialListAndParagraph?.toBookdown())
277
+ .toBe(`\n\n1. two\n2. three\n\nPost`);
278
+ // Tables
279
+ const partialTable = chapter.copyRange({ start: { node: cellr2c2, index: 0 }, end: { node: cellr3c2, index: 1 } });
280
+ const partialTableAndParagraph = chapter.copyRange({ start: { node: afterList, index: 0 }, end: { node: cellr1c2, index: 1 } });
281
+ expect(partialTable?.toBookdown())
282
+ .toBe(`,|e|f\n,g|h|\n`);
283
+ expect(partialTableAndParagraph?.toBookdown())
284
+ .toBe(`Post-list\n\n,a|b|\n\n\n`);
285
+ // Callouts
286
+ const partialCallout = chapter.copyRange({ start: { node: calloutOne, index: 0 }, end: { node: calloutTwo, index: 1 } });
287
+ const partialTableAndCallout = chapter.copyRange({ start: { node: cellr3c3, index: 0 }, end: { node: calloutTwo, index: 3 } });
288
+ expect(partialCallout?.toBookdown())
289
+ .toBe(`=\none\n\nt\n=`);
290
+ expect(partialTableAndCallout?.toBookdown())
291
+ .toBe(`\n\n,||i\n\n\n\n\n=\none\n\ntwo\n=\n\n`);
292
+ // Quote
293
+ const partialCalloutAndQuote = chapter.copyRange({ start: { node: calloutThree, index: 0 }, end: { node: quoteOne, index: 9 } });
294
+ expect(partialCalloutAndQuote?.toBookdown())
295
+ .toBe(`\n\n=\nthree\n=\n\n\n\n"\nI'm tired\n"\n\n`);
296
+ });
297
+ test("to HTML", () => {
298
+ expect(Parser.parseChapter(undefined, "Hi.\n\nI am paragraphs.").toHTML()).toBe("<p>Hi.</p><p>I am paragraphs.</p>");
299
+ expect(Parser.parseChapter(undefined, "* I am one\n\n* I am two\n\n* I am three").toHTML()).toBe("<p></p><ul><li>I am one</li><li>I am two</li><li>I am three</li></ul><p></p>");
300
+ expect(Parser.parseChapter(undefined, "1. I am one\n\n2. I am two\n\n3. I am three").toHTML()).toBe("<p></p><ol><li>I am one</li><li>I am two</li><li>I am three</li></ol><p></p>");
301
+ expect(Parser.parseChapter(undefined, "|image.jpg|A picture|A caption|A credit|").toHTML()).toBe('<p></p><img src="image.jpg" alt="A picture" /><p></p>');
302
+ });
@@ -0,0 +1,4 @@
1
+ declare type Bookkeeping = {
2
+ symbols: Record<string, string>;
3
+ };
4
+ export default Bookkeeping;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import BlocksNode from "./BlocksNode";
2
+ import type BlockNode from "./BlockNode";
3
+ import type Position from "./Position";
4
+ export default class CalloutNode extends BlocksNode {
5
+ #private;
6
+ constructor(elements: BlockNode[], position?: Position);
7
+ getType(): string;
8
+ getPosition(): Position;
9
+ toText(): string;
10
+ toHTML(): string;
11
+ toBookdown(): string;
12
+ getChildren(): BlockNode[];
13
+ copy(): this;
14
+ withPosition(position: Position): CalloutNode;
15
+ withChildReplaced(node: BlockNode, replacement: BlockNode | undefined): this | undefined;
16
+ create(blocks: BlockNode[]): CalloutNode;
17
+ }
@@ -0,0 +1,36 @@
1
+ import BlocksNode from "./BlocksNode";
2
+ export default class CalloutNode extends BlocksNode {
3
+ #position;
4
+ constructor(elements, position = "|") {
5
+ super(elements);
6
+ this.#position = position;
7
+ }
8
+ getType() { return "callout"; }
9
+ getPosition() { return this.#position; }
10
+ toText() {
11
+ return this.getBlocks().map(element => element.toText()).join(" ");
12
+ }
13
+ toHTML() {
14
+ return `<hr/>${super.toHTML()}<hr/>`;
15
+ }
16
+ toBookdown() {
17
+ return "=\n" + this.getBlocks().map(element => element.toBookdown()).join("\n\n") + "\n=" + (this.#position !== "|" ? this.#position : "");
18
+ }
19
+ getChildren() { return this.getBlocks(); }
20
+ copy() {
21
+ return new CalloutNode(this.getBlocks().map(e => e.copy()), this.#position);
22
+ }
23
+ withPosition(position) { return new CalloutNode(this.getBlocks(), position); }
24
+ withChildReplaced(node, replacement) {
25
+ const index = this.getBlocks().indexOf(node);
26
+ if (index < 0)
27
+ return;
28
+ const blocks = replacement === undefined ?
29
+ [...this.getBlocks().slice(0, index), ...this.getBlocks().slice(index + 1)] :
30
+ [...this.getBlocks().slice(0, index), replacement, ...this.getBlocks().slice(index + 1)];
31
+ return new CalloutNode(blocks, this.#position);
32
+ }
33
+ create(blocks) {
34
+ return new CalloutNode(blocks, this.#position);
35
+ }
36
+ }
@@ -0,0 +1,21 @@
1
+ import type Node from "./Node";
2
+ import AtomNode from "./AtomNode";
3
+ import TextNode from "./TextNode";
4
+ declare type Caret = {
5
+ node: TextNode | AtomNode<any>;
6
+ index: number;
7
+ };
8
+ export default Caret;
9
+ export declare type CaretRange = {
10
+ start: Caret;
11
+ end: Caret;
12
+ };
13
+ export declare type IndexRange = {
14
+ start: number;
15
+ end: number;
16
+ };
17
+ export declare function getTextAndAtomNodes(node: Node): (AtomNode<any> | TextNode)[];
18
+ export declare function caretToIndex(node: Node, caret: Caret): number | undefined;
19
+ export declare function indexToCaret(node: Node, index: number): Caret | undefined;
20
+ export declare function caretRangeToIndexRange(node: Node, range: CaretRange): IndexRange | undefined;
21
+ export declare function indexRangeToCaretRange(node: Node, range: IndexRange): CaretRange | undefined;
@@ -0,0 +1,69 @@
1
+ import AtomNode from "./AtomNode";
2
+ import TextNode from "./TextNode";
3
+ import FormatNode from "./FormatNode";
4
+ export function getTextAndAtomNodes(node) {
5
+ return node.getNodes().filter(n => n instanceof TextNode || n instanceof AtomNode);
6
+ }
7
+ function getFormatRoots(node) {
8
+ const roots = [];
9
+ node.traverse((node, parents) => {
10
+ if (node instanceof FormatNode && parents.length > 0 && !(parents[parents.length - 1] instanceof FormatNode))
11
+ roots.push(node);
12
+ });
13
+ return roots;
14
+ }
15
+ export function caretToIndex(node, caret) {
16
+ let roots = getFormatRoots(node);
17
+ if (roots.length === 0)
18
+ roots = [node];
19
+ let currentIndex = 0;
20
+ for (let n = 0; n < roots.length; n++) {
21
+ const nodes = getTextAndAtomNodes(roots[n]);
22
+ for (let i = 0; i < nodes.length; i++) {
23
+ const t = nodes[i];
24
+ if (t !== caret.node)
25
+ currentIndex += t.getCaretPositionCount();
26
+ else
27
+ return currentIndex + caret.index;
28
+ }
29
+ // Include one for the root.
30
+ currentIndex++;
31
+ }
32
+ }
33
+ export function indexToCaret(node, index) {
34
+ let roots = getFormatRoots(node);
35
+ if (roots.length === 0)
36
+ roots = [node];
37
+ let currentIndex = 0;
38
+ for (let n = 0; n < roots.length; n++) {
39
+ const nodes = getTextAndAtomNodes(roots[n]);
40
+ for (let i = 0; i < nodes.length; i++) {
41
+ const t = nodes[i];
42
+ if (index >= currentIndex && index <= currentIndex + t.getCaretPositionCount()) {
43
+ // Prefer empty nodes to enable zero width formats.
44
+ if (i < nodes.length - 1) {
45
+ const next = nodes[i + 1];
46
+ if (next instanceof TextNode && next.getLength() === 0)
47
+ return { node: next, index: 0 };
48
+ }
49
+ return { node: t, index: index - currentIndex };
50
+ }
51
+ currentIndex += t.getCaretPositionCount();
52
+ }
53
+ currentIndex++;
54
+ }
55
+ }
56
+ export function caretRangeToIndexRange(node, range) {
57
+ const startIndex = caretToIndex(node, range.start);
58
+ const endIndex = caretToIndex(node, range.end);
59
+ if (startIndex === undefined || endIndex === undefined)
60
+ return;
61
+ return { start: startIndex, end: endIndex };
62
+ }
63
+ export function indexRangeToCaretRange(node, range) {
64
+ const start = indexToCaret(node, range.start);
65
+ const end = indexToCaret(node, range.end);
66
+ if (start === undefined || end === undefined)
67
+ return;
68
+ return { start: start, end: end };
69
+ }
@@ -0,0 +1,34 @@
1
+ import type Bookkeeping from "./Bookkeeping";
2
+ import type BlockNode from "./BlockNode";
3
+ import ErrorNode from "./ErrorNode";
4
+ import type TextNode from "./TextNode";
5
+ import FootnoteNode from "./FootnoteNode";
6
+ import EmbedNode from "./EmbedNode";
7
+ import ParagraphNode from "./ParagraphNode";
8
+ import type AtomNode from "./AtomNode";
9
+ import LabelNode from "./LabelNode";
10
+ import CommentNode from "./CommentNode";
11
+ import BlocksNode from "./BlocksNode";
12
+ export default class ChapterNode extends BlocksNode {
13
+ #private;
14
+ constructor(blocks: BlockNode[], metadata?: Bookkeeping);
15
+ getType(): string;
16
+ getChapter(): this;
17
+ create(blocks: BlockNode[]): BlocksNode;
18
+ getErrors(): ErrorNode[];
19
+ getCitations(): Set<string>;
20
+ getFootnotes(): FootnoteNode[];
21
+ getHeaders(): ParagraphNode[];
22
+ getEmbeds(): EmbedNode[];
23
+ getComments(): CommentNode[];
24
+ getLabels(): LabelNode[];
25
+ hasLabel(labelID: string): boolean;
26
+ getCitationNumber(citationID: string): number | null;
27
+ toText(): string;
28
+ toBookdown(): string;
29
+ getNextTextOrAtom(node: TextNode | AtomNode<any>): TextNode | AtomNode<any> | undefined;
30
+ getPreviousTextOrAtom(node: TextNode | AtomNode<any>): TextNode | AtomNode<any> | undefined;
31
+ getChildren(): BlockNode[];
32
+ withChildReplaced(node: BlockNode, replacement: BlockNode | undefined): this | undefined;
33
+ copy(): this;
34
+ }
@@ -0,0 +1,80 @@
1
+ import ErrorNode from "./ErrorNode";
2
+ import FootnoteNode from "./FootnoteNode";
3
+ import EmbedNode from "./EmbedNode";
4
+ import ParagraphNode from "./ParagraphNode";
5
+ import CitationsNode from "./CitationsNode";
6
+ import LabelNode from "./LabelNode";
7
+ import CommentNode from "./CommentNode";
8
+ import BlocksNode from "./BlocksNode";
9
+ export default class ChapterNode extends BlocksNode {
10
+ #metadata;
11
+ constructor(blocks, metadata) {
12
+ super(blocks);
13
+ // Content extracted during parsing.
14
+ this.#metadata = metadata === undefined ? { symbols: {} } : metadata;
15
+ }
16
+ getType() { return "chapter"; }
17
+ getChapter() { return this; }
18
+ create(blocks) {
19
+ return new ChapterNode(blocks, this.#metadata);
20
+ }
21
+ getErrors() { return this.getNodes().filter(n => n instanceof ErrorNode); }
22
+ getCitations() {
23
+ const citations = new Set();
24
+ this.getNodes().filter(n => n instanceof CitationsNode).forEach(cites => cites.getMeta().forEach(citationID => citations.add(citationID)));
25
+ return citations;
26
+ }
27
+ getFootnotes() { return this.getNodes().filter(n => n instanceof FootnoteNode); }
28
+ getHeaders() { return this.getNodes().filter(n => n instanceof ParagraphNode && n.getLevel() > 0); }
29
+ getEmbeds() { return this.getNodes().filter(n => n instanceof EmbedNode); }
30
+ getComments() { return this.getNodes().filter(n => n instanceof CommentNode); }
31
+ getLabels() {
32
+ return this.getNodes().filter(n => n instanceof LabelNode);
33
+ }
34
+ hasLabel(labelID) {
35
+ return this.getLabels().find(label => label.getMeta() === labelID) !== undefined;
36
+ }
37
+ getCitationNumber(citationID) {
38
+ const index = Array.from(this.getCitations()).sort().indexOf(citationID);
39
+ return index < 0 ? null : index + 1;
40
+ }
41
+ toText() {
42
+ return this.getBlocks().map(block => block.toText()).join(" ");
43
+ }
44
+ toBookdown() {
45
+ // Render the symbols then all the blocks
46
+ return Object.keys(this.#metadata.symbols).sort().map(name => `@${name}: ${this.#metadata.symbols[name]}\n\n`).join("") +
47
+ this.getBlocks().map(b => b.toBookdown()).join("\n\n");
48
+ }
49
+ getNextTextOrAtom(node) {
50
+ // Otherwise, find the next text node after this one.
51
+ const nodes = this.getTextAndAtomNodes();
52
+ const index = nodes.indexOf(node);
53
+ return index === undefined ? undefined :
54
+ index < nodes.length - 1 ? nodes[index + 1] :
55
+ undefined;
56
+ }
57
+ getPreviousTextOrAtom(node) {
58
+ // Otherwise, find the next text node after this one.
59
+ const nodes = this.getTextAndAtomNodes();
60
+ const index = nodes.indexOf(node);
61
+ return index === undefined ? undefined :
62
+ index > 0 ? nodes[index - 1] :
63
+ undefined;
64
+ }
65
+ getChildren() { return this.getBlocks(); }
66
+ withChildReplaced(node, replacement) {
67
+ const index = this.getBlocks().indexOf(node);
68
+ /// If we couldn't find the requested node, don't change anything.
69
+ if (index < 0)
70
+ return undefined;
71
+ // Make a new blocks node, assigning and re-parenting the replacement.
72
+ const blocks = replacement === undefined ?
73
+ [...this.getBlocks().slice(0, index), ...this.getBlocks().slice(index + 1)] :
74
+ [...this.getBlocks().slice(0, index), replacement, ...this.getBlocks().slice(index + 1)];
75
+ return new ChapterNode(blocks, this.#metadata);
76
+ }
77
+ copy() {
78
+ return new ChapterNode(this.getBlocks().map(b => b.copy()), this.#metadata);
79
+ }
80
+ }
@@ -0,0 +1,16 @@
1
+ import AtomNode from "./AtomNode";
2
+ import type Caret from "./Caret";
3
+ import type Node from "./Node";
4
+ export default class CitationsNode extends AtomNode<string[]> {
5
+ constructor(citations: string[]);
6
+ getType(): string;
7
+ getDefaultCaret(): Caret;
8
+ toText(): string;
9
+ toBookdown(): string;
10
+ toHTML(): string;
11
+ getParentOf(): Node | undefined;
12
+ copy(): this;
13
+ withMeta(citations: string[]): CitationsNode;
14
+ withChildReplaced(): undefined;
15
+ withContentInRange(): this | undefined;
16
+ }
@@ -0,0 +1,17 @@
1
+ import AtomNode from "./AtomNode";
2
+ export default class CitationsNode extends AtomNode {
3
+ constructor(citations) {
4
+ super(citations);
5
+ }
6
+ getType() { return "citations"; }
7
+ getDefaultCaret() { return { node: this, index: 0 }; }
8
+ ;
9
+ toText() { return ""; }
10
+ toBookdown() { return `<${this.getMeta().join(",")}>`; }
11
+ toHTML() { return `(${this.getMeta().join(",")})`; }
12
+ getParentOf() { return undefined; }
13
+ copy() { return new CitationsNode([...this.getMeta()]); }
14
+ withMeta(citations) { return new CitationsNode(citations); }
15
+ withChildReplaced() { return undefined; }
16
+ withContentInRange() { return this.copy(); }
17
+ }
@@ -0,0 +1,30 @@
1
+ import type Node from "./Node";
2
+ import type Position from "./Position";
3
+ import FormatNode from "./FormatNode";
4
+ import TextNode from "./TextNode";
5
+ import BlockNode from "./BlockNode";
6
+ import type { CaretRange } from "./Caret";
7
+ export default class CodeNode extends BlockNode {
8
+ #private;
9
+ constructor(code: TextNode, language: string, position: Position, caption?: FormatNode);
10
+ getType(): string;
11
+ getCodeNode(): TextNode;
12
+ getCode(): string;
13
+ getCaption(): FormatNode;
14
+ getPosition(): Position;
15
+ getLanguage(): string;
16
+ isExecutable(): boolean;
17
+ getFormats(): FormatNode[];
18
+ toText(): string;
19
+ toHTML(): string;
20
+ toBookdown(): string;
21
+ getChildren(): (FormatNode | TextNode)[];
22
+ copy(): this;
23
+ getParentOf(node: Node): Node | undefined;
24
+ withChildReplaced(node: Node, replacement: Node | undefined): this | undefined;
25
+ withPosition(position: Position): CodeNode;
26
+ withLanguage(language: string): CodeNode;
27
+ withExecutable(executable: boolean): CodeNode;
28
+ withCaption(caption: FormatNode): CodeNode;
29
+ withContentInRange(range: CaretRange): this | undefined;
30
+ }