extwee 2.2.0 → 2.2.2

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 (74) hide show
  1. package/.github/codeql-analysis.yml +51 -0
  2. package/README.md +9 -3
  3. package/build/extwee +0 -0
  4. package/build/extwee.web.min.js +1 -1
  5. package/docs/_sidebar.md +1 -0
  6. package/docs/examples/dynamicPassages.md +1 -1
  7. package/docs/examples/twsToTwee.md +1 -1
  8. package/docs/objects/story.md +1 -2
  9. package/index.js +3 -1
  10. package/package.json +22 -19
  11. package/src/IFID/generate.js +20 -0
  12. package/src/JSON/parse.js +44 -1
  13. package/src/Passage.js +61 -31
  14. package/src/Story.js +272 -110
  15. package/src/StoryFormat/parse.js +190 -80
  16. package/src/StoryFormat.js +78 -88
  17. package/src/TWS/parse.js +3 -3
  18. package/src/Twee/parse.js +3 -4
  19. package/src/Twine1HTML/compile.js +3 -1
  20. package/src/Twine1HTML/parse.js +3 -4
  21. package/src/Twine2ArchiveHTML/compile.js +9 -1
  22. package/src/Twine2ArchiveHTML/parse.js +33 -3
  23. package/src/Twine2HTML/compile.js +32 -7
  24. package/src/Twine2HTML/parse.js +51 -55
  25. package/test/IFID/IFID.Generate.test.js +10 -0
  26. package/test/JSON/JSON.Parse.test.js +24 -24
  27. package/test/Passage.test.js +69 -0
  28. package/test/Story.test.js +298 -49
  29. package/test/StoryFormat/StoryFormat.Parse.test.js +442 -55
  30. package/test/StoryFormat.test.js +9 -2
  31. package/test/TWS/Parse.test.js +1 -1
  32. package/test/Twine1HTML/Twine1HTML.Compile.test.js +1 -1
  33. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Compile.test.js +1 -1
  34. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +20 -4
  35. package/test/Twine2HTML/Twine2HTML.Compile.test.js +36 -121
  36. package/test/Twine2HTML/Twine2HTML.Parse.test.js +63 -43
  37. package/test/Twine2HTML/Twine2HTMLCompiler/format.js +9 -0
  38. package/test/Twine2HTML/Twine2HTMLParser/missingZoom.html +1 -1
  39. package/test/Twine2HTML/Twine2HTMLParser/unescaping.html +33 -0
  40. package/tsconfig.json +21 -0
  41. package/types/index.d.ts +14 -0
  42. package/types/src/JSON/parse.d.ts +8 -0
  43. package/types/src/Passage.d.ts +72 -0
  44. package/types/src/Story.d.ts +161 -0
  45. package/types/src/StoryFormat/parse.d.ts +7 -0
  46. package/types/src/StoryFormat.d.ts +97 -0
  47. package/types/src/TWS/parse.d.ts +10 -0
  48. package/types/src/Twee/parse.d.ts +10 -0
  49. package/types/src/Twine1HTML/compile.d.ts +17 -0
  50. package/types/src/Twine1HTML/parse.d.ts +10 -0
  51. package/types/src/Twine2ArchiveHTML/compile.d.ts +6 -0
  52. package/types/src/Twine2ArchiveHTML/parse.d.ts +6 -0
  53. package/types/src/Twine2HTML/compile.d.ts +9 -0
  54. package/types/src/Twine2HTML/parse.d.ts +17 -0
  55. package/types/src/extwee.d.ts +2 -0
  56. package/web-index.js +3 -1
  57. package/test/StoryFormat/StoryFormatParser/example.js +0 -3
  58. package/test/StoryFormat/StoryFormatParser/example2.js +0 -3
  59. package/test/StoryFormat/StoryFormatParser/format.js +0 -1
  60. package/test/StoryFormat/StoryFormatParser/format_doublename.js +0 -1
  61. package/test/StoryFormat/StoryFormatParser/harlowe.js +0 -3
  62. package/test/StoryFormat/StoryFormatParser/missingAuthor.js +0 -1
  63. package/test/StoryFormat/StoryFormatParser/missingDescription.js +0 -1
  64. package/test/StoryFormat/StoryFormatParser/missingImage.js +0 -1
  65. package/test/StoryFormat/StoryFormatParser/missingLicense.js +0 -1
  66. package/test/StoryFormat/StoryFormatParser/missingName.js +0 -1
  67. package/test/StoryFormat/StoryFormatParser/missingProofing.js +0 -1
  68. package/test/StoryFormat/StoryFormatParser/missingSource.js +0 -1
  69. package/test/StoryFormat/StoryFormatParser/missingURL.js +0 -1
  70. package/test/StoryFormat/StoryFormatParser/missingVersion.js +0 -1
  71. package/test/StoryFormat/StoryFormatParser/versionWrong.js +0 -1
  72. package/test/Twine2HTML/Twine2HTMLParser/missingName.html +0 -33
  73. package/test/Twine2HTML/Twine2HTMLParser/missingPID.html +0 -15
  74. package/test/Twine2HTML/Twine2HTMLParser/missingPassageName.html +0 -15
@@ -1,8 +1,10 @@
1
1
  import { parse as parseStoryFormat } from '../../src/StoryFormat/parse.js';
2
2
  import { parse as parseTwine2HTML } from '../../src/Twine2HTML/parse.js';
3
3
  import { compile as compileTwine2HTML } from '../../src/Twine2HTML/compile.js';
4
- import Story from '../../src/Story.js';
4
+ import { Story } from '../../src/Story.js';
5
5
  import Passage from '../../src/Passage.js';
6
+ import StoryFormat from '../../src/StoryFormat.js';
7
+ import { generate as generateIFID } from '../../src/IFID/generate.js';
6
8
  import { readFileSync } from 'node:fs';
7
9
 
8
10
  describe('Twine2HTMLCompiler', () => {
@@ -16,29 +18,6 @@ describe('Twine2HTMLCompiler', () => {
16
18
  expect(() => { compileTwine2HTML(s, {}); }).toThrow();
17
19
  });
18
20
 
19
- it('Read, write, and read HTML', () => {
20
- // Read HTML.
21
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/twineExample3.html', 'utf-8');
22
-
23
- // Parse HTML.
24
- const story = parseTwine2HTML(fr);
25
-
26
- // Read StoryFormat.
27
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
28
-
29
- // Parse StoryFormat.
30
- const storyFormat = parseStoryFormat(fr2);
31
-
32
- // Write HTML.
33
- const fr3 = compileTwine2HTML(story, storyFormat);
34
-
35
- // Parse HTML.
36
- const story2 = parseTwine2HTML(fr3);
37
-
38
- // Test both names to be the same.
39
- expect(story.name).toBe(story2.name);
40
- });
41
-
42
21
  it('Should write one and two-tag passages', () => {
43
22
  // Read HTML.
44
23
  const fr = readFileSync('test/Twine2HTML/Twine2HTMLCompiler/TestTags.html', 'utf-8');
@@ -47,7 +26,7 @@ describe('Twine2HTMLCompiler', () => {
47
26
  const story = parseTwine2HTML(fr);
48
27
 
49
28
  // Read StoryFormat.
50
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
29
+ const fr2 = readFileSync('test/Twine2HTML/Twine2HTMLCompiler/format.js', 'utf-8');
51
30
 
52
31
  // Parse StoryFormat.
53
32
  const storyFormat = parseStoryFormat(fr2);
@@ -62,12 +41,12 @@ describe('Twine2HTMLCompiler', () => {
62
41
  let tags2 = '';
63
42
 
64
43
  // Combine contents of tags.
65
- story.forEachPassage((p) => {
44
+ story.passages.forEach((p) => {
66
45
  tags += p.tags.join('');
67
46
  });
68
47
 
69
48
  // Combine contents of tags.
70
- story2.forEachPassage((p) => {
49
+ story2.passages.forEach((p) => {
71
50
  tags2 += p.tags.join('');
72
51
  });
73
52
 
@@ -76,148 +55,84 @@ describe('Twine2HTMLCompiler', () => {
76
55
  expect(tags).toBe(tags2);
77
56
  });
78
57
 
79
- it('Should not add optional position to passages', () => {
80
- // Create Story.
81
- const story = new Story();
82
- // Add passage.
83
- story.addPassage(new Passage('A'));
84
- // Add passage.
85
- story.addPassage(new Passage('B'));
86
- // Add StoryTitle
87
- story.addPassage(new Passage('StoryTitle', 'Title'));
88
- // Add Start
89
- story.addPassage(new Passage('Start', 'Content'));
90
-
91
- // Read StoryFormat.
92
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
93
-
94
- // Parse StoryFormat.
95
- const storyFormat = parseStoryFormat(fr2);
96
-
97
- // Set start
98
- story.start = 'Start';
99
-
100
- // Write out HTML with story and storyFormat.
101
- // (Will add position to passages without them.)
102
- const fr3 = compileTwine2HTML(story, storyFormat);
103
-
104
- // Parse new HTML file.
105
- const story2 = parseTwine2HTML(fr3);
106
-
107
- // Verify none of the directly created passages have position.
108
- story.forEachPassage((passage) => {
109
- expect(Object.prototype.hasOwnProperty.call(passage.metadata, 'position')).toBe(false);
110
- });
111
-
112
- // Verify none parsed passages have position.
113
- story2.forEachPassage((passage) => {
114
- expect(Object.prototype.hasOwnProperty.call(passage.metadata, 'position')).toBe(false);
115
- });
116
- });
117
-
118
- it("Don't write creator if missing originally", () => {
58
+ it('Throw error if IFID does not exist', () => {
119
59
  // Create a new story.
120
60
  const story = new Story();
121
61
 
122
62
  // Create a passage.
123
63
  story.addPassage(new Passage('A'));
124
64
 
125
- // Create another passage.
126
- story.addPassage(new Passage('B'));
127
-
128
- // Create another passage.
129
- story.addPassage(new Passage('StoryTitle', 'Title'));
130
-
131
- // Add Start
132
- story.addPassage(new Passage('Start', 'Content'));
133
-
134
- // Set start
135
- story.start = 'Start';
136
-
137
65
  // Read StoryFormat.
138
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
66
+ const fr2 = readFileSync('test/Twine2HTML/Twine2HTMLCompiler/format.js', 'utf-8');
139
67
 
140
68
  // Parse StoryFormat.
141
69
  const storyFormat = parseStoryFormat(fr2);
142
70
 
143
- // Write the HTML.
144
- const fr3 = compileTwine2HTML(story, storyFormat);
145
-
146
- // Parse HTML.
147
- const story2 = parseTwine2HTML(fr3);
148
-
149
- // Test creator (should be default)
150
- expect(story.creator).toBe('extwee');
151
-
152
- // Test parsed creator (should be default)
153
- expect(story2.creator).toBe('extwee');
154
-
155
- // Creator should be the same
156
- expect(story.creator).toBe(story2.creator);
71
+ expect(() => {
72
+ compileTwine2HTML(story, storyFormat);
73
+ }).toThrow();
157
74
  });
158
75
 
159
- it('Throw error if StoryTitle does not exist', () => {
76
+ it('Throw error if starting passage property does not exist', () => {
160
77
  // Create a new story.
161
78
  const story = new Story();
162
79
 
163
80
  // Create a passage.
164
81
  story.addPassage(new Passage('A'));
165
82
 
83
+ // Create StoryTitle
84
+ story.addPassage(new Passage('StoryTitle', 'Name'));
85
+
86
+ // Set a passage that doesn't exist
87
+ story.start = 'Nope';
88
+
166
89
  // Read StoryFormat.
167
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
90
+ const fr2 = readFileSync('test/Twine2HTML/Twine2HTMLCompiler/format.js', 'utf-8');
168
91
 
169
92
  // Parse StoryFormat.
170
93
  const storyFormat = parseStoryFormat(fr2);
171
94
 
95
+ // Throws error.
172
96
  expect(() => {
173
97
  compileTwine2HTML(story, storyFormat);
174
98
  }).toThrow();
175
99
  });
176
100
 
177
- it('Throw error if no start or Start exists', () => {
101
+ it('Throw error if source is empty string in StoryFormat', () => {
178
102
  // Create a new story.
179
103
  const story = new Story();
180
104
 
181
- // Create a passage.
182
- story.addPassage(new Passage('A'));
183
-
184
- // Create StoryTitle
185
- story.addPassage(new Passage('StoryTitle', 'Name'));
186
-
187
- // Read StoryFormat.
188
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
105
+ // Create StoryFormat.
106
+ const sf = new StoryFormat();
189
107
 
190
- // Parse StoryFormat.
191
- const storyFormat = parseStoryFormat(fr2);
108
+ // Set source to empty string.
109
+ sf.source = '';
192
110
 
193
111
  // Throws error.
194
112
  expect(() => {
195
- compileTwine2HTML(story, storyFormat);
113
+ compileTwine2HTML(story, sf);
196
114
  }).toThrow();
197
115
  });
198
116
 
199
- it('Throw error if starting passage property does not exist', () => {
117
+ it('Throw error if story name is empty string', () => {
200
118
  // Create a new story.
201
119
  const story = new Story();
202
120
 
203
- // Create a passage.
204
- story.addPassage(new Passage('A'));
205
-
206
- // Create StoryTitle
207
- story.addPassage(new Passage('StoryTitle', 'Name'));
121
+ // Create StoryFormat.
122
+ const sf = new StoryFormat();
208
123
 
209
- // Set a passage that doesn't exist
210
- story.start = 'Nope';
124
+ // Set source to non-empty string.
125
+ sf.source = 'Test';
211
126
 
212
- // Read StoryFormat.
213
- const fr2 = readFileSync('test/StoryFormat/StoryFormatParser/format.js', 'utf-8');
127
+ // Generate IFID (to avoid throwing error).
128
+ story.IFID = generateIFID();
214
129
 
215
- // Parse StoryFormat.
216
- const storyFormat = parseStoryFormat(fr2);
130
+ // Set story name to empty string.
131
+ story.name = '';
217
132
 
218
133
  // Throws error.
219
134
  expect(() => {
220
- compileTwine2HTML(story, storyFormat);
135
+ compileTwine2HTML(story, sf);
221
136
  }).toThrow();
222
137
  });
223
138
  });
@@ -1,12 +1,12 @@
1
1
  import { readFileSync } from 'node:fs';
2
- import { parse as parseTwine2HTML, escapeMetacharacters } from '../../src/Twine2HTML/parse.js';
2
+ import { parse as parseTwine2HTML } from '../../src/Twine2HTML/parse.js';
3
3
 
4
4
  // Pull the name and version of this project from package.json.
5
5
  // These are used as the 'creator' and 'creator-version'.
6
6
  const { version } = JSON.parse(readFileSync('package.json', 'utf-8'));
7
7
 
8
8
  describe('Twine2HTMLParser', () => {
9
- describe('#parse()', () => {
9
+ describe('Errors', () => {
10
10
  it('Should throw error if content is not a string', () => {
11
11
  expect(() => { parseTwine2HTML({}); }).toThrow();
12
12
  });
@@ -14,7 +14,9 @@ describe('Twine2HTMLParser', () => {
14
14
  it('Should throw error if content is not Twine-2 style HTML', () => {
15
15
  expect(() => { parseTwine2HTML(''); }).toThrow();
16
16
  });
17
+ });
17
18
 
19
+ describe('#parse()', () => {
18
20
  it('Should be able to parse Twine 2 HTML for story name', () => {
19
21
  const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/twineExample.html', 'utf-8');
20
22
  const story = parseTwine2HTML(fr);
@@ -34,18 +36,6 @@ describe('Twine2HTMLParser', () => {
34
36
  expect(p.tags).toHaveLength(2);
35
37
  });
36
38
 
37
- it('Should have default name', () => {
38
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingName.html', 'utf-8');
39
- const story = parseTwine2HTML(fr);
40
- expect(story.name).toBe('');
41
- });
42
-
43
- it('Should set a missing IFID to an empty string', () => {
44
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingIFID.html', 'utf-8');
45
- const tp = parseTwine2HTML(fr);
46
- expect(tp.IFID).toBe('');
47
- });
48
-
49
39
  it('Should have Extwee for creator when missing', () => {
50
40
  const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingCreator.html', 'utf-8');
51
41
  const tp = parseTwine2HTML(fr);
@@ -73,7 +63,7 @@ describe('Twine2HTMLParser', () => {
73
63
  it('Should have empty string as zoom when missing', () => {
74
64
  const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingZoom.html', 'utf-8');
75
65
  const tp = parseTwine2HTML(fr);
76
- expect(tp.zoom).toBe(0);
66
+ expect(tp.zoom).toBe(1);
77
67
  });
78
68
 
79
69
  it('Should not have position if passage does not', () => {
@@ -120,19 +110,10 @@ describe('Twine2HTMLParser', () => {
120
110
  expect(stylesheetPassages.length).toBe(1);
121
111
  });
122
112
 
123
- it('Should throw error if startNode is missing', () => {
113
+ it('Should parse HTML without passage start node', () => {
124
114
  const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingStartnode.html', 'utf-8');
125
- expect(() => { parseTwine2HTML(fr); }).toThrow();
126
- });
127
-
128
- it('Should throw error if passage name is missing', () => {
129
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingPassageName.html', 'utf-8');
130
- expect(() => { parseTwine2HTML(fr); }).toThrow();
131
- });
132
-
133
- it('Should throw error without PID', () => {
134
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingPID.html', 'utf-8');
135
- expect(() => { parseTwine2HTML(fr); }).toThrow();
115
+ const story = parseTwine2HTML(fr);
116
+ expect(story.start).toBe('');
136
117
  });
137
118
 
138
119
  it('Should parse tag colors', () => {
@@ -143,18 +124,6 @@ describe('Twine2HTMLParser', () => {
143
124
  expect(tagColors.a).toBe('red');
144
125
  });
145
126
 
146
- it('Should throw error if startnode is missing', () => {
147
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/missingStartnode.html', 'utf-8');
148
- expect(() => { parseTwine2HTML(fr); }).toThrow();
149
- });
150
-
151
- it('Should throw error when startnode has PID that does not exist', () => {
152
- const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/lyingStartnode.html', 'utf-8');
153
- expect(() => {
154
- parseTwine2HTML(fr);
155
- }).toThrow();
156
- });
157
-
158
127
  it('Do not update name and color if those attributes do not exist', () => {
159
128
  const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/lyingTagColors.html', 'utf-8');
160
129
  const story = parseTwine2HTML(fr);
@@ -163,10 +132,61 @@ describe('Twine2HTMLParser', () => {
163
132
  });
164
133
  });
165
134
 
166
- describe('#escapeMetacharacters()', () => {
167
- it('Should escape metacharacters', () => {
168
- /* eslint no-useless-escape: "off" */
169
- expect(escapeMetacharacters('\\\{\\\}\\\[\\\]\\\\')).toBe('\\\\{\\\\}\\\\[\\\\]\\\\');
135
+ describe('Unescaping', () => {
136
+ it('Should unescape HTML metacharacters for passage searching', () => {
137
+ const fr = readFileSync('test/Twine2HTML/Twine2HTMLParser/unescaping.html', 'utf-8');
138
+ const story = parseTwine2HTML(fr);
139
+ expect(story.getPassageByName('"Test"').text).toBe('Success');
140
+ });
141
+ });
142
+
143
+ describe('Warnings', () => {
144
+ beforeEach(() => {
145
+ // Mock console.warn.
146
+ jest.spyOn(console, 'warn').mockImplementation();
147
+ });
148
+
149
+ afterEach(() => {
150
+ // Restore all mocks.
151
+ jest.restoreAllMocks();
152
+ });
153
+
154
+ it('Should generate a warning if name attribute is missing from tw-storydata', () => {
155
+ const s = '<tw-storydata ifid=\'E70FC479-01D9-4E44-AC6A-AFF9F5E1C475\'></tw-storydata>';
156
+ parseTwine2HTML(s);
157
+ expect(console.warn).toHaveBeenCalledWith('Warning: The name attribute is missing from tw-storydata!');
158
+ });
159
+
160
+ it('Should generate a warning if ifid attribute is missing from tw-storydata', () => {
161
+ const s = '<tw-storydata name=\'Test\'></tw-storydata>';
162
+ parseTwine2HTML(s);
163
+ expect(console.warn).toHaveBeenCalledWith('Warning: The ifid attribute is missing from tw-storydata!');
164
+ });
165
+
166
+ it('Should generate a warning if ifid on tw-storydata is malformed', () => {
167
+ const s = '<tw-storydata ifid=\'1234\'></tw-storydata>';
168
+ parseTwine2HTML(s);
169
+ expect(console.warn).toHaveBeenCalledWith('Warning: The IFID is not in valid UUIDv4 formatting on tw-storydata!');
170
+ });
171
+
172
+ it('Should generate warning if passage name is missing', () => {
173
+ const fr = `<tw-storydata name="Tags" startnode="1" creator="Twine" creator-version="2.3.9" ifid="1A6023FC-F68A-4E55-BE9A-5EDFDB7879E6" zoom="1" format="Harlowe" format-version="3.1.0" options="" hidden>
174
+ <style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css"></style>
175
+ <script role="script" id="twine-user-script" type="text/twine-javascript"></script>
176
+ <tw-passagedata pid="1" tags="this-one another-one-like-this" position="200,99" size="100,100">Double-click this passage to edit it.</tw-passagedata>
177
+ </tw-storydata>`;
178
+ parseTwine2HTML(fr);
179
+ expect(console.warn).toHaveBeenCalledWith('Warning: name attribute is missing! Default passage name will be used.');
180
+ });
181
+
182
+ it('Should generate error if passage PID is missing', () => {
183
+ const fr = `<tw-storydata name="Tags" startnode="1" creator="Twine" creator-version="2.3.9" ifid="1A6023FC-F68A-4E55-BE9A-5EDFDB7879E6" zoom="1" format="Harlowe" format-version="3.1.0" options="" hidden>
184
+ <style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css"></style>
185
+ <script role="script" id="twine-user-script" type="text/twine-javascript"></script>
186
+ <tw-passagedata name="Untitled Passage" tags="this-one another-one-like-this" position="200,99" size="100,100">Double-click this passage to edit it.</tw-passagedata>
187
+ </tw-storydata>`;
188
+ parseTwine2HTML(fr);
189
+ expect(console.warn).toHaveBeenCalledWith('Warning: pid attribute is missing! Default PID will be used.');
170
190
  });
171
191
  });
172
192
  });
@@ -0,0 +1,9 @@
1
+ window.storyFormat({
2
+ "name": "Test Story Format",
3
+ "version": "1.0.0",
4
+ "author": "Dan Cox",
5
+ "description": "A test story format.",
6
+ "source": "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>{{STORY_NAME}}</title><style>body { font-family: Arial, sans-serif; }</style></head><body>{{STORY_DATA}}</body></html>",
7
+ "license": "MIT",
8
+ "proofing": false
9
+ });
@@ -6,6 +6,6 @@
6
6
  </head>
7
7
  <body>
8
8
  <tw-story></tw-story>
9
- <tw-storydata name="Tags" startnode="1" creator="Twine" creator-version="2.3.9" ifid="1A6023FC-F68A-4E55-BE9A-5EDFDB7879E6 format="Harlowe" format-version="3.1.0" options="" hidden><style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css"></style><script role="script" id="twine-user-script" type="text/twine-javascript"></script><tw-passagedata pid="1" name="Untitled Passage" tags="this-one another-one-like-this" position="200,99" size="100,100">Double-click this passage to edit it.</tw-passagedata></tw-storydata>
9
+ <tw-storydata name="Tags" startnode="1" creator="Twine" creator-version="2.3.9" ifid="1A6023FC-F68A-4E55-BE9A-5EDFDB7879E6" format="Harlowe" format-version="3.1.0" options="" hidden><style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css"></style><script role="script" id="twine-user-script" type="text/twine-javascript"></script><tw-passagedata pid="1" name="Untitled Passage" tags="this-one another-one-like-this" position="200,99" size="100,100">Double-click this passage to edit it.</tw-passagedata></tw-storydata>
10
10
  </body>
11
11
  </html>