extwee 2.0.6 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +25 -25
- package/.github/FUNDING.yml +3 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/nodejs.yml +25 -24
- package/.travis.yml +13 -13
- package/CODE_OF_CONDUCT.md +82 -82
- package/LICENSE +21 -21
- package/README.md +173 -36
- package/SECURITY.md +12 -12
- package/babel.config.json +18 -22
- package/build/extwee +0 -0
- package/build/extwee.exe +0 -0
- package/build/extwee.web.min.js +2 -0
- package/build/extwee.web.min.js.LICENSE.txt +1 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +167 -0
- package/docs/_sidebar.md +20 -0
- package/docs/examples/dynamicPassages.md +28 -0
- package/docs/examples/jsonToTwee.md +23 -0
- package/docs/examples/twsToTwee.md +25 -0
- package/docs/formats/json.md +17 -0
- package/docs/formats/twee.md +13 -0
- package/docs/formats/twine1HTML.md +13 -0
- package/docs/formats/twine2ArchiveHTML.md +13 -0
- package/docs/formats/twine2HTML.md +13 -0
- package/docs/formats/tws.md +9 -0
- package/docs/index.html +26 -0
- package/docs/install/binaries.md +9 -0
- package/docs/install/npm.md +20 -0
- package/docs/install/npx.md +9 -0
- package/docs/objects/passage.md +47 -0
- package/docs/objects/story.md +70 -0
- package/docs/objects/storyformat.md +27 -0
- package/index.html +22 -0
- package/index.js +29 -31
- package/package.json +68 -58
- package/src/JSON/parse.js +128 -0
- package/src/Passage.js +279 -202
- package/src/Story.js +653 -523
- package/src/StoryFormat/parse.js +134 -0
- package/src/StoryFormat.js +259 -300
- package/src/TWS/parse.js +86 -0
- package/src/Twee/parse.js +157 -0
- package/src/Twine1HTML/compile.js +58 -0
- package/src/Twine1HTML/parse.js +134 -0
- package/src/Twine2ArchiveHTML/compile.js +36 -0
- package/src/Twine2ArchiveHTML/parse.js +49 -0
- package/src/Twine2HTML/compile.js +35 -0
- package/src/Twine2HTML/parse.js +349 -0
- package/src/extwee.js +206 -0
- package/test/CLI/CLI.test.js +49 -0
- package/test/CLI/files/example.json +1 -0
- package/test/CLI/files/example6.twee +22 -0
- package/test/{Roundtrip → CLI/files}/harlowe.js +2 -2
- package/test/CLI/{input.html → files/input.html} +47 -47
- package/test/CLI/files/output/test.twee +0 -0
- package/test/CLI/{tweeExample.twee → files/tweeExample.twee} +17 -17
- package/test/CLI/files/twine1/LICENSE.txt +32 -0
- package/test/CLI/files/twine1/code.js +5 -0
- package/test/CLI/files/twine1/engine.js +43 -0
- package/test/CLI/files/twine1/header.html +325 -0
- package/test/CLI/files/twine1Test.html +371 -0
- package/test/CLI/{twineExample.html → files/twineExample.html} +16 -15
- package/test/JSON/JSON.Parse.test.js +316 -0
- package/test/Passage.test.js +244 -104
- package/test/Roundtrip/{Example1.html → Files/Example1.html} +63 -63
- package/test/Roundtrip/Files/LICENSE +19 -0
- package/test/Roundtrip/Files/example1.twee +10 -0
- package/test/Roundtrip/{example2.twee → Files/example2.twee} +27 -18
- package/test/Roundtrip/{example4.twee → Files/example4.twee} +27 -27
- package/test/{StoryFormatParser → Roundtrip/Files}/harlowe.js +2 -2
- package/test/Roundtrip/{round.html → Files/round.html} +6 -4
- package/test/Roundtrip/Roundtrip.test.js +54 -0
- package/test/Story.test.js +677 -423
- package/test/StoryFormat/StoryFormat.Parse.test.js +91 -0
- package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example.js +3 -3
- package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example2.js +3 -3
- package/test/{CLI → StoryFormat/StoryFormatParser}/harlowe.js +2 -2
- package/test/StoryFormat.test.js +152 -152
- package/test/TWS/Parse.test.js +78 -0
- package/test/TWS/TWSParser/Example1.tws +150 -0
- package/test/TWS/TWSParser/Example5.tws +414 -0
- package/test/TWS/TWSParser/noscale.tws +0 -0
- package/test/TWS/TWSParser/nostory.tws +0 -0
- package/test/Twee/Twee.Parse.test.js +76 -0
- package/test/{TweeParser → Twee/TweeParser}/emptytags.twee +2 -2
- package/test/{TweeParser → Twee/TweeParser}/example.twee +32 -32
- package/test/{TweeParser → Twee/TweeParser}/missing.twee +19 -19
- package/test/{TweeParser → Twee/TweeParser}/multipleScriptPassages.twee +19 -19
- package/test/{TweeParser → Twee/TweeParser}/multipleStyleTag.twee +19 -19
- package/test/{TweeParser → Twee/TweeParser}/multipletags.twee +10 -10
- package/test/{TweeParser → Twee/TweeParser}/noTitle.twee +2 -2
- package/test/{TweeParser → Twee/TweeParser}/notes.twee +16 -16
- package/test/{TweeParser → Twee/TweeParser}/pasagemetadataerror.twee +2 -2
- package/test/{TweeParser → Twee/TweeParser}/scriptPassage.twee +16 -16
- package/test/{TweeParser → Twee/TweeParser}/singletag.twee +13 -13
- package/test/{TweeParser → Twee/TweeParser}/startMetadata.twee +14 -14
- package/test/{TweeParser → Twee/TweeParser}/storydataerror.twee +25 -25
- package/test/{TweeParser → Twee/TweeParser}/stylePassage.twee +16 -16
- package/test/{Story → Twee/TweeParser}/test.twee +25 -25
- package/test/Twine1HTML/Twine1HTML.Compile.test.js +180 -0
- package/test/Twine1HTML/Twine1HTML.Parse.test.js +183 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/LICENSE +674 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/engine.js +43 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/jquery.js +4 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/modernizr.js +4 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/engineTest.html +1 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/LICENSE +32 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/code.js +4 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/header.html +327 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test.html +0 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test1.html +6 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test2.html +6 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test3.html +43 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test4.html +372 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test5.html +372 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Compile.test.js +35 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +25 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLCompiler/test1.html +6 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLParser/test1.html +3 -0
- package/test/Twine2HTML/Twine2HTML.Compile.test.js +224 -0
- package/test/Twine2HTML/Twine2HTML.Parse.test.js +173 -0
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/creator.html +4 -4
- package/test/{CLI → Twine2HTML/Twine2HTMLCompiler}/example6.twee +15 -15
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/missingStoryTitle.twee +29 -29
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test2.html +10 -8
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test3.html +1 -1
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test4.html +4 -4
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Example1.html +52 -52
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Tags.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingStartnode.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingTagColors.html +48 -48
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreator.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreatorVersion.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormat.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormatVersion.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingIFID.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingName.html +33 -33
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPID.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageName.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageTags.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPosition.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingScript.html +14 -14
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingSize.html +35 -35
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStartnode.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStyle.html +14 -14
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingZoom.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample.html +23 -23
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample2.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample3.html +15 -15
- package/test/Twine2HTML/Twine2HTMLParser/unescaping.html +33 -0
- package/tsconfig.json +21 -0
- package/types/index.d.ts +14 -0
- package/types/src/JSON/parse.d.ts +8 -0
- package/types/src/Passage.d.ts +72 -0
- package/types/src/Story.d.ts +161 -0
- package/types/src/StoryFormat/parse.d.ts +7 -0
- package/types/src/StoryFormat.d.ts +97 -0
- package/types/src/TWS/parse.d.ts +10 -0
- package/types/src/Twee/parse.d.ts +10 -0
- package/types/src/Twine1HTML/compile.d.ts +17 -0
- package/types/src/Twine1HTML/parse.d.ts +10 -0
- package/types/src/Twine2ArchiveHTML/compile.d.ts +6 -0
- package/types/src/Twine2ArchiveHTML/parse.d.ts +6 -0
- package/types/src/Twine2HTML/compile.d.ts +9 -0
- package/types/src/Twine2HTML/parse.d.ts +17 -0
- package/types/src/extwee.d.ts +2 -0
- package/web-index.js +29 -0
- package/webpack.config.js +12 -0
- package/bin/extwee.js +0 -47
- package/src/FileReader.js +0 -33
- package/src/HTMLParser.js +0 -345
- package/src/HTMLWriter.js +0 -231
- package/src/StoryFormatParser.js +0 -142
- package/src/TweeParser.js +0 -161
- package/src/TweeWriter.js +0 -121
- package/story-formats/chapbook-1.2.0/format.js +0 -1
- package/story-formats/chapbook-1.2.0/logo.svg +0 -1
- package/story-formats/harlowe-1.2.4/format.js +0 -1
- package/story-formats/harlowe-1.2.4/icon.svg +0 -78
- package/story-formats/harlowe-2.1.0/format.js +0 -2
- package/story-formats/harlowe-2.1.0/icon.svg +0 -78
- package/story-formats/harlowe-3.1.0/format.js +0 -3
- package/story-formats/harlowe-3.1.0/icon.svg +0 -78
- package/story-formats/paperthin-1.0.0/format.js +0 -1
- package/story-formats/paperthin-1.0.0/icon.svg +0 -5
- package/story-formats/snowman-1.4.0/format.js +0 -1
- package/story-formats/snowman-1.4.0/icon.svg +0 -436
- package/story-formats/snowman-2.0.2/format.js +0 -1
- package/story-formats/snowman-2.0.2/icon.svg +0 -436
- package/story-formats/sugarcube-1.0.35/LICENSE +0 -23
- package/story-formats/sugarcube-1.0.35/format.js +0 -1
- package/story-formats/sugarcube-1.0.35/icon.svg +0 -56
- package/story-formats/sugarcube-2.31.1/LICENSE +0 -22
- package/story-formats/sugarcube-2.31.1/format.js +0 -1
- package/story-formats/sugarcube-2.31.1/icon.svg +0 -56
- package/test/CLI/test2.html +0 -45
- package/test/CLI.test.js +0 -30
- package/test/FileReader/t1.txt +0 -1
- package/test/FileReader.test.js +0 -14
- package/test/HTMLParser.test.js +0 -177
- package/test/HTMLWriter/example6.twee +0 -16
- package/test/HTMLWriter.test.js +0 -287
- package/test/Roundtrip/example1.twee +0 -21
- package/test/Roundtrip.test.js +0 -48
- package/test/Story/startmeta.twee +0 -29
- package/test/StoryFormatParser.test.js +0 -91
- package/test/TweeParser/test.twee +0 -25
- package/test/TweeParser.test.js +0 -79
- package/test/TweeWriter/test1.twee +0 -18
- package/test/TweeWriter/test3.twee +0 -12
- package/test/TweeWriter/test4.twee +0 -14
- package/test/TweeWriter/test5.twee +0 -20
- package/test/TweeWriter/test6.twee +0 -15
- package/test/TweeWriter/test7.twee +0 -15
- package/test/TweeWriter.test.js +0 -107
- /package/test/CLI/{test.twee → files/test.twee} +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format_doublename.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingAuthor.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingDescription.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingImage.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingLicense.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingName.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingProofing.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingSource.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingURL.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingVersion.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/versionWrong.js +0 -0
- /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/TestTags.html +0 -0
- /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test11.html +0 -0
- /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test6.html +0 -0
- /package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/tagColors.html +0 -0
package/src/Story.js
CHANGED
|
@@ -1,523 +1,653 @@
|
|
|
1
|
-
import Passage from './Passage.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
*/
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*
|
|
106
|
-
* @
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
* @
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
*
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
*
|
|
191
|
-
* @
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
* @
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
*
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
*
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
*
|
|
398
|
-
* @
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
//
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
1
|
+
import Passage from './Passage.js';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { encode } from 'html-entities';
|
|
4
|
+
|
|
5
|
+
const creatorName = 'extwee';
|
|
6
|
+
const creatorVersion = '2.2.1';
|
|
7
|
+
|
|
8
|
+
class Story {
|
|
9
|
+
/**
|
|
10
|
+
* Internal name of story
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
#_name = '';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Internal start
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
#_start = '';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Internal IFID
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
#_IFID = '';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Internal story format
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
#_format = '';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Internal version of story format
|
|
35
|
+
*/
|
|
36
|
+
#_formatVersion = '';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Internal zoom level
|
|
40
|
+
*/
|
|
41
|
+
#_zoom = 0;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Passages
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
#_passages = [];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creator
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
#_creator = '';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* CreatorVersion
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
#_creatorVersion = '';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Metadata
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
#_metadata = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Tag Colors
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
#_tagColors = {};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Creates a story.
|
|
75
|
+
* @param {string} name - Name of the story.
|
|
76
|
+
*/
|
|
77
|
+
constructor (name = '') {
|
|
78
|
+
// Every story has a name.
|
|
79
|
+
this.name = name;
|
|
80
|
+
// Store the creator.
|
|
81
|
+
this.#_creator = creatorName;
|
|
82
|
+
this.#_creatorVersion = creatorVersion;
|
|
83
|
+
// Set metadata to an object.
|
|
84
|
+
this.#_metadata = {};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Each story has a name
|
|
89
|
+
* @returns {string} Name
|
|
90
|
+
*/
|
|
91
|
+
get name () { return this.#_name; }
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} a - Replacement story name
|
|
95
|
+
*/
|
|
96
|
+
set name (a) {
|
|
97
|
+
if (typeof a === 'string') {
|
|
98
|
+
this.#_name = a;
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error('Story name must be a string');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Tag Colors object (each property is a tag and its color)
|
|
106
|
+
* @returns {object} tag colors array
|
|
107
|
+
*/
|
|
108
|
+
get tagColors () { return this.#_tagColors; }
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {object} a - Replacement tag colors
|
|
112
|
+
*/
|
|
113
|
+
set tagColors (a) {
|
|
114
|
+
if (a instanceof Object) {
|
|
115
|
+
this.#_tagColors = a;
|
|
116
|
+
} else {
|
|
117
|
+
throw new Error('Tag colors must be an object!');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Interactive Fiction ID (IFID) of Story
|
|
123
|
+
* @returns {string} IFID
|
|
124
|
+
*/
|
|
125
|
+
get IFID () { return this.#_IFID; }
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} i - Replacement IFID
|
|
129
|
+
*/
|
|
130
|
+
set IFID (i) {
|
|
131
|
+
if (typeof i === 'string') {
|
|
132
|
+
this.#_IFID = i;
|
|
133
|
+
} else {
|
|
134
|
+
throw new Error('IFID must be a String!');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Name of start passage.
|
|
140
|
+
* @returns {string} start
|
|
141
|
+
*/
|
|
142
|
+
get start () { return this.#_start; }
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {string} s - Replacement start
|
|
146
|
+
*/
|
|
147
|
+
set start (s) {
|
|
148
|
+
if (typeof s === 'string') {
|
|
149
|
+
this.#_start = s;
|
|
150
|
+
} else {
|
|
151
|
+
throw new Error('start (passage name) must be a String!');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Story format version of Story.
|
|
157
|
+
* @returns {string} story format version
|
|
158
|
+
*/
|
|
159
|
+
get formatVersion () { return this.#_formatVersion; }
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} f - Replacement format version
|
|
163
|
+
*/
|
|
164
|
+
set formatVersion (f) {
|
|
165
|
+
if (typeof f === 'string') {
|
|
166
|
+
this.#_formatVersion = f;
|
|
167
|
+
} else {
|
|
168
|
+
throw new Error('Story format version must be a String!');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Metadata of Story.
|
|
174
|
+
* @returns {object} metadata of story
|
|
175
|
+
*/
|
|
176
|
+
get metadata () { return this.#_metadata; }
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {object} o - Replacement metadata
|
|
180
|
+
*/
|
|
181
|
+
set metadata (o) {
|
|
182
|
+
if (typeof o === 'object') {
|
|
183
|
+
this.#_metadata = o;
|
|
184
|
+
} else {
|
|
185
|
+
throw new Error('Story metadata must be Object!');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Story format of Story.
|
|
191
|
+
* @returns {string} format
|
|
192
|
+
*/
|
|
193
|
+
get format () { return this.#_format; }
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {string} f - Replacement format
|
|
197
|
+
*/
|
|
198
|
+
set format (f) {
|
|
199
|
+
if (typeof f === 'string') {
|
|
200
|
+
this.#_format = f;
|
|
201
|
+
} else {
|
|
202
|
+
throw new Error('Story format must be a String!');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Program used to create Story.
|
|
208
|
+
* @returns {string} Creator Program
|
|
209
|
+
*/
|
|
210
|
+
get creator () { return this.#_creator; }
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param {string} c - Creator Program of Story
|
|
214
|
+
*/
|
|
215
|
+
set creator (c) {
|
|
216
|
+
if (typeof c === 'string') {
|
|
217
|
+
this.#_creator = c;
|
|
218
|
+
} else {
|
|
219
|
+
throw new Error('Creator must be String');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Version used to create Story.
|
|
225
|
+
* @returns {string} Version
|
|
226
|
+
*/
|
|
227
|
+
get creatorVersion () { return this.#_creatorVersion; }
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {string} c - Version of creator program
|
|
231
|
+
*/
|
|
232
|
+
set creatorVersion (c) {
|
|
233
|
+
if (typeof c === 'string') {
|
|
234
|
+
this.#_creatorVersion = c;
|
|
235
|
+
} else {
|
|
236
|
+
throw new Error('Creator version must be a string!');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Zoom level.
|
|
242
|
+
* @returns {number} Zoom level
|
|
243
|
+
*/
|
|
244
|
+
get zoom () { return this.#_zoom; }
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @param {number} n - Replacement zoom level
|
|
248
|
+
*/
|
|
249
|
+
set zoom (n) {
|
|
250
|
+
if (typeof n === 'number') {
|
|
251
|
+
// Parse float with a fixed length and then force into Number
|
|
252
|
+
this.#_zoom = Number(Number.parseFloat(n).toFixed(2));
|
|
253
|
+
} else {
|
|
254
|
+
throw new Error('Zoom level must be a Number!');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Add a passage to the story.
|
|
260
|
+
* `StoryData` will override story metadata and `StoryTitle` will override story name.
|
|
261
|
+
* @param {Passage} p - Passage to add to Story.
|
|
262
|
+
*/
|
|
263
|
+
addPassage (p) {
|
|
264
|
+
// Check if passed argument is a Passage.
|
|
265
|
+
if (!(p instanceof Passage)) {
|
|
266
|
+
// We can only add passages to array.
|
|
267
|
+
throw new Error('Can only add Passages to the story!');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Does this passage already exist in the collection?
|
|
271
|
+
// If it does, we ignore it and return.
|
|
272
|
+
if (this.getPassageByName(p.name) !== null) {
|
|
273
|
+
// Warn user
|
|
274
|
+
console.warn('Ignored passage with same name as existing one!');
|
|
275
|
+
//
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Parse StoryData.
|
|
280
|
+
if (p.name === 'StoryData') {
|
|
281
|
+
// Try to parse JSON.
|
|
282
|
+
try {
|
|
283
|
+
// Attempt to parse storyData JSON.
|
|
284
|
+
const metadata = JSON.parse(p.text);
|
|
285
|
+
|
|
286
|
+
// IFID.
|
|
287
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'ifid')) {
|
|
288
|
+
this.IFID = metadata.ifid;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Format.
|
|
292
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'format')) {
|
|
293
|
+
this.format = metadata.format;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// formatVersion.
|
|
297
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'format-version')) {
|
|
298
|
+
this.formatVersion = metadata['format-version'];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Zoom.
|
|
302
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'zoom')) {
|
|
303
|
+
this.zoom = metadata.zoom;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Start.
|
|
307
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'start')) {
|
|
308
|
+
this.start = metadata.start;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Tag colors.
|
|
312
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'tag-colors')) {
|
|
313
|
+
this.tagColors = metadata['tag-colors'];
|
|
314
|
+
}
|
|
315
|
+
} catch (event) {
|
|
316
|
+
// Ignore errors.
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Don't add StoryData to passages.
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Parse StoryTitle.
|
|
324
|
+
if (p.name === 'StoryTitle') {
|
|
325
|
+
// If there is a StoryTitle passage, we accept the name.
|
|
326
|
+
// Set internal name based on StoryTitle.
|
|
327
|
+
this.name = p.text;
|
|
328
|
+
// Once we override story.name, return.
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// This is not StoryData or StoryTitle.
|
|
333
|
+
// Push the passage to the array.
|
|
334
|
+
this.#_passages.push(p);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Remove a passage from the story by name.
|
|
339
|
+
* @param {string} name - Passage name to remove
|
|
340
|
+
*/
|
|
341
|
+
removePassageByName (name) {
|
|
342
|
+
this.#_passages = this.#_passages.filter(passage => passage.name !== name);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Find passages by tag.
|
|
347
|
+
* @param {string} t - Passage name to search for
|
|
348
|
+
* @returns {Array} Return array of passages
|
|
349
|
+
*/
|
|
350
|
+
getPassagesByTag (t) {
|
|
351
|
+
// Look through passages
|
|
352
|
+
return this.#_passages.filter((passage) => {
|
|
353
|
+
// Look through each passage's tags
|
|
354
|
+
return passage.tags.some((tag) => t === tag);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Find passage by name.
|
|
360
|
+
* @param {string} name - Passage name to search for
|
|
361
|
+
* @returns {Passage | null} Return passage or null
|
|
362
|
+
*/
|
|
363
|
+
getPassageByName (name) {
|
|
364
|
+
// Look through passages
|
|
365
|
+
const results = this.#_passages.find((passage) => passage.name === name);
|
|
366
|
+
// Return entry or null, if not found
|
|
367
|
+
return results !== undefined ? results : null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Size (number of passages).
|
|
372
|
+
* @returns {number} Return number of passages
|
|
373
|
+
*/
|
|
374
|
+
size () {
|
|
375
|
+
return this.#_passages.length;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* forEach-style iterator of passages in Story.
|
|
380
|
+
* @param {Function} callback - Callback function
|
|
381
|
+
*/
|
|
382
|
+
forEachPassage (callback) {
|
|
383
|
+
// Check if argument is a function.
|
|
384
|
+
if (typeof callback !== 'function') {
|
|
385
|
+
// Throw error
|
|
386
|
+
throw new Error('Callback must be a function!');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Use internal forEach.
|
|
390
|
+
this.#_passages.forEach((element, index) => {
|
|
391
|
+
// Call callback function with element and index.
|
|
392
|
+
callback(element, index);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Export Story as JSON representation.
|
|
398
|
+
* @returns {string} JSON string.
|
|
399
|
+
*/
|
|
400
|
+
toJSON () {
|
|
401
|
+
// Create an initial object for later serialization.
|
|
402
|
+
const s = {
|
|
403
|
+
name: this.name,
|
|
404
|
+
tagColors: this.tagColors,
|
|
405
|
+
ifid: this.IFID,
|
|
406
|
+
start: this.start,
|
|
407
|
+
formatVersion: this.formatVersion,
|
|
408
|
+
metadata: this.metadata,
|
|
409
|
+
format: this.format,
|
|
410
|
+
creator: this.creator,
|
|
411
|
+
creatorVersion: this.creatorVersion,
|
|
412
|
+
zoom: this.zoom,
|
|
413
|
+
passages: []
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// For each passage, convert into simple object.
|
|
417
|
+
this.forEachPassage((p) => {
|
|
418
|
+
s.passages.push({
|
|
419
|
+
name: p.name,
|
|
420
|
+
tags: p.tags,
|
|
421
|
+
metadata: p.metadata,
|
|
422
|
+
text: p.text
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Return stringified Story object.
|
|
427
|
+
return JSON.stringify(s, null, 4);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Return Twee representation.
|
|
432
|
+
*
|
|
433
|
+
* See: Twee 3 Specification
|
|
434
|
+
* (https://github.com/iftechfoundation/twine-specs/blob/master/twee-3-specification.md)
|
|
435
|
+
* @returns {string} Twee String
|
|
436
|
+
*/
|
|
437
|
+
toTwee () {
|
|
438
|
+
// Write the StoryData first.
|
|
439
|
+
let outputContents = ':: StoryData\n';
|
|
440
|
+
|
|
441
|
+
// Create default object.
|
|
442
|
+
const metadata = {};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* ifid: (string) Required. Maps to <tw-storydata ifid>.
|
|
446
|
+
*/
|
|
447
|
+
// Is there an IFID?
|
|
448
|
+
if (this.IFID === '') {
|
|
449
|
+
// Generate a new IFID for this work.
|
|
450
|
+
// Twine 2 uses v4 (random) UUIDs, using only capital letters.
|
|
451
|
+
metadata.ifid = uuidv4().toUpperCase();
|
|
452
|
+
} else {
|
|
453
|
+
// Use existing (non-default) value.
|
|
454
|
+
metadata.ifid = this.IFID;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* format: (string) Optional. Maps to <tw-storydata format>.
|
|
459
|
+
*/
|
|
460
|
+
metadata.format = this.format;
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* format-version: (string) Optional. Maps to <tw-storydata format-version>.
|
|
464
|
+
*/
|
|
465
|
+
metadata['format-version'] = this.formatVersion;
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* zoom: (decimal) Optional. Maps to <tw-storydata zoom>.
|
|
469
|
+
*/
|
|
470
|
+
metadata.zoom = this.zoom;
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* start: (string) Optional.
|
|
474
|
+
* Maps to <tw-passagedata name> of the node whose pid matches <tw-storydata startnode>.
|
|
475
|
+
*/
|
|
476
|
+
metadata.start = this.start;
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* tag-colors: (object of tag(string):color(string) pairs) Optional.
|
|
480
|
+
* Pairs map to <tw-tag> nodes as <tw-tag name>:<tw-tag color>.
|
|
481
|
+
*/
|
|
482
|
+
const numberOfColors = Object.keys(this.tagColors).length;
|
|
483
|
+
|
|
484
|
+
// Are there any colors?
|
|
485
|
+
if (numberOfColors > 0) {
|
|
486
|
+
// Add a tag-colors property
|
|
487
|
+
metadata['tag-colors'] = this.tagColors;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Write out the story metadata.
|
|
491
|
+
outputContents += `${JSON.stringify(metadata, undefined, 2)}`;
|
|
492
|
+
|
|
493
|
+
// Add two newlines.
|
|
494
|
+
outputContents += '\n\n';
|
|
495
|
+
|
|
496
|
+
// Write story name as StoryTitle.
|
|
497
|
+
outputContents += ':: StoryTitle\n' + this.name;
|
|
498
|
+
|
|
499
|
+
// Add two newlines.
|
|
500
|
+
outputContents += '\n\n';
|
|
501
|
+
|
|
502
|
+
// For each passage, append it to the output.
|
|
503
|
+
this.forEachPassage((passage) => {
|
|
504
|
+
outputContents += passage.toTwee();
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// Return the Twee string.
|
|
508
|
+
return outputContents;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Return Twine 2 HTML.
|
|
513
|
+
*
|
|
514
|
+
* See: Twine 2 HTML Output
|
|
515
|
+
* (https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-htmloutput-spec.md)
|
|
516
|
+
* @returns {string} Twine 2 HTML string
|
|
517
|
+
*/
|
|
518
|
+
toTwine2HTML () {
|
|
519
|
+
// Prepare HTML content.
|
|
520
|
+
let storyData = `<tw-storydata name="${ encode( this.name ) }"`;
|
|
521
|
+
// Passage Identification (PID) counter.
|
|
522
|
+
// (Twine 2 starts with 1, so we mirror that.)
|
|
523
|
+
let PIDcounter = 1;
|
|
524
|
+
|
|
525
|
+
// Does start exist?
|
|
526
|
+
if (this.start === '') {
|
|
527
|
+
// We can't create a Twine 2 HTML file without a starting passage.
|
|
528
|
+
throw new Error('No starting passage!');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Try to find starting passage.
|
|
532
|
+
// If it doesn't exist, we throw an error.
|
|
533
|
+
if (this.getPassageByName(this.start) === null) {
|
|
534
|
+
// We can't create a Twine 2 HTML file without a starting passage.
|
|
535
|
+
throw new Error('Starting passage not found');
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Set initial PID value.
|
|
539
|
+
let startPID = 1;
|
|
540
|
+
// We have to do a bit of nonsense here.
|
|
541
|
+
// Twine 2 HTML cares about PID values.
|
|
542
|
+
this.forEachPassage((p) => {
|
|
543
|
+
// Have we found the starting passage?
|
|
544
|
+
if (p.name === this.start) {
|
|
545
|
+
// If so, set the PID based on index.
|
|
546
|
+
startPID = PIDcounter;
|
|
547
|
+
}
|
|
548
|
+
// Increase and keep looking.
|
|
549
|
+
PIDcounter++;
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// Set starting passage PID.
|
|
553
|
+
storyData += ` startnode="${startPID}"`;
|
|
554
|
+
|
|
555
|
+
// Defaults to 'extwee' if missing.
|
|
556
|
+
storyData += ` creator="${ encode( this.creator ) }"`;
|
|
557
|
+
|
|
558
|
+
// Default to extwee version.
|
|
559
|
+
storyData += ` creator-version="${this.creatorVersion}"`;
|
|
560
|
+
|
|
561
|
+
// Check if IFID exists.
|
|
562
|
+
if (this.IFID !== '') {
|
|
563
|
+
// Write the existing IFID.
|
|
564
|
+
storyData += ` ifid="${this.IFID}"`;
|
|
565
|
+
} else {
|
|
566
|
+
// Generate a new IFID.
|
|
567
|
+
// Twine 2 uses v4 (random) UUIDs, using only capital letters.
|
|
568
|
+
storyData += ` ifid="${uuidv4().toUpperCase()}"`;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Write existing or default value.
|
|
572
|
+
storyData += ` zoom="${this.zoom}"`;
|
|
573
|
+
|
|
574
|
+
// Write existing or default value.
|
|
575
|
+
storyData += ` format="${ encode(this.#_format) }"`;
|
|
576
|
+
|
|
577
|
+
// Write existing or default value.
|
|
578
|
+
storyData += ` format-version="${this.#_formatVersion}"`;
|
|
579
|
+
|
|
580
|
+
// Add the default attributes.
|
|
581
|
+
storyData += ' options hidden>\n';
|
|
582
|
+
|
|
583
|
+
// Start the STYLE.
|
|
584
|
+
storyData += '\t<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">';
|
|
585
|
+
|
|
586
|
+
// Get stylesheet passages.
|
|
587
|
+
const stylesheetPassages = this.getPassagesByTag('stylesheet');
|
|
588
|
+
|
|
589
|
+
// Concatenate passages.
|
|
590
|
+
stylesheetPassages.forEach((passage) => {
|
|
591
|
+
// Add text of passages.
|
|
592
|
+
storyData += passage.text;
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// Close the STYLE.
|
|
596
|
+
storyData += '</style>\n';
|
|
597
|
+
|
|
598
|
+
// Start the SCRIPT.
|
|
599
|
+
storyData += '\t<script role="script" id="twine-user-script" type="text/twine-javascript">';
|
|
600
|
+
|
|
601
|
+
// Get stylesheet passages.
|
|
602
|
+
const scriptPassages = this.getPassagesByTag('script');
|
|
603
|
+
|
|
604
|
+
// Concatenate passages.
|
|
605
|
+
scriptPassages.forEach((passage) => {
|
|
606
|
+
// Add text of passages.
|
|
607
|
+
storyData += passage.text;
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// Close SCRIPT.
|
|
611
|
+
storyData += '</script>\n';
|
|
612
|
+
|
|
613
|
+
// Reset the PID counter.
|
|
614
|
+
PIDcounter = 1;
|
|
615
|
+
|
|
616
|
+
// Build the passages HTML.
|
|
617
|
+
this.forEachPassage((passage) => {
|
|
618
|
+
// Append each passage element using the PID counter.
|
|
619
|
+
storyData += passage.toTwine2HTML(PIDcounter);
|
|
620
|
+
// Increase counter inside loop.
|
|
621
|
+
PIDcounter++;
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// Close the HTML element.
|
|
625
|
+
storyData += '</tw-storydata>';
|
|
626
|
+
|
|
627
|
+
// Return HTML contents.
|
|
628
|
+
return storyData;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Return Twine 1 HTML.
|
|
633
|
+
*
|
|
634
|
+
* See: Twine 1 HTML Output
|
|
635
|
+
* (https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md)
|
|
636
|
+
* @returns {string} Twine 1 HTML string.
|
|
637
|
+
*/
|
|
638
|
+
toTwine1HTML () {
|
|
639
|
+
// Begin HTML output.
|
|
640
|
+
let outputContents = '';
|
|
641
|
+
|
|
642
|
+
// Process passages (if any).
|
|
643
|
+
this.forEachPassage((p) => {
|
|
644
|
+
// Output HTML output per passage.
|
|
645
|
+
outputContents += `\t${p.toTwine1HTML()}`;
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
// Return Twine 1 HTML content.
|
|
649
|
+
return outputContents;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
export { Story, creatorName, creatorVersion };
|