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.
Files changed (233) 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 +20 -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 +68 -58
  37. package/src/JSON/parse.js +128 -0
  38. package/src/Passage.js +279 -202
  39. package/src/Story.js +653 -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 +349 -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 +244 -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 +677 -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 +173 -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/test/Twine2HTML/Twine2HTMLParser/unescaping.html +33 -0
  152. package/tsconfig.json +21 -0
  153. package/types/index.d.ts +14 -0
  154. package/types/src/JSON/parse.d.ts +8 -0
  155. package/types/src/Passage.d.ts +72 -0
  156. package/types/src/Story.d.ts +161 -0
  157. package/types/src/StoryFormat/parse.d.ts +7 -0
  158. package/types/src/StoryFormat.d.ts +97 -0
  159. package/types/src/TWS/parse.d.ts +10 -0
  160. package/types/src/Twee/parse.d.ts +10 -0
  161. package/types/src/Twine1HTML/compile.d.ts +17 -0
  162. package/types/src/Twine1HTML/parse.d.ts +10 -0
  163. package/types/src/Twine2ArchiveHTML/compile.d.ts +6 -0
  164. package/types/src/Twine2ArchiveHTML/parse.d.ts +6 -0
  165. package/types/src/Twine2HTML/compile.d.ts +9 -0
  166. package/types/src/Twine2HTML/parse.d.ts +17 -0
  167. package/types/src/extwee.d.ts +2 -0
  168. package/web-index.js +29 -0
  169. package/webpack.config.js +12 -0
  170. package/bin/extwee.js +0 -47
  171. package/src/FileReader.js +0 -33
  172. package/src/HTMLParser.js +0 -345
  173. package/src/HTMLWriter.js +0 -231
  174. package/src/StoryFormatParser.js +0 -142
  175. package/src/TweeParser.js +0 -161
  176. package/src/TweeWriter.js +0 -121
  177. package/story-formats/chapbook-1.2.0/format.js +0 -1
  178. package/story-formats/chapbook-1.2.0/logo.svg +0 -1
  179. package/story-formats/harlowe-1.2.4/format.js +0 -1
  180. package/story-formats/harlowe-1.2.4/icon.svg +0 -78
  181. package/story-formats/harlowe-2.1.0/format.js +0 -2
  182. package/story-formats/harlowe-2.1.0/icon.svg +0 -78
  183. package/story-formats/harlowe-3.1.0/format.js +0 -3
  184. package/story-formats/harlowe-3.1.0/icon.svg +0 -78
  185. package/story-formats/paperthin-1.0.0/format.js +0 -1
  186. package/story-formats/paperthin-1.0.0/icon.svg +0 -5
  187. package/story-formats/snowman-1.4.0/format.js +0 -1
  188. package/story-formats/snowman-1.4.0/icon.svg +0 -436
  189. package/story-formats/snowman-2.0.2/format.js +0 -1
  190. package/story-formats/snowman-2.0.2/icon.svg +0 -436
  191. package/story-formats/sugarcube-1.0.35/LICENSE +0 -23
  192. package/story-formats/sugarcube-1.0.35/format.js +0 -1
  193. package/story-formats/sugarcube-1.0.35/icon.svg +0 -56
  194. package/story-formats/sugarcube-2.31.1/LICENSE +0 -22
  195. package/story-formats/sugarcube-2.31.1/format.js +0 -1
  196. package/story-formats/sugarcube-2.31.1/icon.svg +0 -56
  197. package/test/CLI/test2.html +0 -45
  198. package/test/CLI.test.js +0 -30
  199. package/test/FileReader/t1.txt +0 -1
  200. package/test/FileReader.test.js +0 -14
  201. package/test/HTMLParser.test.js +0 -177
  202. package/test/HTMLWriter/example6.twee +0 -16
  203. package/test/HTMLWriter.test.js +0 -287
  204. package/test/Roundtrip/example1.twee +0 -21
  205. package/test/Roundtrip.test.js +0 -48
  206. package/test/Story/startmeta.twee +0 -29
  207. package/test/StoryFormatParser.test.js +0 -91
  208. package/test/TweeParser/test.twee +0 -25
  209. package/test/TweeParser.test.js +0 -79
  210. package/test/TweeWriter/test1.twee +0 -18
  211. package/test/TweeWriter/test3.twee +0 -12
  212. package/test/TweeWriter/test4.twee +0 -14
  213. package/test/TweeWriter/test5.twee +0 -20
  214. package/test/TweeWriter/test6.twee +0 -15
  215. package/test/TweeWriter/test7.twee +0 -15
  216. package/test/TweeWriter.test.js +0 -107
  217. /package/test/CLI/{test.twee → files/test.twee} +0 -0
  218. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format.js +0 -0
  219. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format_doublename.js +0 -0
  220. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingAuthor.js +0 -0
  221. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingDescription.js +0 -0
  222. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingImage.js +0 -0
  223. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingLicense.js +0 -0
  224. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingName.js +0 -0
  225. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingProofing.js +0 -0
  226. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingSource.js +0 -0
  227. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingURL.js +0 -0
  228. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingVersion.js +0 -0
  229. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/versionWrong.js +0 -0
  230. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/TestTags.html +0 -0
  231. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test11.html +0 -0
  232. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test6.html +0 -0
  233. /package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/tagColors.html +0 -0
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
- }
package/src/HTMLWriter.js DELETED
@@ -1,231 +0,0 @@
1
- /**
2
- * @external Story
3
- * @see Story.js
4
- * @external StoryFormat
5
- * @see StoryFormat.js
6
- */
7
-
8
- import fs from 'fs';
9
- import Story from './Story.js';
10
- import StoryFormat from './StoryFormat.js';
11
- import { v4 as uuidv4 } from 'uuid';
12
-
13
- /**
14
- * @class HTMLWriter
15
- * @module HTMLWriter
16
- */
17
- export default class HTMLWriter {
18
- /**
19
- * Write story to file using story format and adding any CSS and JS
20
- *
21
- * @public
22
- * @static
23
- * @function writeFile
24
- * @param {string} file - File to write
25
- * @param {Story} story - Story object to write
26
- * @param {StoryFormat} storyFormat - StoryFormat to write
27
- */
28
- static write (file, story, storyFormat) {
29
- if (!(story instanceof Story)) {
30
- throw new Error('Error: story must be a Story object!');
31
- }
32
-
33
- if (!(storyFormat instanceof StoryFormat)) {
34
- throw new Error('storyFormat must be a StoryFormat object!');
35
- }
36
-
37
- let outputContents = '';
38
- let storyData = '';
39
-
40
- // Look for StoryTitle
41
- const storyTitle = story.getPassageByName('StoryTitle');
42
-
43
- // Does the passage exist?
44
- if (storyTitle != null) {
45
- // Always overwrite any existing name with StoryTitle (per spec)
46
- story.name = storyTitle.text;
47
-
48
- // Use story.name for name.
49
- storyData += `<tw-storydata name="${story.name}"`;
50
- } else {
51
- throw new Error("'name' is required attribute. (Add StoryTitle to story.)");
52
- }
53
-
54
- // Does start exist?
55
- if (story.start !== '') {
56
- // Try to get starting passage
57
- const startingPassage = story.getPassageByName(story.start);
58
- // Does it exist currently?
59
- if (startingPassage !== null) {
60
- // Add the starting passage
61
- storyData += ` startnode="${startingPassage.pid}"`;
62
- } else {
63
- // Throw error if no starting passage exists
64
- throw new Error('Starting passage not found');
65
- }
66
- } else {
67
- // Throw error if no starting passage exists
68
- throw new Error('No starting passage found!');
69
- }
70
-
71
- // Defaults to 'extwee' if missing.
72
- storyData += ` creator="${story.creator}"`;
73
-
74
- // Default to extwee version.
75
- storyData += ` creator-version="${story.creatorVersion}"`;
76
-
77
- // Check if IFID exists.
78
- if (story.IFID !== '') {
79
- // Write the existing IFID
80
- storyData += ` ifid="${story.IFID}"`;
81
- } else {
82
- // Generate a new IFID
83
- // Twine 2 uses v4 (random) UUIDs, using only capital letters
84
- storyData += ` ifid="${uuidv4().toUpperCase()}"`;
85
- }
86
-
87
- // Write existing or default value.
88
- storyData += ` zoom="${story.zoom}"`;
89
-
90
- // Write existing or default value.
91
- storyData += ` format="${storyFormat.name}"`;
92
-
93
- // Write existing or default value.
94
- storyData += ` format-version="${storyFormat.version}"`;
95
-
96
- // Add the default.
97
- storyData += ' options hidden>\n';
98
-
99
- // Start the STYLE.
100
- storyData += '\t<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">';
101
-
102
- // Get stylesheet passages
103
- const stylesheetPassages = story.getPassagesByTag('stylesheet');
104
-
105
- // Concatenate passages
106
- stylesheetPassages.forEach((passage) => {
107
- // Add text of passages
108
- storyData += passage.text;
109
- // Remove from story
110
- story.removePassageByName(passage.name);
111
- });
112
-
113
- // Close the STYLE
114
- storyData += '</style>\n';
115
-
116
- // Start the SCRIPT
117
- storyData += '\t<script role="script" id="twine-user-script" type="text/twine-javascript">';
118
-
119
- // Get stylesheet passages
120
- const scriptPassages = story.getPassagesByTag('script');
121
-
122
- // Concatenate passages
123
- scriptPassages.forEach((passage) => {
124
- // Add text of passages
125
- storyData += passage.text;
126
- // Remove from story
127
- story.removePassageByName(passage.name);
128
- });
129
-
130
- // Close SCRIPT
131
- storyData += '</script>\n';
132
-
133
- // Build the passages
134
- story.forEach((passage) => {
135
- // Start the passage element
136
- storyData += '\t<tw-passagedata';
137
-
138
- /**
139
- * pid: (string) Required.
140
- * The Passage ID (PID).
141
- */
142
- storyData += ` pid="${passage.pid}"`;
143
-
144
- /**
145
- * name: (string) Required.
146
- * The name of the passage.
147
- */
148
- storyData += ` name="${passage.name}"`;
149
-
150
- /**
151
- * tags: (string) Optional.
152
- * Any tags for the passage separated by spaces.
153
- */
154
- if (passage.tags.length > 1) {
155
- storyData += ` tags="${passage.tags.join(' ')}" `;
156
- } else if (passage.tags.length === 1) {
157
- storyData += ` tags="${passage.tags[0]}" `;
158
- }
159
-
160
- /**
161
- * position: (string) Optional.
162
- * Comma-separated X and Y position of the upper-left of the passage
163
- * when viewed within the Twine 2 editor.
164
- */
165
- if (Object.prototype.hasOwnProperty.call(passage.metadata, 'position')) {
166
- storyData += ` position="${passage.metadata.position}" `;
167
- }
168
-
169
- /**
170
- * size: (string) Optional.
171
- * Comma-separated width and height of the passage
172
- * when viewed within the Twine 2 editor.
173
- */
174
- if (Object.prototype.hasOwnProperty.call(passage.metadata, 'size')) {
175
- storyData += `size="${passage.metadata.size}" `;
176
- }
177
-
178
- storyData += `>${HTMLWriter.escape(passage.text)}</tw-passagedata>\n`;
179
- });
180
-
181
- storyData += '</tw-storydata>';
182
-
183
- // Replace the story name in the source file
184
- storyFormat.source = storyFormat.source.replaceAll(/{{STORY_NAME}}/gm, story.name);
185
-
186
- // Replace the story data
187
- storyFormat.source = storyFormat.source.replaceAll(/{{STORY_DATA}}/gm, storyData);
188
-
189
- // Combine everything together.
190
- outputContents += storyFormat.source;
191
-
192
- try {
193
- // Try to write.
194
- fs.writeFileSync(file, outputContents);
195
- } catch (event) {
196
- // Throw error
197
- throw new Error('Error: Cannot write HTML file!');
198
- }
199
- }
200
-
201
- /**
202
- * Escape HTML characters
203
- *
204
- * @public
205
- * @static
206
- * @function escape
207
- * @param {string} text - Text to escape
208
- * @returns {string} Escaped text
209
- */
210
- static escape (text) {
211
- // Throw error if text is not a string
212
- if (Object.prototype.toString.call(text) !== '[object String]') {
213
- throw new Error('Text argument is not a String');
214
- }
215
-
216
- const rules = [
217
- ['&', '&amp;'],
218
- ['<', '&lt;'],
219
- ['>', '&gt;'],
220
- ['"', '&quot;'],
221
- ["'", '&#x27;'],
222
- ['`', '&#x60;']
223
- ];
224
-
225
- rules.forEach(([rule, template]) => {
226
- text = text.replaceAll(rule, template);
227
- });
228
-
229
- return text;
230
- }
231
- }