extwee 2.0.6 → 2.2.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 (216) hide show
  1. package/.eslintrc.json +25 -25
  2. package/.github/FUNDING.yml +3 -0
  3. package/.github/dependabot.yml +11 -0
  4. package/.github/workflows/nodejs.yml +25 -24
  5. package/.travis.yml +13 -13
  6. package/CODE_OF_CONDUCT.md +82 -82
  7. package/LICENSE +21 -21
  8. package/README.md +173 -36
  9. package/SECURITY.md +12 -12
  10. package/babel.config.json +18 -22
  11. package/build/extwee +0 -0
  12. package/build/extwee.exe +0 -0
  13. package/build/extwee.web.min.js +2 -0
  14. package/build/extwee.web.min.js.LICENSE.txt +1 -0
  15. package/docs/.nojekyll +0 -0
  16. package/docs/README.md +167 -0
  17. package/docs/_sidebar.md +19 -0
  18. package/docs/examples/dynamicPassages.md +28 -0
  19. package/docs/examples/jsonToTwee.md +23 -0
  20. package/docs/examples/twsToTwee.md +25 -0
  21. package/docs/formats/json.md +17 -0
  22. package/docs/formats/twee.md +13 -0
  23. package/docs/formats/twine1HTML.md +13 -0
  24. package/docs/formats/twine2ArchiveHTML.md +13 -0
  25. package/docs/formats/twine2HTML.md +13 -0
  26. package/docs/formats/tws.md +9 -0
  27. package/docs/index.html +26 -0
  28. package/docs/install/binaries.md +9 -0
  29. package/docs/install/npm.md +20 -0
  30. package/docs/install/npx.md +9 -0
  31. package/docs/objects/passage.md +47 -0
  32. package/docs/objects/story.md +70 -0
  33. package/docs/objects/storyformat.md +27 -0
  34. package/index.html +22 -0
  35. package/index.js +29 -31
  36. package/package.json +65 -58
  37. package/src/JSON/parse.js +128 -0
  38. package/src/Passage.js +298 -202
  39. package/src/Story.js +650 -523
  40. package/src/StoryFormat/parse.js +134 -0
  41. package/src/StoryFormat.js +259 -300
  42. package/src/TWS/parse.js +86 -0
  43. package/src/Twee/parse.js +157 -0
  44. package/src/Twine1HTML/compile.js +58 -0
  45. package/src/Twine1HTML/parse.js +134 -0
  46. package/src/Twine2ArchiveHTML/compile.js +36 -0
  47. package/src/Twine2ArchiveHTML/parse.js +49 -0
  48. package/src/Twine2HTML/compile.js +35 -0
  49. package/src/Twine2HTML/parse.js +348 -0
  50. package/src/extwee.js +206 -0
  51. package/test/CLI/CLI.test.js +49 -0
  52. package/test/CLI/files/example.json +1 -0
  53. package/test/CLI/files/example6.twee +22 -0
  54. package/test/{Roundtrip → CLI/files}/harlowe.js +2 -2
  55. package/test/CLI/{input.html → files/input.html} +47 -47
  56. package/test/CLI/files/output/test.twee +0 -0
  57. package/test/CLI/{tweeExample.twee → files/tweeExample.twee} +17 -17
  58. package/test/CLI/files/twine1/LICENSE.txt +32 -0
  59. package/test/CLI/files/twine1/code.js +5 -0
  60. package/test/CLI/files/twine1/engine.js +43 -0
  61. package/test/CLI/files/twine1/header.html +325 -0
  62. package/test/CLI/files/twine1Test.html +371 -0
  63. package/test/CLI/{twineExample.html → files/twineExample.html} +16 -15
  64. package/test/JSON/JSON.Parse.test.js +316 -0
  65. package/test/Passage.test.js +175 -104
  66. package/test/Roundtrip/{Example1.html → Files/Example1.html} +63 -63
  67. package/test/Roundtrip/Files/LICENSE +19 -0
  68. package/test/Roundtrip/Files/example1.twee +10 -0
  69. package/test/Roundtrip/{example2.twee → Files/example2.twee} +27 -18
  70. package/test/Roundtrip/{example4.twee → Files/example4.twee} +27 -27
  71. package/test/{StoryFormatParser → Roundtrip/Files}/harlowe.js +2 -2
  72. package/test/Roundtrip/{round.html → Files/round.html} +6 -4
  73. package/test/Roundtrip/Roundtrip.test.js +54 -0
  74. package/test/Story.test.js +638 -423
  75. package/test/StoryFormat/StoryFormat.Parse.test.js +91 -0
  76. package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example.js +3 -3
  77. package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example2.js +3 -3
  78. package/test/{CLI → StoryFormat/StoryFormatParser}/harlowe.js +2 -2
  79. package/test/StoryFormat.test.js +152 -152
  80. package/test/TWS/Parse.test.js +78 -0
  81. package/test/TWS/TWSParser/Example1.tws +150 -0
  82. package/test/TWS/TWSParser/Example5.tws +414 -0
  83. package/test/TWS/TWSParser/noscale.tws +0 -0
  84. package/test/TWS/TWSParser/nostory.tws +0 -0
  85. package/test/Twee/Twee.Parse.test.js +76 -0
  86. package/test/{TweeParser → Twee/TweeParser}/emptytags.twee +2 -2
  87. package/test/{TweeParser → Twee/TweeParser}/example.twee +32 -32
  88. package/test/{TweeParser → Twee/TweeParser}/missing.twee +19 -19
  89. package/test/{TweeParser → Twee/TweeParser}/multipleScriptPassages.twee +19 -19
  90. package/test/{TweeParser → Twee/TweeParser}/multipleStyleTag.twee +19 -19
  91. package/test/{TweeParser → Twee/TweeParser}/multipletags.twee +10 -10
  92. package/test/{TweeParser → Twee/TweeParser}/noTitle.twee +2 -2
  93. package/test/{TweeParser → Twee/TweeParser}/notes.twee +16 -16
  94. package/test/{TweeParser → Twee/TweeParser}/pasagemetadataerror.twee +2 -2
  95. package/test/{TweeParser → Twee/TweeParser}/scriptPassage.twee +16 -16
  96. package/test/{TweeParser → Twee/TweeParser}/singletag.twee +13 -13
  97. package/test/{TweeParser → Twee/TweeParser}/startMetadata.twee +14 -14
  98. package/test/{TweeParser → Twee/TweeParser}/storydataerror.twee +25 -25
  99. package/test/{TweeParser → Twee/TweeParser}/stylePassage.twee +16 -16
  100. package/test/{Story → Twee/TweeParser}/test.twee +25 -25
  101. package/test/Twine1HTML/Twine1HTML.Compile.test.js +180 -0
  102. package/test/Twine1HTML/Twine1HTML.Parse.test.js +183 -0
  103. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/LICENSE +674 -0
  104. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/engine.js +43 -0
  105. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/jquery.js +4 -0
  106. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/modernizr.js +4 -0
  107. package/test/Twine1HTML/Twine1HTMLCompiler/engineTest.html +1 -0
  108. package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/LICENSE +32 -0
  109. package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/code.js +4 -0
  110. package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/header.html +327 -0
  111. package/test/Twine1HTML/Twine1HTMLCompiler/test.html +0 -0
  112. package/test/Twine1HTML/Twine1HTMLCompiler/test1.html +6 -0
  113. package/test/Twine1HTML/Twine1HTMLCompiler/test2.html +6 -0
  114. package/test/Twine1HTML/Twine1HTMLCompiler/test3.html +43 -0
  115. package/test/Twine1HTML/Twine1HTMLCompiler/test4.html +372 -0
  116. package/test/Twine1HTML/Twine1HTMLCompiler/test5.html +372 -0
  117. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Compile.test.js +35 -0
  118. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +25 -0
  119. package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLCompiler/test1.html +6 -0
  120. package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLParser/test1.html +3 -0
  121. package/test/Twine2HTML/Twine2HTML.Compile.test.js +224 -0
  122. package/test/Twine2HTML/Twine2HTML.Parse.test.js +172 -0
  123. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/creator.html +4 -4
  124. package/test/{CLI → Twine2HTML/Twine2HTMLCompiler}/example6.twee +15 -15
  125. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/missingStoryTitle.twee +29 -29
  126. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test2.html +10 -8
  127. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test3.html +1 -1
  128. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test4.html +4 -4
  129. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Example1.html +52 -52
  130. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Tags.html +15 -15
  131. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingStartnode.html +15 -15
  132. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingTagColors.html +48 -48
  133. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreator.html +11 -11
  134. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreatorVersion.html +11 -11
  135. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormat.html +11 -11
  136. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormatVersion.html +11 -11
  137. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingIFID.html +11 -11
  138. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingName.html +33 -33
  139. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPID.html +15 -15
  140. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageName.html +15 -15
  141. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageTags.html +15 -15
  142. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPosition.html +15 -15
  143. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingScript.html +14 -14
  144. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingSize.html +35 -35
  145. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStartnode.html +11 -11
  146. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStyle.html +14 -14
  147. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingZoom.html +11 -11
  148. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample.html +23 -23
  149. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample2.html +15 -15
  150. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample3.html +15 -15
  151. package/web-index.js +29 -0
  152. package/webpack.config.js +12 -0
  153. package/bin/extwee.js +0 -47
  154. package/src/FileReader.js +0 -33
  155. package/src/HTMLParser.js +0 -345
  156. package/src/HTMLWriter.js +0 -231
  157. package/src/StoryFormatParser.js +0 -142
  158. package/src/TweeParser.js +0 -161
  159. package/src/TweeWriter.js +0 -121
  160. package/story-formats/chapbook-1.2.0/format.js +0 -1
  161. package/story-formats/chapbook-1.2.0/logo.svg +0 -1
  162. package/story-formats/harlowe-1.2.4/format.js +0 -1
  163. package/story-formats/harlowe-1.2.4/icon.svg +0 -78
  164. package/story-formats/harlowe-2.1.0/format.js +0 -2
  165. package/story-formats/harlowe-2.1.0/icon.svg +0 -78
  166. package/story-formats/harlowe-3.1.0/format.js +0 -3
  167. package/story-formats/harlowe-3.1.0/icon.svg +0 -78
  168. package/story-formats/paperthin-1.0.0/format.js +0 -1
  169. package/story-formats/paperthin-1.0.0/icon.svg +0 -5
  170. package/story-formats/snowman-1.4.0/format.js +0 -1
  171. package/story-formats/snowman-1.4.0/icon.svg +0 -436
  172. package/story-formats/snowman-2.0.2/format.js +0 -1
  173. package/story-formats/snowman-2.0.2/icon.svg +0 -436
  174. package/story-formats/sugarcube-1.0.35/LICENSE +0 -23
  175. package/story-formats/sugarcube-1.0.35/format.js +0 -1
  176. package/story-formats/sugarcube-1.0.35/icon.svg +0 -56
  177. package/story-formats/sugarcube-2.31.1/LICENSE +0 -22
  178. package/story-formats/sugarcube-2.31.1/format.js +0 -1
  179. package/story-formats/sugarcube-2.31.1/icon.svg +0 -56
  180. package/test/CLI/test2.html +0 -45
  181. package/test/CLI.test.js +0 -30
  182. package/test/FileReader/t1.txt +0 -1
  183. package/test/FileReader.test.js +0 -14
  184. package/test/HTMLParser.test.js +0 -177
  185. package/test/HTMLWriter/example6.twee +0 -16
  186. package/test/HTMLWriter.test.js +0 -287
  187. package/test/Roundtrip/example1.twee +0 -21
  188. package/test/Roundtrip.test.js +0 -48
  189. package/test/Story/startmeta.twee +0 -29
  190. package/test/StoryFormatParser.test.js +0 -91
  191. package/test/TweeParser/test.twee +0 -25
  192. package/test/TweeParser.test.js +0 -79
  193. package/test/TweeWriter/test1.twee +0 -18
  194. package/test/TweeWriter/test3.twee +0 -12
  195. package/test/TweeWriter/test4.twee +0 -14
  196. package/test/TweeWriter/test5.twee +0 -20
  197. package/test/TweeWriter/test6.twee +0 -15
  198. package/test/TweeWriter/test7.twee +0 -15
  199. package/test/TweeWriter.test.js +0 -107
  200. /package/test/CLI/{test.twee → files/test.twee} +0 -0
  201. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format.js +0 -0
  202. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format_doublename.js +0 -0
  203. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingAuthor.js +0 -0
  204. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingDescription.js +0 -0
  205. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingImage.js +0 -0
  206. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingLicense.js +0 -0
  207. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingName.js +0 -0
  208. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingProofing.js +0 -0
  209. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingSource.js +0 -0
  210. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingURL.js +0 -0
  211. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingVersion.js +0 -0
  212. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/versionWrong.js +0 -0
  213. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/TestTags.html +0 -0
  214. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test11.html +0 -0
  215. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test6.html +0 -0
  216. /package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/tagColors.html +0 -0
@@ -0,0 +1,157 @@
1
+ import Passage from '../Passage.js';
2
+ import Story from '../Story.js';
3
+
4
+ /**
5
+ * Parses Twee 3 text into a Story object.
6
+ *
7
+ * See: Twee 3 Specification
8
+ * (https://github.com/iftechfoundation/twine-specs/blob/master/twee-3-specification.md)
9
+ * @param {string} fileContents - File contents to parse
10
+ * @returns {Story} story
11
+ */
12
+ function parse (fileContents) {
13
+ // Create Story.
14
+ const story = new Story();
15
+
16
+ // Throw error if fileContents is not a string
17
+ if (Object.prototype.toString.call(fileContents) !== '[object String]') {
18
+ throw new Error('Contents not a String');
19
+ }
20
+
21
+ let adjusted = '';
22
+
23
+ // Check if there are extra content in the files
24
+ // If so, cut it all out for the parser
25
+ if (fileContents[0] !== ':' && fileContents[1] !== ':') {
26
+ adjusted = fileContents.slice(fileContents.indexOf('::'), fileContents.length);
27
+ } else {
28
+ adjusted = fileContents;
29
+ }
30
+
31
+ // Split the file based on the passage sigil (::) proceeded by a newline
32
+ const parsingPassages = adjusted.split('\n::');
33
+
34
+ // Fix the first result
35
+ parsingPassages[0] = parsingPassages[0].slice(2, parsingPassages[0].length);
36
+
37
+ // Set the initial pid
38
+ let pid = 1;
39
+
40
+ // Iterate through the passages
41
+ parsingPassages.forEach((passage) => {
42
+ // Set default values
43
+ let tags = '';
44
+ let metadata = '';
45
+ let text = '';
46
+ let name = '';
47
+
48
+ // Header is everything to the first newline
49
+ let header = passage.slice(0, passage.indexOf('\n'));
50
+ // Text is everything else
51
+ // (Also eat the leading newline character.)
52
+ // (And trim any remaining whitespace.)
53
+ text = passage.substring(header.length + 1, passage.length).trim();
54
+
55
+ // Test for metadata
56
+ const openingCurlyBracketPosition = header.lastIndexOf('{');
57
+ const closingCurlyBracketPosition = header.lastIndexOf('}');
58
+
59
+ if (openingCurlyBracketPosition !== -1 && closingCurlyBracketPosition !== -1) {
60
+ // Save the text metadata
61
+ metadata = header.slice(openingCurlyBracketPosition, closingCurlyBracketPosition + 1);
62
+
63
+ // Remove the metadata from the header
64
+ header = header.substring(0, openingCurlyBracketPosition) + header.substring(closingCurlyBracketPosition + 1);
65
+ }
66
+
67
+ // There was passage metadata
68
+ if (metadata.length > 0) {
69
+ // Try to parse the metadata
70
+ try {
71
+ metadata = JSON.parse(metadata);
72
+ } catch (event) {
73
+ }
74
+ } else {
75
+ // There wasn't any metadata, so set default
76
+ metadata = {};
77
+ }
78
+
79
+ // Test for tags
80
+ const openingSquareBracketPosition = header.lastIndexOf('[');
81
+ const closingSquareBracketPosition = header.lastIndexOf(']');
82
+
83
+ if (openingSquareBracketPosition !== -1 && closingSquareBracketPosition !== -1) {
84
+ tags = header.slice(openingSquareBracketPosition, closingSquareBracketPosition + 1);
85
+
86
+ // Remove the tags from the header
87
+ header = header.substring(0, openingSquareBracketPosition) + header.substring(closingSquareBracketPosition + 1);
88
+ }
89
+
90
+ // Parse tags
91
+ if (tags.length > 0) {
92
+ // Eat the opening and closing square brackets
93
+ tags = tags.substring(1, tags.length - 1);
94
+
95
+ // Set empty default
96
+ let tagsArray = [];
97
+
98
+ // Test if tags is not single, empty string
99
+ if (!(tags === '')) {
100
+ tagsArray = tags.split(' ');
101
+ }
102
+
103
+ // There are multiple tags
104
+ if (tagsArray.length > 1) {
105
+ // Create future array
106
+ const futureTagArray = [];
107
+
108
+ // Move through entries
109
+ // Add a trimmed version into future array
110
+ tagsArray.forEach((tag) => { futureTagArray.push(tag.trim()); });
111
+
112
+ // Set the tags back to the future array
113
+ tags = futureTagArray;
114
+ } else if (tagsArray.length === 1) {
115
+ // There was only one tag
116
+ // Store it
117
+ const temp = tags;
118
+
119
+ // Switch tags over to an array
120
+ tags = [];
121
+ // Push the single entry
122
+ tags.push(temp);
123
+ } else {
124
+ // Make sure tags is set to empty array if no tags were found
125
+ tags = [];
126
+ }
127
+ } else {
128
+ // There were no tags, so set it to an empty array;
129
+ tags = [];
130
+ }
131
+
132
+ // Filter out any empty string tags
133
+ tags = tags.filter(tag => tag !== '');
134
+
135
+ // Trim any remaining whitespace
136
+ header = header.trim();
137
+
138
+ // Check if there is a name left
139
+ if (header.length > 0) {
140
+ name = header;
141
+ } else {
142
+ // No name left. Something went wrong. Blame user.
143
+ throw new Error('Malformed passage header!');
144
+ }
145
+
146
+ // addPassage() method does all the work.
147
+ story.addPassage(new Passage(name, text, tags, metadata, pid));
148
+
149
+ // Increase pid
150
+ pid++;
151
+ });
152
+
153
+ // Return Story.
154
+ return story;
155
+ }
156
+
157
+ export { parse };
@@ -0,0 +1,58 @@
1
+ import Story from '../Story.js';
2
+
3
+ /**
4
+ * Write a combination of Story object, `engine.js` (from Twine 1), `header.html`, and optional `code.js`.
5
+ * @param {Story} story - Story object to write.
6
+ * @param {string} engine - Source of `engine.js` file from Twine 1.
7
+ * @param {string} header - `header.html` content for Twine 1 story format.
8
+ * @param {string} name - Name of the story format (needed for `code.js` inclusion).
9
+ * @param {string} codeJS - `code.js` content with additional JavaScript.
10
+ * @param {object} config - Limited configuration object acting in place of `StorySettings`.
11
+ * @param {string} config.jquery - jQuery source.
12
+ * @param {string} config.modernizr - Modernizr source.
13
+ * @returns {string} Twine 1 HTML.
14
+ */
15
+ function compile (story, engine = '', header = '', name = '', codeJS = '', config = { jquery: '', modernizr: '' }) {
16
+ // We must have a Story object.
17
+ if (!(story instanceof Story)) {
18
+ throw new TypeError('Error: story must be a Story object!');
19
+ }
20
+
21
+ // Replace the "VERSION" with story.creator.
22
+ header = header.replaceAll(/"VERSION"/gm, story.creator);
23
+
24
+ // Replace the "TIME" with new Date().
25
+ header = header.replaceAll(/"TIME"/gm, new Date());
26
+
27
+ // Replace the ENGINE with `engine.js` code.
28
+ header = header.replaceAll(/"ENGINE"/gm, engine);
29
+
30
+ // Replace the NAME (e.g. "JONAH") with `engine.js` code.
31
+ header = header.replaceAll(`"${name.toUpperCase()}"`, codeJS);
32
+
33
+ // Replace "STORY_SIZE".
34
+ header = header.replaceAll(/"STORY_SIZE"/gm, `"${story.size()}"`);
35
+
36
+ // Replace "STORY" with Twine 1 HTML.
37
+ header = header.replaceAll(/"STORY"/gm, story.toTwine1HTML());
38
+
39
+ // Replace START_AT with ''.
40
+ header = header.replaceAll(/"START_AT"/gm, '\'\'');
41
+
42
+ // Does 'jquery' exist?
43
+ if (Object.prototype.hasOwnProperty.call(config, 'jquery')) {
44
+ // Replace JQUERY with jQuery.
45
+ header = header.replaceAll(/"JQUERY"/gm, config.jquery);
46
+ }
47
+
48
+ // Does 'modernizr' exist?
49
+ if (Object.prototype.hasOwnProperty.call(config, 'modernizr')) {
50
+ // Replace "MODERNIZR" with Modernizr.
51
+ header = header.replaceAll(/"MODERNIZR"/gm, config.modernizr);
52
+ }
53
+
54
+ // Return code.
55
+ return header;
56
+ }
57
+
58
+ export { compile };
@@ -0,0 +1,134 @@
1
+ import { parse as HtmlParser } from 'node-html-parser';
2
+ import Passage from '../Passage.js';
3
+ import Story from '../Story.js';
4
+
5
+ /**
6
+ * Parses Twine 1 HTML into a Story object.
7
+ *
8
+ * See: Twine 1 HTML Output Documentation
9
+ * (https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md)
10
+ * @param {string} content - Twine 1 HTML content to parse.
11
+ * @returns {Story} Story object
12
+ */
13
+ function parse (content) {
14
+ // Create a default Story.
15
+ const s = new Story();
16
+
17
+ // Send to node-html-parser.
18
+ // Enable getting the content of 'script', 'style', and 'pre' elements.
19
+ // Get back a DOM.
20
+ const dom = new HtmlParser(
21
+ content,
22
+ {
23
+ lowerCaseTagName: false,
24
+ script: true,
25
+ style: true,
26
+ pre: true
27
+ });
28
+
29
+ // Look for `<div id="storeArea">`.
30
+ let storyData = dom.querySelector('#storeArea');
31
+
32
+ // Does the `<div id="storeArea">` element exist?
33
+ if (storyData === null) {
34
+ // Look for `<div id="store-area">`.
35
+ storyData = dom.querySelector('#store-area');
36
+ // Check for null
37
+ if (storyData == null) {
38
+ // Can't find any story data.
39
+ throw new Error('Cannot find #storeArea or #store-area!');
40
+ }
41
+ }
42
+
43
+ // Pull out the `[tiddler]` elements.
44
+ const storyPassages = dom.querySelectorAll('[tiddler]');
45
+
46
+ // Move through the passages.
47
+ for (const passage in storyPassages) {
48
+ // Get the passage attributes.
49
+ const attr = storyPassages[passage].attributes;
50
+ // Get the passage text.
51
+ const text = storyPassages[passage].rawText;
52
+
53
+ /**
54
+ * twine-position: (string) Required.
55
+ * Comma-separated X and Y coordinates of the passage within Twine 1.
56
+ */
57
+ // Set a default position.
58
+ let position = null;
59
+ // Does position exist?
60
+ if (Object.prototype.hasOwnProperty.call(attr, 'twine-position')) {
61
+ // Update position.
62
+ position = attr['twine-position'];
63
+ }
64
+
65
+ /**
66
+ * tiddler: (string) Required.
67
+ * The name of the passage.
68
+ */
69
+ // Create a default value.
70
+ const name = attr.tiddler;
71
+ // Is this `StoryTitle`?
72
+ if (name === 'StoryTitle') {
73
+ // If StoryTitle exists, we accept the story name.
74
+ s.name = text;
75
+ }
76
+
77
+ /**
78
+ * tags: (string) Required.
79
+ * Space-separated list of passages tags, if any.
80
+ */
81
+ // Create empty tag array.
82
+ let tags = [];
83
+ // Does the tags attribute exist?
84
+ if (Object.prototype.hasOwnProperty.call(attr, 'tags')) {
85
+ // Escape any tags
86
+ // (Attributes can, themselves, be empty strings.)
87
+ if (attr.tags.length > 0 && attr.tags !== '""') {
88
+ // Escape the tags.
89
+ tags = attr.tags;
90
+ // Split by spaces into an array.
91
+ tags = tags.split(' ');
92
+ }
93
+
94
+ // Remove any empty strings.
95
+ tags = tags.filter(tag => tag !== '');
96
+ }
97
+
98
+ // Create metadata for passage.
99
+ // We translate Twine 1 attribute into Twine 2 metadata.
100
+ const metadata = {};
101
+
102
+ // Does position exist?
103
+ if (position !== null) {
104
+ // Add the property to metadata
105
+ metadata.position = position;
106
+ }
107
+
108
+ /**
109
+ * modifier: (string) Optional.
110
+ * Name of the tool that last edited the passage.
111
+ * Generally, for versions of Twine 1, this value will be "twee".
112
+ * Twee compilers may place their own name (e.g. "tweego" for Tweego).
113
+ */
114
+ if (Object.prototype.hasOwnProperty.call(attr, 'modifier')) {
115
+ // In Twine 2, `creator` maps to Twine 1's `modifier`.
116
+ s.creator = attr.modifier;
117
+ }
118
+
119
+ // Add the passage.
120
+ s.addPassage(
121
+ new Passage(
122
+ name,
123
+ text,
124
+ tags,
125
+ metadata
126
+ )
127
+ );
128
+ }
129
+
130
+ // Return story object.
131
+ return s;
132
+ }
133
+
134
+ export { parse };
@@ -0,0 +1,36 @@
1
+ import Story from '../Story.js';
2
+
3
+ /**
4
+ * Write array of Story objects into Twine 2 Archive HTML.
5
+ * @param {Array} stories - Array of Story objects.
6
+ * @returns {string} Twine 2 Archive HTML.
7
+ */
8
+ function compile (stories) {
9
+ // Can only accept array.
10
+ if (!Array.isArray(stories)) {
11
+ throw new TypeError('Stories is not array!');
12
+ }
13
+
14
+ // Output
15
+ let outputContents = '';
16
+
17
+ // Go through each entry (which must be a Story).
18
+ for (const story of stories) {
19
+ // If this is not a story, throw a TypeError.
20
+ if (!(story instanceof Story)) {
21
+ // Throw TypeError.
22
+ throw new TypeError('Error: story must be a Story object!');
23
+ }
24
+
25
+ // Append content.
26
+ outputContents += story.toTwine2HTML();
27
+
28
+ // Append newlines.
29
+ outputContents += '\n\n';
30
+ }
31
+
32
+ // Return output
33
+ return outputContents;
34
+ }
35
+
36
+ export { compile };
@@ -0,0 +1,49 @@
1
+ import { parse as HtmlParser } from 'node-html-parser';
2
+ import { parse as parseTwine2HTML } from '../Twine2HTML/parse.js';
3
+
4
+ /**
5
+ * Parse HTML for one or more Twine 2 HTML elements and return array of story objects.
6
+ * @param {string} content - Content to parse for Twine 2 HTML elements.
7
+ * @returns {Array} Array of stories found in content.
8
+ */
9
+ function parse (content) {
10
+ // Can only parse string values.
11
+ if (typeof content !== 'string') {
12
+ throw new TypeError('Content is not a string!');
13
+ }
14
+
15
+ // Send to node-html-parser.
16
+ // Enable getting the content of 'script', 'style', and 'pre' elements.
17
+ // Get back a DOM.
18
+ const dom = new HtmlParser(
19
+ content,
20
+ {
21
+ lowerCaseTagName: false,
22
+ script: true,
23
+ style: true,
24
+ pre: true
25
+ });
26
+
27
+ // Array of possible story elements.
28
+ const outputArray = [];
29
+
30
+ // Pull out the `<tw-storydata>` element.
31
+ const storyDataElements = dom.getElementsByTagName('tw-storydata');
32
+
33
+ // Did we find any elements?
34
+ if (storyDataElements.length === 0) {
35
+ // If there is not a single `<tw-storydata>` element, this is not a Twine 2 story!
36
+ throw new Error('Not Twine 2 HTML content!');
37
+ }
38
+
39
+ // Iterate through all `<tw-storydata>` elements.
40
+ for (const storyElement of storyDataElements) {
41
+ // Convert element back into HTML text and parse.
42
+ outputArray.push(parseTwine2HTML(storyElement.outerHTML));
43
+ }
44
+
45
+ // Return array.
46
+ return outputArray;
47
+ }
48
+
49
+ export { parse };
@@ -0,0 +1,35 @@
1
+ import Story from '../Story.js';
2
+ import StoryFormat from '../StoryFormat.js';
3
+
4
+ /**
5
+ * Write a combination of Story + StoryFormat into Twine 2 HTML file.
6
+ * @param {Story} story - Story object to write.
7
+ * @param {StoryFormat} storyFormat - StoryFormat to write.
8
+ * @returns {string} Twine 2 HTML.
9
+ */
10
+ function compile (story, storyFormat) {
11
+ if (!(story instanceof Story)) {
12
+ throw new Error('Error: story must be a Story object!');
13
+ }
14
+
15
+ if (!(storyFormat instanceof StoryFormat)) {
16
+ throw new Error('storyFormat must be a StoryFormat object!');
17
+ }
18
+
19
+ let outputContents = '';
20
+ const storyData = story.toTwine2HTML();
21
+
22
+ // Replace the story name in the source file.
23
+ storyFormat.source = storyFormat.source.replaceAll(/{{STORY_NAME}}/gm, story.name);
24
+
25
+ // Replace the story data.
26
+ storyFormat.source = storyFormat.source.replaceAll(/{{STORY_DATA}}/gm, storyData);
27
+
28
+ // Combine everything together.
29
+ outputContents += storyFormat.source;
30
+
31
+ // Return content.
32
+ return outputContents;
33
+ }
34
+
35
+ export { compile };