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.
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/components/Auth.svelte +52 -0
- package/components/Auth.svelte.d.ts +16 -0
- package/components/Authentication.d.ts +8 -0
- package/components/Authentication.js +1 -0
- package/components/BookPreview.svelte +87 -0
- package/components/BookPreview.svelte.d.ts +18 -0
- package/components/External.svelte +4 -0
- package/components/External.svelte.d.ts +18 -0
- package/components/Link.svelte +5 -0
- package/components/Link.svelte.d.ts +19 -0
- package/components/chapter/Atom.svelte +36 -0
- package/components/chapter/Atom.svelte.d.ts +19 -0
- package/components/chapter/Block.svelte +32 -0
- package/components/chapter/Block.svelte.d.ts +17 -0
- package/components/chapter/Callout.svelte +10 -0
- package/components/chapter/Callout.svelte.d.ts +17 -0
- package/components/chapter/CaptionedCode.svelte +34 -0
- package/components/chapter/CaptionedCode.svelte.d.ts +17 -0
- package/components/chapter/ChapterBody.svelte +23 -0
- package/components/chapter/ChapterBody.svelte.d.ts +18 -0
- package/components/chapter/Citations.svelte +66 -0
- package/components/chapter/Citations.svelte.d.ts +17 -0
- package/components/chapter/Code.svelte +35 -0
- package/components/chapter/Code.svelte.d.ts +22 -0
- package/components/chapter/Comment.svelte +46 -0
- package/components/chapter/Comment.svelte.d.ts +17 -0
- package/components/chapter/Definition.svelte +38 -0
- package/components/chapter/Definition.svelte.d.ts +20 -0
- package/components/chapter/Embed.svelte +122 -0
- package/components/chapter/Embed.svelte.d.ts +18 -0
- package/components/chapter/ErrorMessage.svelte +4 -0
- package/components/chapter/ErrorMessage.svelte.d.ts +17 -0
- package/components/chapter/Footnote.svelte +37 -0
- package/components/chapter/Footnote.svelte.d.ts +19 -0
- package/components/chapter/Format.svelte +57 -0
- package/components/chapter/Format.svelte.d.ts +18 -0
- package/components/chapter/InlineCode.svelte +12 -0
- package/components/chapter/InlineCode.svelte.d.ts +17 -0
- package/components/chapter/Label.svelte +20 -0
- package/components/chapter/Label.svelte.d.ts +17 -0
- package/components/chapter/Link.svelte +51 -0
- package/components/chapter/Link.svelte.d.ts +17 -0
- package/components/chapter/List.svelte +14 -0
- package/components/chapter/List.svelte.d.ts +17 -0
- package/components/chapter/Marginal.svelte +48 -0
- package/components/chapter/Marginal.svelte.d.ts +19 -0
- package/components/chapter/Paragraph.svelte +15 -0
- package/components/chapter/Paragraph.svelte.d.ts +18 -0
- package/components/chapter/Python.svelte +81 -0
- package/components/chapter/Python.svelte.d.ts +18 -0
- package/components/chapter/Quote.svelte +16 -0
- package/components/chapter/Quote.svelte.d.ts +17 -0
- package/components/chapter/Rule.svelte +4 -0
- package/components/chapter/Rule.svelte.d.ts +17 -0
- package/components/chapter/Table.svelte +29 -0
- package/components/chapter/Table.svelte.d.ts +17 -0
- package/components/chapter/Text.svelte +62 -0
- package/components/chapter/Text.svelte.d.ts +17 -0
- package/components/chapter/renderPosition.d.ts +3 -0
- package/components/chapter/renderPosition.js +4 -0
- package/components/editor/BookishEditor.svelte +755 -0
- package/components/editor/BookishEditor.svelte.d.ts +22 -0
- package/components/editor/CalloutEditor.svelte +10 -0
- package/components/editor/CalloutEditor.svelte.d.ts +17 -0
- package/components/editor/CaptionedCodeEditor.svelte +22 -0
- package/components/editor/CaptionedCodeEditor.svelte.d.ts +17 -0
- package/components/editor/CaretContext.d.ts +18 -0
- package/components/editor/CaretContext.js +1 -0
- package/components/editor/CaretState.d.ts +47 -0
- package/components/editor/CaretState.js +1 -0
- package/components/editor/CaretUtilities.d.ts +5 -0
- package/components/editor/CaretUtilities.js +1 -0
- package/components/editor/CitationsEditor.svelte +42 -0
- package/components/editor/CitationsEditor.svelte.d.ts +17 -0
- package/components/editor/Clipboard.d.ts +3 -0
- package/components/editor/Clipboard.js +1 -0
- package/components/editor/Command.d.ts +18 -0
- package/components/editor/Command.js +1 -0
- package/components/editor/Commands.d.ts +3 -0
- package/components/editor/Commands.js +951 -0
- package/components/editor/ConfirmButton.svelte +43 -0
- package/components/editor/ConfirmButton.svelte.d.ts +18 -0
- package/components/editor/DefinitionEditor.svelte +28 -0
- package/components/editor/DefinitionEditor.svelte.d.ts +17 -0
- package/components/editor/EmbedEditor.svelte +87 -0
- package/components/editor/EmbedEditor.svelte.d.ts +17 -0
- package/components/editor/ImageChooser.svelte +70 -0
- package/components/editor/ImageChooser.svelte.d.ts +18 -0
- package/components/editor/InlineCodeEditor.svelte +9 -0
- package/components/editor/InlineCodeEditor.svelte.d.ts +17 -0
- package/components/editor/LabelEditor.svelte +23 -0
- package/components/editor/LabelEditor.svelte.d.ts +17 -0
- package/components/editor/LanguageEditor.svelte +11 -0
- package/components/editor/LanguageEditor.svelte.d.ts +17 -0
- package/components/editor/LinkEditor.svelte +75 -0
- package/components/editor/LinkEditor.svelte.d.ts +17 -0
- package/components/editor/Note.svelte +9 -0
- package/components/editor/Note.svelte.d.ts +23 -0
- package/components/editor/PositionEditor.svelte +14 -0
- package/components/editor/PositionEditor.svelte.d.ts +18 -0
- package/components/editor/QuoteEditor.svelte +5 -0
- package/components/editor/QuoteEditor.svelte.d.ts +17 -0
- package/components/editor/Switch.svelte +56 -0
- package/components/editor/Switch.svelte.d.ts +20 -0
- package/components/editor/TextEditor.svelte +193 -0
- package/components/editor/TextEditor.svelte.d.ts +23 -0
- package/components/editor/Toggle.svelte +38 -0
- package/components/editor/Toggle.svelte.d.ts +19 -0
- package/components/editor/Toolbar.svelte +202 -0
- package/components/editor/Toolbar.svelte.d.ts +20 -0
- package/components/editor/ToolbarGroup.svelte +41 -0
- package/components/editor/ToolbarGroup.svelte.d.ts +19 -0
- package/components/editor/ToolbarIcon.svelte +22 -0
- package/components/editor/ToolbarIcon.svelte.d.ts +16 -0
- package/components/editor/ToolbarSpacer.svelte +1 -0
- package/components/editor/ToolbarSpacer.svelte.d.ts +19 -0
- package/components/editor/URLEditor.svelte +15 -0
- package/components/editor/URLEditor.svelte.d.ts +18 -0
- package/components/editor/UndoState.d.ts +8 -0
- package/components/editor/UndoState.js +1 -0
- package/components/page/Acknowledgements.svelte +29 -0
- package/components/page/Acknowledgements.svelte.d.ts +14 -0
- package/components/page/Alert.svelte +11 -0
- package/components/page/Alert.svelte.d.ts +23 -0
- package/components/page/Authors.svelte +62 -0
- package/components/page/Authors.svelte.d.ts +20 -0
- package/components/page/BulkReferenceEditor.svelte +32 -0
- package/components/page/BulkReferenceEditor.svelte.d.ts +14 -0
- package/components/page/Chapter.svelte +301 -0
- package/components/page/Chapter.svelte.d.ts +18 -0
- package/components/page/ChapterContext.d.ts +10 -0
- package/components/page/ChapterContext.js +1 -0
- package/components/page/Contexts.d.ts +27 -0
- package/components/page/Contexts.js +21 -0
- package/components/page/DefinitionView.svelte +129 -0
- package/components/page/DefinitionView.svelte.d.ts +18 -0
- package/components/page/Description.svelte +22 -0
- package/components/page/Description.svelte.d.ts +14 -0
- package/components/page/Edition.svelte +106 -0
- package/components/page/Edition.svelte.d.ts +21 -0
- package/components/page/Glossary.svelte +61 -0
- package/components/page/Glossary.svelte.d.ts +14 -0
- package/components/page/Header.svelte +105 -0
- package/components/page/Header.svelte.d.ts +27 -0
- package/components/page/Index.svelte +117 -0
- package/components/page/Index.svelte.d.ts +14 -0
- package/components/page/Instructions.svelte +30 -0
- package/components/page/Instructions.svelte.d.ts +16 -0
- package/components/page/License.svelte +29 -0
- package/components/page/License.svelte.d.ts +14 -0
- package/components/page/Loading.svelte +13 -0
- package/components/page/Loading.svelte.d.ts +19 -0
- package/components/page/Media.svelte +76 -0
- package/components/page/Media.svelte.d.ts +14 -0
- package/components/page/MediaPreview.svelte +11 -0
- package/components/page/MediaPreview.svelte.d.ts +19 -0
- package/components/page/Outline.svelte +173 -0
- package/components/page/Outline.svelte.d.ts +19 -0
- package/components/page/Page.svelte +53 -0
- package/components/page/Page.svelte.d.ts +19 -0
- package/components/page/PossibleChapter.svelte +14 -0
- package/components/page/PossibleChapter.svelte.d.ts +14 -0
- package/components/page/PossibleReference.svelte +16 -0
- package/components/page/PossibleReference.svelte.d.ts +19 -0
- package/components/page/Print.svelte +8 -0
- package/components/page/Print.svelte.d.ts +14 -0
- package/components/page/Reference.svelte +145 -0
- package/components/page/Reference.svelte.d.ts +17 -0
- package/components/page/References.svelte +58 -0
- package/components/page/References.svelte.d.ts +14 -0
- package/components/page/Revisions.svelte +124 -0
- package/components/page/Revisions.svelte.d.ts +14 -0
- package/components/page/Search.svelte +108 -0
- package/components/page/Search.svelte.d.ts +14 -0
- package/components/page/Status.svelte +49 -0
- package/components/page/Status.svelte.d.ts +19 -0
- package/components/page/SubdomainEditor.svelte +30 -0
- package/components/page/SubdomainEditor.svelte.d.ts +14 -0
- package/components/page/TableOfContents.svelte +234 -0
- package/components/page/TableOfContents.svelte.d.ts +14 -0
- package/components/page/TableOfContentsImage.svelte +15 -0
- package/components/page/TableOfContentsImage.svelte.d.ts +16 -0
- package/components/page/TableOfContentsRow.svelte +57 -0
- package/components/page/TableOfContentsRow.svelte.d.ts +24 -0
- package/components/page/ThemeEditor.svelte +52 -0
- package/components/page/ThemeEditor.svelte.d.ts +14 -0
- package/components/page/ThemeEditorPreview.svelte +37 -0
- package/components/page/ThemeEditorPreview.svelte.d.ts +17 -0
- package/components/page/ThemeSetEditor.svelte +17 -0
- package/components/page/ThemeSetEditor.svelte.d.ts +18 -0
- package/components/page/ThemeVariableEditor.svelte +28 -0
- package/components/page/ThemeVariableEditor.svelte.d.ts +18 -0
- package/components/page/Unknown.svelte +29 -0
- package/components/page/Unknown.svelte.d.ts +16 -0
- package/components/page/margins.d.ts +2 -0
- package/components/page/margins.js +132 -0
- package/models/BookLoader.d.ts +2 -0
- package/models/BookLoader.js +67 -0
- package/models/Firebase.d.ts +4 -0
- package/models/Firebase.js +26 -0
- package/models/Firestore.d.ts +22 -0
- package/models/Firestore.js +194 -0
- package/models/book/Book.d.ts +60 -0
- package/models/book/Book.js +135 -0
- package/models/book/BookMedia.d.ts +23 -0
- package/models/book/BookMedia.js +102 -0
- package/models/book/Chapter.d.ts +70 -0
- package/models/book/Chapter.js +221 -0
- package/models/book/ChapterID.d.ts +10 -0
- package/models/book/ChapterID.js +10 -0
- package/models/book/Definition.d.ts +6 -0
- package/models/book/Definition.js +1 -0
- package/models/book/Edition.d.ts +111 -0
- package/models/book/Edition.js +466 -0
- package/models/book/Theme.d.ts +106 -0
- package/models/book/Theme.js +76 -0
- package/models/book/ThemeColors.d.ts +15 -0
- package/models/book/ThemeColors.js +1 -0
- package/models/chapter/AtomNode.d.ts +18 -0
- package/models/chapter/AtomNode.js +31 -0
- package/models/chapter/BlockNode.d.ts +21 -0
- package/models/chapter/BlockNode.js +117 -0
- package/models/chapter/BlocksNode.d.ts +50 -0
- package/models/chapter/BlocksNode.js +709 -0
- package/models/chapter/BlocksNode.test.d.ts +1 -0
- package/models/chapter/BlocksNode.test.js +302 -0
- package/models/chapter/Bookkeeping.d.ts +4 -0
- package/models/chapter/Bookkeeping.js +1 -0
- package/models/chapter/CalloutNode.d.ts +17 -0
- package/models/chapter/CalloutNode.js +36 -0
- package/models/chapter/Caret.d.ts +21 -0
- package/models/chapter/Caret.js +69 -0
- package/models/chapter/ChapterNode.d.ts +34 -0
- package/models/chapter/ChapterNode.js +80 -0
- package/models/chapter/CitationsNode.d.ts +16 -0
- package/models/chapter/CitationsNode.js +17 -0
- package/models/chapter/CodeNode.d.ts +30 -0
- package/models/chapter/CodeNode.js +64 -0
- package/models/chapter/CommentNode.d.ts +18 -0
- package/models/chapter/CommentNode.js +32 -0
- package/models/chapter/DefinitionNode.d.ts +12 -0
- package/models/chapter/DefinitionNode.js +20 -0
- package/models/chapter/Edit.d.ts +7 -0
- package/models/chapter/Edit.js +1 -0
- package/models/chapter/EmbedNode.d.ts +40 -0
- package/models/chapter/EmbedNode.js +89 -0
- package/models/chapter/ErrorNode.d.ts +17 -0
- package/models/chapter/ErrorNode.js +21 -0
- package/models/chapter/FootnoteNode.d.ts +18 -0
- package/models/chapter/FootnoteNode.js +29 -0
- package/models/chapter/FormatNode.d.ts +60 -0
- package/models/chapter/FormatNode.js +614 -0
- package/models/chapter/FormatNode.test.d.ts +1 -0
- package/models/chapter/FormatNode.test.js +54 -0
- package/models/chapter/InlineCodeNode.d.ts +12 -0
- package/models/chapter/InlineCodeNode.js +18 -0
- package/models/chapter/LabelNode.d.ts +17 -0
- package/models/chapter/LabelNode.js +22 -0
- package/models/chapter/LinkNode.d.ts +12 -0
- package/models/chapter/LinkNode.js +16 -0
- package/models/chapter/ListNode.d.ts +51 -0
- package/models/chapter/ListNode.js +370 -0
- package/models/chapter/ListNode.test.d.ts +1 -0
- package/models/chapter/ListNode.test.js +49 -0
- package/models/chapter/MetadataNode.d.ts +17 -0
- package/models/chapter/MetadataNode.js +24 -0
- package/models/chapter/Node.d.ts +36 -0
- package/models/chapter/Node.js +173 -0
- package/models/chapter/Node.test.d.ts +1 -0
- package/models/chapter/Node.test.js +27 -0
- package/models/chapter/ParagraphNode.d.ts +33 -0
- package/models/chapter/ParagraphNode.js +93 -0
- package/models/chapter/ParagraphNode.test.d.ts +1 -0
- package/models/chapter/ParagraphNode.test.js +21 -0
- package/models/chapter/Parser.d.ts +75 -0
- package/models/chapter/Parser.js +824 -0
- package/models/chapter/Parser.test.d.ts +1 -0
- package/models/chapter/Parser.test.js +25 -0
- package/models/chapter/Position.d.ts +2 -0
- package/models/chapter/Position.js +1 -0
- package/models/chapter/QuoteNode.d.ts +25 -0
- package/models/chapter/QuoteNode.js +61 -0
- package/models/chapter/ReferenceNode.d.ts +29 -0
- package/models/chapter/ReferenceNode.js +48 -0
- package/models/chapter/RootNode.d.ts +5 -0
- package/models/chapter/RootNode.js +1 -0
- package/models/chapter/RuleNode.d.ts +15 -0
- package/models/chapter/RuleNode.js +16 -0
- package/models/chapter/TableNode.d.ts +35 -0
- package/models/chapter/TableNode.js +176 -0
- package/models/chapter/TableNode.test.d.ts +1 -0
- package/models/chapter/TableNode.test.js +31 -0
- package/models/chapter/TextNode.d.ts +36 -0
- package/models/chapter/TextNode.js +136 -0
- package/models/chapter/TextNode.test.d.ts +1 -0
- package/models/chapter/TextNode.test.js +6 -0
- package/models/loadEdition.d.ts +5 -0
- package/models/loadEdition.js +16 -0
- package/package.json +189 -0
- package/util/getSubdomain.d.ts +3 -0
- package/util/getSubdomain.js +22 -0
- package/util/isMobile.d.ts +2 -0
- package/util/isMobile.js +6 -0
- package/util/mineReference.d.ts +3 -0
- package/util/mineReference.js +62 -0
- package/util/scrollToEyeLevel.d.ts +2 -0
- package/util/scrollToEyeLevel.js +5 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// A global node ID generator, for mapping views back to models.
|
|
2
|
+
let nodeID = 1;
|
|
3
|
+
export default class Node {
|
|
4
|
+
// A globally unique identifier, helpful for mapping things back to nodes.
|
|
5
|
+
nodeID;
|
|
6
|
+
constructor() {
|
|
7
|
+
this.nodeID = nodeID++;
|
|
8
|
+
}
|
|
9
|
+
getID() { return this.nodeID; }
|
|
10
|
+
traverse(fn, parents) {
|
|
11
|
+
const path = parents === undefined ? [this] : [...parents, this];
|
|
12
|
+
this.traverseChildren(fn, path);
|
|
13
|
+
fn.call(undefined, this, path);
|
|
14
|
+
}
|
|
15
|
+
// Traverses each of the children of this node.
|
|
16
|
+
traverseChildren(fn, parents) {
|
|
17
|
+
const children = this.getChildren();
|
|
18
|
+
children.forEach(child => child.traverse(fn, parents));
|
|
19
|
+
}
|
|
20
|
+
// Depth first sequence of all nodes in this tree.
|
|
21
|
+
getNodes() {
|
|
22
|
+
const nodes = [];
|
|
23
|
+
this.traverse(node => nodes.push(node));
|
|
24
|
+
return nodes;
|
|
25
|
+
}
|
|
26
|
+
// Swap them order if this is two text nodes that are reversed.
|
|
27
|
+
sortRange(range) {
|
|
28
|
+
const start = range.start.node;
|
|
29
|
+
const end = range.end.node;
|
|
30
|
+
if (start === end)
|
|
31
|
+
return {
|
|
32
|
+
start: { node: start, index: Math.min(range.start.index, range.end.index) },
|
|
33
|
+
end: { node: end, index: Math.max(range.start.index, range.end.index) }
|
|
34
|
+
};
|
|
35
|
+
// Find the common ancestor of the two nodes.
|
|
36
|
+
const ancestor = start.getCommonAncestor(this, end);
|
|
37
|
+
if (ancestor === undefined)
|
|
38
|
+
return range;
|
|
39
|
+
// Where do these text nodes appear in the ancestor's node sequence?
|
|
40
|
+
let startIndex = ancestor.getNodes().indexOf(start);
|
|
41
|
+
let endIndex = ancestor.getNodes().indexOf(end);
|
|
42
|
+
// Defensively verify that we could find the given nodes in the document.
|
|
43
|
+
// If we can't, something is wrong upstream.
|
|
44
|
+
if (startIndex < 0 || endIndex < 0)
|
|
45
|
+
throw Error(`Couldn't find caret range node(s) in this tree.`);
|
|
46
|
+
// If we didn't find them, or the start is before the end, return the given range.
|
|
47
|
+
return startIndex === undefined || endIndex === undefined || startIndex < endIndex ?
|
|
48
|
+
range :
|
|
49
|
+
// If they're the same node, order the index.
|
|
50
|
+
startIndex === endIndex ?
|
|
51
|
+
{
|
|
52
|
+
start: {
|
|
53
|
+
node: range.start.node,
|
|
54
|
+
index: Math.min(range.start.index, range.end.index)
|
|
55
|
+
},
|
|
56
|
+
end: {
|
|
57
|
+
node: range.end.node,
|
|
58
|
+
index: Math.max(range.start.index, range.end.index)
|
|
59
|
+
}
|
|
60
|
+
} :
|
|
61
|
+
// Otherwise, swap the caret positions
|
|
62
|
+
{ start: range.end, end: range.start };
|
|
63
|
+
}
|
|
64
|
+
getNode(id) { return this.getNodes().find(n => n.getID() === id); }
|
|
65
|
+
// An index of the given node in the depth-first traversal of the nodes.
|
|
66
|
+
getIndexOf(node) {
|
|
67
|
+
const index = this.getNodes().indexOf(node);
|
|
68
|
+
return index >= 0 ? index : undefined;
|
|
69
|
+
}
|
|
70
|
+
// True if this tree contains the given node.
|
|
71
|
+
contains(node) {
|
|
72
|
+
let found = false;
|
|
73
|
+
this.traverse(n => { if (n === node)
|
|
74
|
+
found = true; });
|
|
75
|
+
return found;
|
|
76
|
+
}
|
|
77
|
+
// Returns a list of the given node's parents in this tree, from root to direct parent.
|
|
78
|
+
getParentsOf(node) {
|
|
79
|
+
let parents = undefined;
|
|
80
|
+
this.traverse((n, p) => { if (n === node)
|
|
81
|
+
parents = p; });
|
|
82
|
+
if (parents === undefined)
|
|
83
|
+
return;
|
|
84
|
+
const withoutNode = [...parents];
|
|
85
|
+
withoutNode.pop();
|
|
86
|
+
return withoutNode;
|
|
87
|
+
}
|
|
88
|
+
getParent(root) { return root.getParentOf(this); }
|
|
89
|
+
hasAncestor(root, node) {
|
|
90
|
+
let parent = root.getParentOf(this);
|
|
91
|
+
while (parent) {
|
|
92
|
+
if (parent === node)
|
|
93
|
+
return true;
|
|
94
|
+
parent = root.getParentOf(parent);
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
getClosestParentOfType(root, type) {
|
|
99
|
+
let parent = root.getParentOf(this);
|
|
100
|
+
while (parent) {
|
|
101
|
+
if (parent instanceof type)
|
|
102
|
+
return parent;
|
|
103
|
+
parent = root.getParentOf(parent);
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
isInside(root, type) {
|
|
108
|
+
return this.getClosestParentMatching(root, p => p instanceof type) !== undefined;
|
|
109
|
+
}
|
|
110
|
+
getClosestParentMatching(root, match) {
|
|
111
|
+
let parent = root.getParentOf(this);
|
|
112
|
+
while (parent) {
|
|
113
|
+
if (match.call(undefined, parent))
|
|
114
|
+
return parent;
|
|
115
|
+
parent = root.getParentOf(parent);
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
getFarthestParentMatching(root, match) {
|
|
120
|
+
const matchingParents = this.getAncestors(root).filter(match);
|
|
121
|
+
return matchingParents.length > 0 ? matchingParents[matchingParents.length - 1] : undefined;
|
|
122
|
+
}
|
|
123
|
+
getAncestors(root) {
|
|
124
|
+
const ancestors = [];
|
|
125
|
+
let parent = root.getParentOf(this);
|
|
126
|
+
while (parent) {
|
|
127
|
+
ancestors.push(parent);
|
|
128
|
+
parent = root.getParentOf(parent);
|
|
129
|
+
}
|
|
130
|
+
return ancestors;
|
|
131
|
+
}
|
|
132
|
+
getCommonAncestor(root, node) {
|
|
133
|
+
const thisParents = this.getAncestors(root);
|
|
134
|
+
const thatParents = node.getAncestors(root);
|
|
135
|
+
for (let i = 0; i < thisParents.length; i++) {
|
|
136
|
+
for (let j = 0; j < thatParents.length; j++) {
|
|
137
|
+
if (thisParents[i] === thatParents[j])
|
|
138
|
+
return thisParents[i];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
// Traverse this node's children, trying to replace the given node with the replacement node (or nothing, if allowed)
|
|
144
|
+
withNodeReplaced(node, replacement) {
|
|
145
|
+
// Is this the node we're replacing? Return the replacement.
|
|
146
|
+
if (node === this)
|
|
147
|
+
return replacement;
|
|
148
|
+
// Find all the children
|
|
149
|
+
const children = this.getChildren();
|
|
150
|
+
// Search each one for a match.
|
|
151
|
+
for (let i = 0; i < children.length; i++) {
|
|
152
|
+
const child = children[i];
|
|
153
|
+
// If we found the child, return a new version of this node with the child replaced.
|
|
154
|
+
if (child === node)
|
|
155
|
+
return this.withChildReplaced(node, replacement);
|
|
156
|
+
// Does this child have it? If so, return a version of this node with the revised child.
|
|
157
|
+
const revisedChild = child.withNodeReplaced(node, replacement);
|
|
158
|
+
if (revisedChild !== undefined)
|
|
159
|
+
return this.withChildReplaced(child, revisedChild);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// By default, a node doesn't know how to insert anything into itself. Subclasses
|
|
163
|
+
// are responsible for overriding this if they want to support specific insertions.
|
|
164
|
+
withNodeInserted(caret, node) { caret; node; return undefined; }
|
|
165
|
+
copyRange(range) {
|
|
166
|
+
const sortedRange = this.sortRange(range);
|
|
167
|
+
// Find the common ancestor of the range, then ask it to copy the portion of it selected and produce a node.
|
|
168
|
+
const commonAncestor = sortedRange.start.node.getCommonAncestor(this, sortedRange.end.node);
|
|
169
|
+
if (commonAncestor === undefined)
|
|
170
|
+
return;
|
|
171
|
+
return commonAncestor.withContentInRange(sortedRange);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { test, expect } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import TextNode from "./TextNode";
|
|
4
|
+
import FormatNode from "./FormatNode";
|
|
5
|
+
|
|
6
|
+
const textOne = new TextNode("one");
|
|
7
|
+
const textTwo = new TextNode("two");
|
|
8
|
+
const textThree = new TextNode("three");
|
|
9
|
+
const textFour = new TextNode("four");
|
|
10
|
+
const textFive = new TextNode("five");
|
|
11
|
+
const bold = new FormatNode("*", [ textThree, textFour ]);
|
|
12
|
+
const format = new FormatNode("", [ textOne, textTwo, bold, textFive ]);
|
|
13
|
+
|
|
14
|
+
test("Replace descendants", () => {
|
|
15
|
+
expect(format.withNodeReplaced(textThree, new TextNode("hi"))?.toBookdown())
|
|
16
|
+
.toBe("onetwo*hifour*five")
|
|
17
|
+
|
|
18
|
+
expect(format.withNodeReplaced(textFive, new TextNode("yo"))?.toBookdown())
|
|
19
|
+
.toBe("onetwo*threefour*yo")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("Parents", () => {
|
|
23
|
+
const parents = format.getParentsOf(textThree);
|
|
24
|
+
expect(parents?.length).toBe(2);
|
|
25
|
+
expect(parents ? parents[0] : undefined).toBe(format);
|
|
26
|
+
expect(parents ? parents[1] : undefined).toBe(bold);
|
|
27
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type Node from "./Node";
|
|
2
|
+
import BlockNode from "./BlockNode";
|
|
3
|
+
import FormatNode from "./FormatNode";
|
|
4
|
+
import TextNode from "./TextNode";
|
|
5
|
+
import type Caret from "./Caret";
|
|
6
|
+
import type { CaretRange } from "./Caret";
|
|
7
|
+
export default class ParagraphNode extends BlockNode {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(level?: number, format?: FormatNode);
|
|
10
|
+
getType(): string;
|
|
11
|
+
getLevel(): number;
|
|
12
|
+
getFormat(): FormatNode;
|
|
13
|
+
getFormats(): FormatNode[];
|
|
14
|
+
getChildren(): FormatNode[];
|
|
15
|
+
getFirstTextNode(): TextNode;
|
|
16
|
+
getLastTextNode(): TextNode;
|
|
17
|
+
getTextNodes(): TextNode[];
|
|
18
|
+
getFirstCaret(): Caret | undefined;
|
|
19
|
+
getLastCaret(): Caret | undefined;
|
|
20
|
+
getSelection(): CaretRange;
|
|
21
|
+
getParentOf(node: Node): Node | undefined;
|
|
22
|
+
toText(): string;
|
|
23
|
+
toBookdown(): string;
|
|
24
|
+
toHTML(): string;
|
|
25
|
+
withLevel(level: number): ParagraphNode;
|
|
26
|
+
withContent(content: FormatNode): ParagraphNode;
|
|
27
|
+
withParagraphAppended(paragraph: ParagraphNode): ParagraphNode;
|
|
28
|
+
withParagraphPrepended(paragraph: ParagraphNode): ParagraphNode;
|
|
29
|
+
split(caret: Caret): [ParagraphNode, ParagraphNode] | undefined;
|
|
30
|
+
copy(): this;
|
|
31
|
+
withChildReplaced(node: Node, replacement: Node | undefined): this | undefined;
|
|
32
|
+
withContentInRange(range: CaretRange): this | undefined;
|
|
33
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import BlockNode from "./BlockNode";
|
|
2
|
+
import FormatNode from "./FormatNode";
|
|
3
|
+
import TextNode from "./TextNode";
|
|
4
|
+
import AtomNode from "./AtomNode";
|
|
5
|
+
import MetadataNode from "./MetadataNode";
|
|
6
|
+
export default class ParagraphNode extends BlockNode {
|
|
7
|
+
#format;
|
|
8
|
+
#level;
|
|
9
|
+
constructor(level = 0, format) {
|
|
10
|
+
super();
|
|
11
|
+
// Always ensure the paragraphs have an empty space at the begining and end for insertion.
|
|
12
|
+
let adjustedFormat = format ?? new FormatNode("", [new TextNode()]);
|
|
13
|
+
if (adjustedFormat.getLength() > 0) {
|
|
14
|
+
const first = adjustedFormat.getSegments()[0];
|
|
15
|
+
const last = adjustedFormat.getSegments()[adjustedFormat.getLength() - 1];
|
|
16
|
+
if (first instanceof AtomNode || first instanceof MetadataNode)
|
|
17
|
+
adjustedFormat = adjustedFormat.withSegmentPrepended(new TextNode());
|
|
18
|
+
if (last instanceof AtomNode || last instanceof MetadataNode)
|
|
19
|
+
adjustedFormat = adjustedFormat.withSegmentAppended(new TextNode());
|
|
20
|
+
}
|
|
21
|
+
// Always ensure a paragraph has at least an empty text node.
|
|
22
|
+
adjustedFormat = adjustedFormat.withTextIfEmpty();
|
|
23
|
+
this.#format = adjustedFormat;
|
|
24
|
+
this.#level = level;
|
|
25
|
+
}
|
|
26
|
+
getType() { return "paragraph"; }
|
|
27
|
+
getLevel() { return this.#level; }
|
|
28
|
+
getFormat() { return this.#format; }
|
|
29
|
+
getFormats() { return [this.#format]; }
|
|
30
|
+
getChildren() { return [this.#format]; }
|
|
31
|
+
getFirstTextNode() { return this.getFormat().getFirstTextNode(); }
|
|
32
|
+
getLastTextNode() { return this.getFormat().getLastTextNode(); }
|
|
33
|
+
getTextNodes() { return this.getNodes().filter(n => n instanceof TextNode); }
|
|
34
|
+
getFirstCaret() { return this.#format.getFirstCaret(); }
|
|
35
|
+
getLastCaret() { return this.#format.getLastCaret(); }
|
|
36
|
+
getSelection() {
|
|
37
|
+
const first = this.getFirstTextNode();
|
|
38
|
+
const last = this.getLastTextNode();
|
|
39
|
+
return { start: { node: first, index: 0 }, end: { node: last, index: last.getLength() } };
|
|
40
|
+
}
|
|
41
|
+
getParentOf(node) { return node === this.#format ? this : this.#format.getParentOf(node); }
|
|
42
|
+
toText() {
|
|
43
|
+
return this.#format.toText();
|
|
44
|
+
}
|
|
45
|
+
toBookdown() {
|
|
46
|
+
return (this.#level === 1 ? "# " : this.#level === 2 ? "## " : this.#level === 3 ? "### " : "") + this.#format.toBookdown();
|
|
47
|
+
}
|
|
48
|
+
toHTML() {
|
|
49
|
+
const tag = this.#level === 1 ? "h1" : this.#level === 2 ? "h2" : this.#level === 3 ? "h2" : "p";
|
|
50
|
+
return `<${tag}>${this.#format.toHTML()}</${tag}>`;
|
|
51
|
+
}
|
|
52
|
+
withLevel(level) { return new ParagraphNode(level, this.#format); }
|
|
53
|
+
withContent(content) { return new ParagraphNode(this.#level, content); }
|
|
54
|
+
withParagraphAppended(paragraph) {
|
|
55
|
+
return new ParagraphNode(this.#level, new FormatNode("", [...this.#format.getSegments(), ...paragraph.#format.getSegments()]));
|
|
56
|
+
}
|
|
57
|
+
withParagraphPrepended(paragraph) {
|
|
58
|
+
return new ParagraphNode(this.#level, new FormatNode("", [...paragraph.#format.getSegments(), ...this.#format.getSegments(),]));
|
|
59
|
+
}
|
|
60
|
+
split(caret) {
|
|
61
|
+
// If we weren't given a text node, do nothing.
|
|
62
|
+
if (!(caret.node instanceof TextNode))
|
|
63
|
+
return;
|
|
64
|
+
// If this caret isn't in this paragraph, do nothing.
|
|
65
|
+
if (this.getParentOf(caret.node) === undefined)
|
|
66
|
+
return;
|
|
67
|
+
// If the caret is at the last position of the paragraph, insert a new empty paragraph.
|
|
68
|
+
const lastTextNode = this.getLastTextNode();
|
|
69
|
+
if (caret.node === lastTextNode && caret.index === lastTextNode.getLength())
|
|
70
|
+
return [this.copy(), new ParagraphNode()];
|
|
71
|
+
// If the caret is in the first position of the paragraph, insert a new empty paragraph before.
|
|
72
|
+
const firstTextNode = this.getFirstTextNode();
|
|
73
|
+
if (caret.node === firstTextNode && caret.index === 0)
|
|
74
|
+
return [new ParagraphNode(), this.copy()];
|
|
75
|
+
// Otherwise, split the paragraph in two, with the caret at the beginning of the second.
|
|
76
|
+
const before = this.#format.withoutContentAfter(caret);
|
|
77
|
+
const after = this.#format.withoutContentBefore(caret);
|
|
78
|
+
if (after === undefined || before === undefined)
|
|
79
|
+
return;
|
|
80
|
+
return [new ParagraphNode(this.#level, before), new ParagraphNode(this.#level, after)];
|
|
81
|
+
}
|
|
82
|
+
copy() { return new ParagraphNode(this.#level, this.#format.copy()); }
|
|
83
|
+
withChildReplaced(node, replacement) {
|
|
84
|
+
return node === this.#format && (replacement instanceof FormatNode || replacement === undefined) ?
|
|
85
|
+
new ParagraphNode(this.#level, replacement) : undefined;
|
|
86
|
+
}
|
|
87
|
+
withContentInRange(range) {
|
|
88
|
+
const newFormat = this.#format.withContentInRange(range);
|
|
89
|
+
if (newFormat === undefined)
|
|
90
|
+
return;
|
|
91
|
+
return new ParagraphNode(this.#level, newFormat);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { test, expect } from 'vitest';
|
|
2
|
+
import FootnoteNode from "./FootnoteNode";
|
|
3
|
+
import FormatNode from "./FormatNode";
|
|
4
|
+
import ParagraphNode from "./ParagraphNode";
|
|
5
|
+
import TextNode from "./TextNode";
|
|
6
|
+
const firstText = new TextNode("This is the first paragraph. What do you think of it?");
|
|
7
|
+
const lastText = new TextNode("This is the second paragraph. I think it's redundant.");
|
|
8
|
+
const combinedText = new TextNode(firstText.getText() + lastText.getText());
|
|
9
|
+
const footnote = new FootnoteNode(new FormatNode("", [new TextNode("Side note!")]));
|
|
10
|
+
const firstParagraph = new ParagraphNode(0, new FormatNode("", [firstText, footnote]));
|
|
11
|
+
const lastParagraph = new ParagraphNode(0, new FormatNode("", [lastText]));
|
|
12
|
+
const combinedParagraph = new ParagraphNode(0, new FormatNode("", [combinedText, footnote]));
|
|
13
|
+
test("Combine/split", () => {
|
|
14
|
+
expect(firstParagraph.withParagraphAppended(lastParagraph).toBookdown()).toBe(`${firstText.getText()}${footnote.toBookdown()}${lastText.getText()}`);
|
|
15
|
+
const split = combinedParagraph.split({ node: combinedText, index: combinedText.getText().indexOf(".") + 1 });
|
|
16
|
+
expect(split).not.toBeUndefined();
|
|
17
|
+
if (split) {
|
|
18
|
+
expect(split[0].toBookdown()).toBe(firstText.getText().substring(0, firstText.getText().indexOf(".") + 1));
|
|
19
|
+
expect(split[1].toBookdown()).toBe(firstText.getText().substring(firstText.getText().indexOf(".") + 1) + lastText.getText() + footnote.toBookdown());
|
|
20
|
+
}
|
|
21
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type Edition from "../book/Edition";
|
|
2
|
+
import CalloutNode from "./CalloutNode";
|
|
3
|
+
import ChapterNode from "./ChapterNode";
|
|
4
|
+
import CitationsNode from "./CitationsNode";
|
|
5
|
+
import CodeNode from "./CodeNode";
|
|
6
|
+
import CommentNode from "./CommentNode";
|
|
7
|
+
import DefinitionNode from "./DefinitionNode";
|
|
8
|
+
import EmbedNode from "./EmbedNode";
|
|
9
|
+
import ErrorNode from "./ErrorNode";
|
|
10
|
+
import FootnoteNode from "./FootnoteNode";
|
|
11
|
+
import FormatNode from "./FormatNode";
|
|
12
|
+
import InlineCodeNode from "./InlineCodeNode";
|
|
13
|
+
import LabelNode from "./LabelNode";
|
|
14
|
+
import LinkNode from "./LinkNode";
|
|
15
|
+
import ListNode from "./ListNode";
|
|
16
|
+
import ParagraphNode from "./ParagraphNode";
|
|
17
|
+
import QuoteNode from "./QuoteNode";
|
|
18
|
+
import ReferenceNode from "./ReferenceNode";
|
|
19
|
+
import RuleNode from "./RuleNode";
|
|
20
|
+
import TableNode from "./TableNode";
|
|
21
|
+
import TextNode from "./TextNode";
|
|
22
|
+
import type Position from "./Position";
|
|
23
|
+
import type BlockNode from "./BlockNode";
|
|
24
|
+
import type Bookkeeping from "./Bookkeeping";
|
|
25
|
+
export default class Parser {
|
|
26
|
+
book: Edition | undefined;
|
|
27
|
+
text: string;
|
|
28
|
+
index: number;
|
|
29
|
+
openedDoubleQuote: boolean;
|
|
30
|
+
metadata: Bookkeeping;
|
|
31
|
+
constructor(book: Edition | undefined, text: string);
|
|
32
|
+
static preprocessSymbols(book: Edition, text: string): string;
|
|
33
|
+
static parseChapter(book: Edition | undefined, text: string): ChapterNode;
|
|
34
|
+
static parseFormat(book: Edition | undefined, text: string): FormatNode;
|
|
35
|
+
static parseEmbed(book: Edition | undefined, text: string): EmbedNode | ErrorNode;
|
|
36
|
+
static parseReference(citationID: string, ref: string | Array<string>, edition: Edition, short?: boolean): FormatNode | ErrorNode | ReferenceNode;
|
|
37
|
+
peek(): string | null;
|
|
38
|
+
more(): boolean;
|
|
39
|
+
read(smarten?: boolean): string | null;
|
|
40
|
+
unread(): void;
|
|
41
|
+
rest(): string;
|
|
42
|
+
charBeforeNext(): string | null;
|
|
43
|
+
charAfterNext(): string | null;
|
|
44
|
+
restOfLine(): string;
|
|
45
|
+
nextIs(text: string): boolean;
|
|
46
|
+
nextIsContentDelimiter(): boolean;
|
|
47
|
+
nextMatches(regex: RegExp): boolean;
|
|
48
|
+
isBlankLine(): boolean;
|
|
49
|
+
readWhitespace(): void;
|
|
50
|
+
readUntilNewLine(): string;
|
|
51
|
+
readUntilNewlineOr(string: string): string;
|
|
52
|
+
parseChapter(): ChapterNode;
|
|
53
|
+
parseSymbols(): void;
|
|
54
|
+
parseBlock(): BlockNode;
|
|
55
|
+
parseParagraph(): ParagraphNode;
|
|
56
|
+
parseHeader(): ParagraphNode;
|
|
57
|
+
parseRule(): RuleNode;
|
|
58
|
+
parseList(): ListNode;
|
|
59
|
+
parseCode(): CodeNode;
|
|
60
|
+
parsePosition(): Position;
|
|
61
|
+
parseQuote(): QuoteNode;
|
|
62
|
+
parseCallout(): CalloutNode;
|
|
63
|
+
parseTable(): TableNode;
|
|
64
|
+
parseFormat(awaiting?: string): FormatNode;
|
|
65
|
+
parseEmbed(): EmbedNode | ErrorNode;
|
|
66
|
+
parseComment(): CommentNode;
|
|
67
|
+
parseLabel(): LabelNode | ErrorNode;
|
|
68
|
+
parseStyle(awaiting: string): FormatNode | ErrorNode;
|
|
69
|
+
parseInlineCode(): InlineCodeNode;
|
|
70
|
+
parseCitations(): CitationsNode;
|
|
71
|
+
parseFootnote(): FootnoteNode;
|
|
72
|
+
parseDefinition(): DefinitionNode;
|
|
73
|
+
parseEscaped(): TextNode | ErrorNode;
|
|
74
|
+
parseLink(): LinkNode | ErrorNode;
|
|
75
|
+
}
|