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.
- package/.eslintrc.json +25 -25
- package/.github/FUNDING.yml +3 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/nodejs.yml +25 -24
- package/.travis.yml +13 -13
- package/CODE_OF_CONDUCT.md +82 -82
- package/LICENSE +21 -21
- package/README.md +173 -36
- package/SECURITY.md +12 -12
- package/babel.config.json +18 -22
- package/build/extwee +0 -0
- package/build/extwee.exe +0 -0
- package/build/extwee.web.min.js +2 -0
- package/build/extwee.web.min.js.LICENSE.txt +1 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +167 -0
- package/docs/_sidebar.md +19 -0
- package/docs/examples/dynamicPassages.md +28 -0
- package/docs/examples/jsonToTwee.md +23 -0
- package/docs/examples/twsToTwee.md +25 -0
- package/docs/formats/json.md +17 -0
- package/docs/formats/twee.md +13 -0
- package/docs/formats/twine1HTML.md +13 -0
- package/docs/formats/twine2ArchiveHTML.md +13 -0
- package/docs/formats/twine2HTML.md +13 -0
- package/docs/formats/tws.md +9 -0
- package/docs/index.html +26 -0
- package/docs/install/binaries.md +9 -0
- package/docs/install/npm.md +20 -0
- package/docs/install/npx.md +9 -0
- package/docs/objects/passage.md +47 -0
- package/docs/objects/story.md +70 -0
- package/docs/objects/storyformat.md +27 -0
- package/index.html +22 -0
- package/index.js +29 -31
- package/package.json +65 -58
- package/src/JSON/parse.js +128 -0
- package/src/Passage.js +298 -202
- package/src/Story.js +650 -523
- package/src/StoryFormat/parse.js +134 -0
- package/src/StoryFormat.js +259 -300
- package/src/TWS/parse.js +86 -0
- package/src/Twee/parse.js +157 -0
- package/src/Twine1HTML/compile.js +58 -0
- package/src/Twine1HTML/parse.js +134 -0
- package/src/Twine2ArchiveHTML/compile.js +36 -0
- package/src/Twine2ArchiveHTML/parse.js +49 -0
- package/src/Twine2HTML/compile.js +35 -0
- package/src/Twine2HTML/parse.js +348 -0
- package/src/extwee.js +206 -0
- package/test/CLI/CLI.test.js +49 -0
- package/test/CLI/files/example.json +1 -0
- package/test/CLI/files/example6.twee +22 -0
- package/test/{Roundtrip → CLI/files}/harlowe.js +2 -2
- package/test/CLI/{input.html → files/input.html} +47 -47
- package/test/CLI/files/output/test.twee +0 -0
- package/test/CLI/{tweeExample.twee → files/tweeExample.twee} +17 -17
- package/test/CLI/files/twine1/LICENSE.txt +32 -0
- package/test/CLI/files/twine1/code.js +5 -0
- package/test/CLI/files/twine1/engine.js +43 -0
- package/test/CLI/files/twine1/header.html +325 -0
- package/test/CLI/files/twine1Test.html +371 -0
- package/test/CLI/{twineExample.html → files/twineExample.html} +16 -15
- package/test/JSON/JSON.Parse.test.js +316 -0
- package/test/Passage.test.js +175 -104
- package/test/Roundtrip/{Example1.html → Files/Example1.html} +63 -63
- package/test/Roundtrip/Files/LICENSE +19 -0
- package/test/Roundtrip/Files/example1.twee +10 -0
- package/test/Roundtrip/{example2.twee → Files/example2.twee} +27 -18
- package/test/Roundtrip/{example4.twee → Files/example4.twee} +27 -27
- package/test/{StoryFormatParser → Roundtrip/Files}/harlowe.js +2 -2
- package/test/Roundtrip/{round.html → Files/round.html} +6 -4
- package/test/Roundtrip/Roundtrip.test.js +54 -0
- package/test/Story.test.js +638 -423
- package/test/StoryFormat/StoryFormat.Parse.test.js +91 -0
- package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example.js +3 -3
- package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example2.js +3 -3
- package/test/{CLI → StoryFormat/StoryFormatParser}/harlowe.js +2 -2
- package/test/StoryFormat.test.js +152 -152
- package/test/TWS/Parse.test.js +78 -0
- package/test/TWS/TWSParser/Example1.tws +150 -0
- package/test/TWS/TWSParser/Example5.tws +414 -0
- package/test/TWS/TWSParser/noscale.tws +0 -0
- package/test/TWS/TWSParser/nostory.tws +0 -0
- package/test/Twee/Twee.Parse.test.js +76 -0
- package/test/{TweeParser → Twee/TweeParser}/emptytags.twee +2 -2
- package/test/{TweeParser → Twee/TweeParser}/example.twee +32 -32
- package/test/{TweeParser → Twee/TweeParser}/missing.twee +19 -19
- package/test/{TweeParser → Twee/TweeParser}/multipleScriptPassages.twee +19 -19
- package/test/{TweeParser → Twee/TweeParser}/multipleStyleTag.twee +19 -19
- package/test/{TweeParser → Twee/TweeParser}/multipletags.twee +10 -10
- package/test/{TweeParser → Twee/TweeParser}/noTitle.twee +2 -2
- package/test/{TweeParser → Twee/TweeParser}/notes.twee +16 -16
- package/test/{TweeParser → Twee/TweeParser}/pasagemetadataerror.twee +2 -2
- package/test/{TweeParser → Twee/TweeParser}/scriptPassage.twee +16 -16
- package/test/{TweeParser → Twee/TweeParser}/singletag.twee +13 -13
- package/test/{TweeParser → Twee/TweeParser}/startMetadata.twee +14 -14
- package/test/{TweeParser → Twee/TweeParser}/storydataerror.twee +25 -25
- package/test/{TweeParser → Twee/TweeParser}/stylePassage.twee +16 -16
- package/test/{Story → Twee/TweeParser}/test.twee +25 -25
- package/test/Twine1HTML/Twine1HTML.Compile.test.js +180 -0
- package/test/Twine1HTML/Twine1HTML.Parse.test.js +183 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/LICENSE +674 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/engine.js +43 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/jquery.js +4 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/modernizr.js +4 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/engineTest.html +1 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/LICENSE +32 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/code.js +4 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/header.html +327 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test.html +0 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test1.html +6 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test2.html +6 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test3.html +43 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test4.html +372 -0
- package/test/Twine1HTML/Twine1HTMLCompiler/test5.html +372 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Compile.test.js +35 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +25 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLCompiler/test1.html +6 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLParser/test1.html +3 -0
- package/test/Twine2HTML/Twine2HTML.Compile.test.js +224 -0
- package/test/Twine2HTML/Twine2HTML.Parse.test.js +172 -0
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/creator.html +4 -4
- package/test/{CLI → Twine2HTML/Twine2HTMLCompiler}/example6.twee +15 -15
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/missingStoryTitle.twee +29 -29
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test2.html +10 -8
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test3.html +1 -1
- package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test4.html +4 -4
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Example1.html +52 -52
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Tags.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingStartnode.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingTagColors.html +48 -48
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreator.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreatorVersion.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormat.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormatVersion.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingIFID.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingName.html +33 -33
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPID.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageName.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageTags.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPosition.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingScript.html +14 -14
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingSize.html +35 -35
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStartnode.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStyle.html +14 -14
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingZoom.html +11 -11
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample.html +23 -23
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample2.html +15 -15
- package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample3.html +15 -15
- package/web-index.js +29 -0
- package/webpack.config.js +12 -0
- package/bin/extwee.js +0 -47
- package/src/FileReader.js +0 -33
- package/src/HTMLParser.js +0 -345
- package/src/HTMLWriter.js +0 -231
- package/src/StoryFormatParser.js +0 -142
- package/src/TweeParser.js +0 -161
- package/src/TweeWriter.js +0 -121
- package/story-formats/chapbook-1.2.0/format.js +0 -1
- package/story-formats/chapbook-1.2.0/logo.svg +0 -1
- package/story-formats/harlowe-1.2.4/format.js +0 -1
- package/story-formats/harlowe-1.2.4/icon.svg +0 -78
- package/story-formats/harlowe-2.1.0/format.js +0 -2
- package/story-formats/harlowe-2.1.0/icon.svg +0 -78
- package/story-formats/harlowe-3.1.0/format.js +0 -3
- package/story-formats/harlowe-3.1.0/icon.svg +0 -78
- package/story-formats/paperthin-1.0.0/format.js +0 -1
- package/story-formats/paperthin-1.0.0/icon.svg +0 -5
- package/story-formats/snowman-1.4.0/format.js +0 -1
- package/story-formats/snowman-1.4.0/icon.svg +0 -436
- package/story-formats/snowman-2.0.2/format.js +0 -1
- package/story-formats/snowman-2.0.2/icon.svg +0 -436
- package/story-formats/sugarcube-1.0.35/LICENSE +0 -23
- package/story-formats/sugarcube-1.0.35/format.js +0 -1
- package/story-formats/sugarcube-1.0.35/icon.svg +0 -56
- package/story-formats/sugarcube-2.31.1/LICENSE +0 -22
- package/story-formats/sugarcube-2.31.1/format.js +0 -1
- package/story-formats/sugarcube-2.31.1/icon.svg +0 -56
- package/test/CLI/test2.html +0 -45
- package/test/CLI.test.js +0 -30
- package/test/FileReader/t1.txt +0 -1
- package/test/FileReader.test.js +0 -14
- package/test/HTMLParser.test.js +0 -177
- package/test/HTMLWriter/example6.twee +0 -16
- package/test/HTMLWriter.test.js +0 -287
- package/test/Roundtrip/example1.twee +0 -21
- package/test/Roundtrip.test.js +0 -48
- package/test/Story/startmeta.twee +0 -29
- package/test/StoryFormatParser.test.js +0 -91
- package/test/TweeParser/test.twee +0 -25
- package/test/TweeParser.test.js +0 -79
- package/test/TweeWriter/test1.twee +0 -18
- package/test/TweeWriter/test3.twee +0 -12
- package/test/TweeWriter/test4.twee +0 -14
- package/test/TweeWriter/test5.twee +0 -20
- package/test/TweeWriter/test6.twee +0 -15
- package/test/TweeWriter/test7.twee +0 -15
- package/test/TweeWriter.test.js +0 -107
- /package/test/CLI/{test.twee → files/test.twee} +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format_doublename.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingAuthor.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingDescription.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingImage.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingLicense.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingName.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingProofing.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingSource.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingURL.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingVersion.js +0 -0
- /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/versionWrong.js +0 -0
- /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/TestTags.html +0 -0
- /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test11.html +0 -0
- /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test6.html +0 -0
- /package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/tagColors.html +0 -0
|
@@ -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 };
|