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
@@ -1,15 +1,15 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <title>twineExample</title>
6
- </head>
7
- <body>
8
- <tw-story></tw-story>
9
- <tw-storydata name="twineExample" startnode="1" creator="Twine" creator-version="2.2.1" ifid="2B68ECD6-348F-4CF5-96F8-549A512A8128" zoom="1" format="Harlowe" format-version="2.1.0" options="" hidden><style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">html{font-size: 1.2em;}</style><script role="script" id="twine-user-script" type="text/twine-javascript">window.test = {};</script><tw-passagedata pid="1" name="Start" tags="" position="102,104" size="100,100">[[Another passage]]
10
-
11
- [[A third passage]]</tw-passagedata><tw-passagedata pid="2" name="Another passage" tags="" position="353,60" size="100,100">[[A fourth passage]]
12
-
13
- [[A third passage]] </tw-passagedata><tw-passagedata pid="3" name="A third passage" tags="" position="350,288" size="100,100">[[Start]]</tw-passagedata><tw-passagedata pid="4" name="A fourth passage" tags="" position="587,197" size="100,100">[[A fifth passage]]</tw-passagedata><tw-passagedata pid="5" name="A fifth passage" tags="" position="800,306" size="100,100">Double-click this passage to edit it.</tw-passagedata></tw-storydata>
14
- </body>
15
- </html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>twineExample</title>
6
+ </head>
7
+ <body>
8
+ <tw-story></tw-story>
9
+ <tw-storydata name="twineExample" startnode="1" creator="Twine" creator-version="2.2.1" ifid="2B68ECD6-348F-4CF5-96F8-549A512A8128" zoom="1" format="Harlowe" format-version="2.1.0" options="" hidden><style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">html{font-size: 1.2em;}</style><script role="script" id="twine-user-script" type="text/twine-javascript">window.test = {};</script><tw-passagedata pid="1" name="Start" tags="" position="102,104" size="100,100">[[Another passage]]
10
+
11
+ [[A third passage]]</tw-passagedata><tw-passagedata pid="2" name="Another passage" tags="" position="353,60" size="100,100">[[A fourth passage]]
12
+
13
+ [[A third passage]] </tw-passagedata><tw-passagedata pid="3" name="A third passage" tags="" position="350,288" size="100,100">[[Start]]</tw-passagedata><tw-passagedata pid="4" name="A fourth passage" tags="" position="587,197" size="100,100">[[A fifth passage]]</tw-passagedata><tw-passagedata pid="5" name="A fifth passage" tags="" position="800,306" size="100,100">Double-click this passage to edit it.</tw-passagedata></tw-storydata>
14
+ </body>
15
+ </html>
package/web-index.js ADDED
@@ -0,0 +1,29 @@
1
+ import { parse as parseTwee } from './src/Twee/parse.js';
2
+ import { parse as parseJSON } from './src/JSON/parse.js';
3
+ import { parse as parseStoryFormat } from './src/StoryFormat/parse.js';
4
+ import { parse as parseTwine1HTML } from './src/Twine1HTML/parse.js';
5
+ import { parse as parseTwine2HTML } from './src/Twine2HTML/parse.js';
6
+ import { parse as parseTwine2ArchiveHTML } from './src/Twine2ArchiveHTML/parse.js';
7
+ import { parse as parseTWS } from './src/TWS/parse.js';
8
+ import { compile as compileTwine1HTML } from './src/Twine1HTML/compile.js';
9
+ import { compile as compileTwine2HTML } from './src/Twine2HTML/compile.js';
10
+ import { compile as compileTwine2ArchiveHTML } from './src/Twine2ArchiveHTML/compile.js';
11
+ import Story from './src/Story.js';
12
+ import Passage from './src/Passage.js';
13
+ import StoryFormat from './src/StoryFormat.js';
14
+
15
+ window.Extwee = {
16
+ parseTwee,
17
+ parseJSON,
18
+ parseTWS,
19
+ parseStoryFormat,
20
+ parseTwine1HTML,
21
+ parseTwine2HTML,
22
+ parseTwine2ArchiveHTML,
23
+ compileTwine1HTML,
24
+ compileTwine2HTML,
25
+ compileTwine2ArchiveHTML,
26
+ Story,
27
+ Passage,
28
+ StoryFormat
29
+ };
@@ -0,0 +1,12 @@
1
+ import path from 'node:path';
2
+
3
+ export default {
4
+ mode: 'production',
5
+ entry: {
6
+ 'extwee.web.min': './web-index.js'
7
+ },
8
+ output: {
9
+ path: path.resolve('./', 'build'),
10
+ filename: '[name].js',
11
+ },
12
+ };
package/bin/extwee.js DELETED
@@ -1,47 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @fileoverview CLI for Extwee
5
- * @author Dan Cox
6
- */
7
-
8
- // Import everything
9
- import Extwee from '../index.js';
10
- // Import Commander
11
- import { Command } from 'commander';
12
-
13
- // Create a new Command
14
- const program = new Command();
15
-
16
- program
17
- .name('extwee')
18
- .version('2.0.6')
19
- .option('-c', 'From Twee into HTML')
20
- .option('-d', 'From HTML into Twee')
21
- .option('-s <storyformat>', 'Path to storyformat')
22
- .option('-i <inputFile>', 'Path to input file')
23
- .option('-o <outputFile>', 'Path to output file');
24
-
25
- // Parse the passed arguments
26
- program.parse(process.argv);
27
-
28
- // Create object of passed arguments parsed by Commander
29
- const options = program.opts();
30
-
31
- // Decompile branch
32
- if(options.d === true) {
33
- const inputHTML = Extwee.readFile(options.i);
34
- const storyObject = Extwee.parseHTML(inputHTML);
35
- Extwee.writeTwee(storyObject, options.o);
36
- process.exit();
37
- }
38
-
39
- // Compile branch
40
- if(options.c === true) {
41
- const inputTwee = Extwee.readFile(options.i);
42
- const story = Extwee.parseTwee(inputTwee);
43
- const inputStoryFormat = Extwee.readFile(options.s);
44
- const parsedStoryFormat = Extwee.parseStoryFormat(inputStoryFormat);
45
- Extwee.writeHTML(options.o, story, parsedStoryFormat);
46
- process.exit();
47
- }
package/src/FileReader.js DELETED
@@ -1,33 +0,0 @@
1
- import fs from 'fs';
2
- /**
3
- * @class FileReader
4
- * @module FileReader
5
- */
6
- export default class FileReader {
7
- /**
8
- * Read a text file
9
- *
10
- * @static
11
- * @public
12
- * @function readFile
13
- * @memberof FileReader
14
- * @param {string} file - Path of file to read
15
- * @returns {string} Content of file
16
- */
17
- static read (file) {
18
- // Setup default value
19
- let contents = '';
20
-
21
- // Attempt to find the file
22
- if (fs.existsSync(file)) {
23
- // The file exists.
24
- contents = fs.readFileSync(file, 'utf8');
25
- } else {
26
- // Throw error if file does not exist
27
- throw new Error('Error: File not found!');
28
- }
29
-
30
- // Return default or updated values
31
- return contents;
32
- }
33
- }
package/src/HTMLParser.js DELETED
@@ -1,345 +0,0 @@
1
- /**
2
- * @external HTML
3
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML|HTML}
4
- */
5
-
6
- import { parse as HtmlParser } from 'node-html-parser';
7
- import Story from './Story.js';
8
- import Passage from './Passage.js';
9
- /**
10
- * @class HTMLParser
11
- * @module HTMLParser
12
- */
13
- export default class HTMLParser {
14
- /**
15
- * Parse HTML text into a JS DOM-like object
16
- *
17
- * @public
18
- * @static
19
- * @function parse
20
- * @param {string} content - Content to parse
21
- * @returns {Story} story
22
- */
23
- static parse (content) {
24
- let story = null;
25
- let startNode = null;
26
-
27
- // Send to node-html-parser
28
- // Enable getting the content of 'script', 'style', and 'pre' elements
29
- // Get back a DOM
30
- const dom = new HtmlParser(
31
- content,
32
- {
33
- lowerCaseTagName: false,
34
- script: true,
35
- style: true,
36
- pre: true
37
- });
38
-
39
- // Pull out the tw-storydata element
40
- const storyData = dom.querySelector('tw-storydata');
41
-
42
- // Does the <tw-storydata> element exist?
43
- if (storyData !== null) {
44
- // Create a Story.
45
- story = new Story();
46
-
47
- /**
48
- * name: (string) Required.
49
- * The name of the story.
50
- */
51
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'name')) {
52
- // Create StoryTitle passage based on name
53
- story.addPassage(new Passage('StoryTitle', storyData.attributes.name));
54
- // Set the story name
55
- story.name = storyData.attributes.name;
56
- } else {
57
- // Name is a required filed. Warn user.
58
- console.warn('Twine 2 HTML must have a name!');
59
- // Set a default name
60
- story.addPassage(new Passage('StoryTitle', 'Untitled'));
61
- }
62
-
63
- /**
64
- * ifid: (string) Required.
65
- * An IFID is a sequence of between 8 and 63 characters,
66
- * each of which shall be a digit, a capital letter or a
67
- * hyphen that uniquely identify a story (see Treaty of Babel).
68
- */
69
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'ifid')) {
70
- // Update story IFID
71
- story.IFID = storyData.attributes.ifid;
72
- } else {
73
- // Name is a required filed. Warn user.
74
- console.warn('Twine 2 HTML must have an IFID!');
75
- }
76
-
77
- /**
78
- * creator: (string) Optional.
79
- * The name of program used to create the file.
80
- */
81
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'creator')) {
82
- // Update story creator
83
- story.creator = storyData.attributes.creator;
84
- }
85
-
86
- /**
87
- * creator-version: (string) Optional.
88
- * The version of the program used to create the file.
89
- */
90
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'creator-version')) {
91
- // Update story creator version
92
- story.creatorVersion = storyData.attributes['creator-version'];
93
- }
94
-
95
- /**
96
- * format: (string) Optional.
97
- * The story format used to create the story.
98
- */
99
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'format')) {
100
- // Update story format
101
- story.format = storyData.attributes.format;
102
- }
103
-
104
- /**
105
- * format-version: (string) Optional.
106
- * The version of the story format used to create the story.
107
- */
108
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'format-version')) {
109
- // Update story format version
110
- story.formatVersion = storyData.attributes['format-version'];
111
- }
112
-
113
- /**
114
- * zoom: (string) Optional.
115
- * The decimal level of zoom (i.e. 1.0 is 100% and 1.2 would be 120% zoom level).
116
- */
117
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'zoom')) {
118
- // Update story zoom
119
- story.zoom = Number(Number.parseFloat(storyData.attributes.zoom).toFixed(2));
120
- }
121
-
122
- /**
123
- * startnode: (string) Optional.
124
- * The PID matching a <tw-passagedata> element whose content should be displayed first.
125
- */
126
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'startnode')) {
127
- // Take string value and convert to Int
128
- startNode = Number.parseInt(storyData.attributes.startnode, 10);
129
- }
130
- } else {
131
- // If there is not a <tw-storydata> element, this is not a Twine 2 story!
132
- throw new Error('Not a Twine 2-style file!');
133
- }
134
-
135
- // Pull out the tw-passagedata elements
136
- const storyPassages = dom.querySelectorAll('tw-passagedata');
137
-
138
- // Move through the passages
139
- for (const passage in storyPassages) {
140
- // Get the passage attributes
141
- const attr = storyPassages[passage].attributes;
142
- // Get the passage text
143
- const text = storyPassages[passage].rawText;
144
-
145
- // Set a default position.
146
- let position = null;
147
- // Does position exist?
148
- if (Object.prototype.hasOwnProperty.call(attr, 'position')) {
149
- // Update position.
150
- position = attr.position;
151
- }
152
-
153
- // Set a default size.
154
- let size = null;
155
- // Does size exist?
156
- if (Object.prototype.hasOwnProperty.call(attr, 'size')) {
157
- // Update size.
158
- size = attr.size;
159
- }
160
-
161
- /**
162
- * name: (string) Required.
163
- * The name of the passage.
164
- *
165
- * https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-htmloutput-spec.md#passages
166
- */
167
- // Create a default value
168
- let name = null;
169
- // Does name exist?
170
- if (Object.prototype.hasOwnProperty.call(attr, 'name')) {
171
- // Escape the name
172
- name = HTMLParser.escapeMetacharacters(attr.name);
173
- } else {
174
- console.warn('Encountered passage without a name! Will not add.');
175
- }
176
-
177
- // Create empty tag array.
178
- let tags = [];
179
- // Does the tags attribute exist?
180
- if (Object.prototype.hasOwnProperty.call(attr, 'tags')) {
181
- // Escape any tags
182
- // (Attributes can, themselves, be empty strings.)
183
- if (attr.tags.length > 0 && attr.tags !== '""') {
184
- // Escape the tags
185
- tags = HTMLParser.escapeMetacharacters(attr.tags);
186
- // Split by spaces into an array
187
- tags = tags.split(' ');
188
- }
189
-
190
- // Remove any empty strings.
191
- tags = tags.filter(tag => tag !== '');
192
- }
193
-
194
- // Create metadata for passage.
195
- const metadata = {};
196
-
197
- // Does position exist?
198
- if (position !== null) {
199
- // Add the property to metadata
200
- metadata.position = position;
201
- }
202
-
203
- // Does size exist?
204
- if (size !== null) {
205
- // Add the property to metadata
206
- metadata.size = size;
207
- }
208
-
209
- /**
210
- * pid: (string) Required.
211
- * The Passage ID (PID).
212
- * (Note: This is subject to change during editing with Twine 2.)
213
- *
214
- * https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-htmloutput-spec.md#passages
215
- */
216
- // Create a default PID
217
- let pid = -1;
218
- // Does pid exist?
219
- if (Object.prototype.hasOwnProperty.call(attr, 'pid')) {
220
- // Parse string into int
221
- // Update PID
222
- pid = Number.parseInt(attr.pid, 10);
223
- } else {
224
- console.warn('Passages are required to have PID. Will not add!');
225
- }
226
-
227
- // If passage is missing name and PID (required attributes),
228
- // they are not added.
229
- if (name !== null && pid !== -1) {
230
- // Add a new Passage into an array
231
- story.addPassage(
232
- new Passage(
233
- name,
234
- text,
235
- tags,
236
- metadata,
237
- pid
238
- )
239
- );
240
- }
241
- }
242
-
243
- // Look for the style element
244
- const styleElement = dom.querySelector('#twine-user-stylesheet');
245
-
246
- // Does the style element exist?
247
- if (styleElement !== null) {
248
- // Check if there is any content.
249
- if (styleElement.rawText.length > 0) {
250
- // Update stylesheet passage
251
- story.addPassage(new Passage(
252
- 'UserStylesheet',
253
- styleElement.rawText,
254
- ['stylesheet'])
255
- );
256
- }
257
- }
258
-
259
- // Look for the script element
260
- const scriptElement = dom.querySelector('#twine-user-script');
261
-
262
- // Does the script element exist?
263
- if (scriptElement !== null) {
264
- // Check if there is any content.
265
- if (scriptElement.rawText.length > 0) {
266
- story.addPassage(new Passage(
267
- 'UserScript',
268
- scriptElement.rawText,
269
- ['script'])
270
- );
271
- }
272
- }
273
-
274
- // Was there a startNode?
275
- if (startNode !== null) {
276
- // Try to find starting passage by PID.
277
- const startingPassage = story.getPassageByPID(startNode);
278
- // Does the passage exist (yet)?
279
- if (startingPassage !== null) {
280
- // If so, update property to name of passage.
281
- story.start = startingPassage.name;
282
- } else {
283
- throw new Error('Invalid startnode detected in <tw-storydata>!');
284
- }
285
- }
286
-
287
- // Look for all <tw-tag> elements
288
- const twTags = dom.querySelectorAll('tw-tag');
289
-
290
- // Parse through the entries
291
- twTags.forEach((tags) => {
292
- // Parse each tag element
293
- const attributes = tags.attributes;
294
-
295
- // Create default value for name
296
- let name = '';
297
-
298
- // Create default value for color
299
- let color = '';
300
-
301
- // Check for name
302
- if (Object.prototype.hasOwnProperty.call(attributes, 'name')) {
303
- name = attributes.name;
304
- }
305
-
306
- // Check for color
307
- if (Object.prototype.hasOwnProperty.call(attributes, 'color')) {
308
- color = attributes.color;
309
- }
310
-
311
- // If both are not empty strings, use them.
312
- if (name !== '' && color !== '') {
313
- // Add name and color to the object
314
- story.tagColors[name] = color;
315
- }
316
- });
317
-
318
- // Return the parsed story
319
- return story;
320
- }
321
-
322
- /**
323
- * Try to escape meta-characters
324
- *
325
- * @public
326
- * @static
327
- * @function escapeMetacharacters
328
- * @param {string} result - Text to parse
329
- * @returns {string} Escaped characters
330
- */
331
- static escapeMetacharacters (result) {
332
- // Replace any single backslash with two of them
333
- result = result.replace(/\\/g, '\\');
334
- // Double-escape escaped {
335
- result = result.replace(/\\\{/g, '\\\\{');
336
- // Double-escape escaped }
337
- result = result.replace(/\\\}/g, '\\\\}');
338
- // Double-escape escaped [
339
- result = result.replace(/\\\[/g, '\\\\[');
340
- // Double-escape escaped ]
341
- result = result.replace(/\\\]/g, '\\\\]');
342
-
343
- return result;
344
- }
345
- }