diff --git a/src/Twine2HTMLParser.js b/src/Twine2HTMLParser.js
index 1ae13aa4..affdd930 100644
--- a/src/Twine2HTMLParser.js
+++ b/src/Twine2HTMLParser.js
@@ -25,7 +25,10 @@ export default class Twine2HTMLParser {
* @returns {Story} Story
*/
static parse (content) {
- let story = null;
+ // Create new story.
+ const story = new Story();
+
+ // Set default start node.
let startNode = null;
// Send to node-html-parser
@@ -40,103 +43,99 @@ export default class Twine2HTMLParser {
pre: true
});
- // Pull out the tw-storydata element
+ // Pull out the `` element.
const storyData = dom.querySelector('tw-storydata');
- // Does the element exist?
- if (storyData !== null) {
- // Create a Story.
- story = new Story();
+ // If there was no element, we cannot continue.
+ if (storyData === null) {
+ // If there is not a element, this is not a Twine 2 story!
+ throw new Error('Not Twine 2 HTML content!');
+ }
- /**
- * name: (string) Required.
- * The name of the story.
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'name')) {
- // Create StoryTitle passage based on name
- story.addPassage(new Passage('StoryTitle', storyData.attributes.name));
- // Set the story name
- story.name = storyData.attributes.name;
- } else {
- // Name is a required filed. Warn user.
- console.warn('Twine 2 HTML must have a name!');
- // Set a default name
- story.addPassage(new Passage('StoryTitle', 'Untitled'));
- }
+ /**
+ * name: (string) Required.
+ * The name of the story.
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'name')) {
+ // Set the story name
+ story.name = storyData.attributes.name;
+ } else {
+ // Name is a required field. Warn user.
+ console.warn('Twine 2 HTML must have a name!');
+ }
- /**
- * ifid: (string) Required.
- * An IFID is a sequence of between 8 and 63 characters,
- * each of which shall be a digit, a capital letter or a
- * hyphen that uniquely identify a story (see Treaty of Babel).
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'ifid')) {
- // Update story IFID
- story.IFID = storyData.attributes.ifid;
- } else {
- // Name is a required filed. Warn user.
- console.warn('Twine 2 HTML must have an IFID!');
- }
+ /**
+ * ifid: (string) Required.
+ * An IFID is a sequence of between 8 and 63 characters,
+ * each of which shall be a digit, a capital letter or a
+ * hyphen that uniquely identify a story (see Treaty of Babel).
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'ifid')) {
+ // Update story IFID.
+ story.IFID = storyData.attributes.ifid;
+ } else {
+ // Name is a required filed. Warn user.
+ console.warn('Twine 2 HTML must have an IFID!');
+ }
- /**
- * creator: (string) Optional.
- * The name of program used to create the file.
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'creator')) {
- // Update story creator
- story.creator = storyData.attributes.creator;
- }
+ /**
+ * creator: (string) Optional.
+ * The name of program used to create the file.
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'creator')) {
+ // Update story creator
+ story.creator = storyData.attributes.creator;
+ }
- /**
- * creator-version: (string) Optional.
- * The version of the program used to create the file.
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'creator-version')) {
- // Update story creator version
- story.creatorVersion = storyData.attributes['creator-version'];
- }
+ /**
+ * creator-version: (string) Optional.
+ * The version of the program used to create the file.
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'creator-version')) {
+ // Update story creator version
+ story.creatorVersion = storyData.attributes['creator-version'];
+ }
- /**
- * format: (string) Optional.
- * The story format used to create the story.
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'format')) {
- // Update story format
- story.format = storyData.attributes.format;
- }
+ /**
+ * format: (string) Optional.
+ * The story format used to create the story.
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'format')) {
+ // Update story format
+ story.format = storyData.attributes.format;
+ }
- /**
- * format-version: (string) Optional.
- * The version of the story format used to create the story.
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'format-version')) {
- // Update story format version
- story.formatVersion = storyData.attributes['format-version'];
- }
+ /**
+ * format-version: (string) Optional.
+ * The version of the story format used to create the story.
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'format-version')) {
+ // Update story format version
+ story.formatVersion = storyData.attributes['format-version'];
+ }
- /**
- * zoom: (string) Optional.
- * The decimal level of zoom (i.e. 1.0 is 100% and 1.2 would be 120% zoom level).
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'zoom')) {
- // Update story zoom
- story.zoom = Number(Number.parseFloat(storyData.attributes.zoom).toFixed(2));
- }
+ /**
+ * zoom: (string) Optional.
+ * The decimal level of zoom (i.e. 1.0 is 100% and 1.2 would be 120% zoom level).
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'zoom')) {
+ // Update story zoom
+ story.zoom = Number(Number.parseFloat(storyData.attributes.zoom).toFixed(2));
+ }
- /**
- * startnode: (string) Optional.
- * The PID matching a element whose content should be displayed first.
- */
- if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'startnode')) {
- // Take string value and convert to Int
- startNode = Number.parseInt(storyData.attributes.startnode, 10);
- }
+ /**
+ * startnode: (string) Optional.
+ * The PID matching a `` element whose content should be displayed first.
+ */
+ if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'startnode')) {
+ // Take string value and convert to Int
+ startNode = Number.parseInt(storyData.attributes.startnode, 10);
} else {
- // If there is not a element, this is not a Twine 2 story!
- throw new Error('Not a Twine 2-style file!');
+ // Throw error without start node.
+ throw new Error('Missing startnode in !');
}
- // Pull out the tw-passagedata elements
+ // Pull out the `` element.
const storyPassages = dom.querySelectorAll('tw-passagedata');
// Move through the passages
@@ -146,6 +145,11 @@ export default class Twine2HTMLParser {
// Get the passage text
const text = storyPassages[passage].rawText;
+ /**
+ * position: (string) Optional.
+ * Comma-separated X and Y position of the upper-left
+ * of the passage when viewed within the Twine 2 editor.
+ */
// Set a default position.
let position = null;
// Does position exist?
@@ -154,6 +158,11 @@ export default class Twine2HTMLParser {
position = attr.position;
}
+ /**
+ * size: (string) Optional.
+ * Comma-separated width and height of the
+ * passage when viewed within the Twine 2 editor.
+ */
// Set a default size.
let size = null;
// Does size exist?
@@ -175,7 +184,7 @@ export default class Twine2HTMLParser {
// Escape the name
name = Twine2HTMLParser.escapeMetacharacters(attr.name);
} else {
- console.warn('Encountered passage without a name! Will not add.');
+ throw new Error('Cannot parse passage data without name!');
}
// Create empty tag array.
@@ -228,6 +237,13 @@ export default class Twine2HTMLParser {
console.warn('Passages are required to have PID. Will not add!');
}
+ // Check the current PID against startNode number.
+ if (pid === startNode) {
+ // These match.
+ // Save the passage name.
+ story.start = name;
+ }
+
// If passage is missing name and PID (required attributes),
// they are not added.
if (name !== null && pid !== -1) {
@@ -237,13 +253,17 @@ export default class Twine2HTMLParser {
name,
text,
tags,
- metadata,
- pid
+ metadata
)
);
}
}
+ // There was an invalid startNode.
+ if (story.start === '') {
+ throw new Error('startNode does not exist within passages!');
+ }
+
// Look for the style element
const styleElement = dom.querySelector('#twine-user-stylesheet');
@@ -275,23 +295,10 @@ export default class Twine2HTMLParser {
}
}
- // Was there a startNode?
- if (startNode !== null) {
- // Try to find starting passage by PID.
- const startingPassage = story.getPassageByPID(startNode);
- // Does the passage exist (yet)?
- if (startingPassage !== null) {
- // If so, update property to name of passage.
- story.start = startingPassage.name;
- } else {
- throw new Error('Invalid startnode detected in !');
- }
- }
-
- // Look for all elements
+ // Look for all elements.
const twTags = dom.querySelectorAll('tw-tag');
- // Parse through the entries
+ // Parse through the entries.
twTags.forEach((tags) => {
// Parse each tag element
const attributes = tags.attributes;
diff --git a/test/Story/startmeta.twee b/test/Story/startmeta.twee
deleted file mode 100644
index 6dde9413..00000000
--- a/test/Story/startmeta.twee
+++ /dev/null
@@ -1,29 +0,0 @@
-:: StoryTitle
-twineExample
-
-:: Another
-This should be the start passage!
-
-:: Start
-Some content.
-
-:: StoryData
-{
- "ifid": "2B68ECD6-348F-4CF5-96F8-549A512A8128",
- "format": "Harlowe",
- "formatVersion": "2.1.0",
- "zoom": "1",
- "start": "Another"
-}
-
-:: Script1 [script]
-// Some code
-
-:: Script2 [script]
-//More code here!
-
-:: Style1 [stylesheet]
-body {font-size: 1.2em}
-
-:: Style2 [stylesheet]
-p {font-style: italic;}
diff --git a/test/Story/test.twee b/test/Story/test.twee
deleted file mode 100644
index ef6ce961..00000000
--- a/test/Story/test.twee
+++ /dev/null
@@ -1,25 +0,0 @@
-:: StoryTitle
-twineExample
-
-:: Start
-Some content.
-
-:: StoryData
-{
- "ifid": "2B68ECD6-348F-4CF5-96F8-549A512A8128",
- "format": "Harlowe",
- "formatVersion": "2.1.0",
- "zoom": "1"
-}
-
-:: Script1 [script]
-// Some code
-
-:: Script2 [script]
-//More code here!
-
-:: Style1 [stylesheet]
-body {font-size: 1.2em}
-
-:: Style2 [stylesheet]
-p {font-style: italic;}
diff --git a/test/TweeWriter/test1.twee b/test/TweeWriter/test1.twee
deleted file mode 100644
index a542f3ca..00000000
--- a/test/TweeWriter/test1.twee
+++ /dev/null
@@ -1,10 +0,0 @@
-:: StoryData
-{
- "ifid": "E0F01980-B9C6-4828-A31F-7FEFC5A164D2",
- "format": "Test",
- "format-version": "1.2.3",
- "zoom": 1,
- "start": "Untitled"
-}
-
-[object Object][object Object][object Object]
\ No newline at end of file
diff --git a/test/TweeWriter/test3.twee b/test/TweeWriter/test3.twee
deleted file mode 100644
index 1c3fa9a0..00000000
--- a/test/TweeWriter/test3.twee
+++ /dev/null
@@ -1,7 +0,0 @@
-:: StoryData
-{
- "ifid": "DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A",
- "start": "Start"
-}
-
-[object Object][object Object]
\ No newline at end of file
diff --git a/test/TweeWriter/test4.twee b/test/TweeWriter/test4.twee
deleted file mode 100644
index d11a0d04..00000000
--- a/test/TweeWriter/test4.twee
+++ /dev/null
@@ -1,14 +0,0 @@
-:: StoryData
-{
- "ifid": "D5D54A73-4E5F-4186-9276-EBDE28110006"
-}
-
-:: StoryTitle
-Title
-
-:: Start
-Content
-
-:: Untitled
-Some stuff
-
diff --git a/test/TweeWriter/test5.twee b/test/TweeWriter/test5.twee
deleted file mode 100644
index 4ab5bee3..00000000
--- a/test/TweeWriter/test5.twee
+++ /dev/null
@@ -1,12 +0,0 @@
-:: StoryData
-{
- "ifid": "BAD36C0B-0BD5-4A81-8EF7-255C9A6912A2",
- "start": "Start",
- "tag-colors": {
- "bar": "green",
- "foo": "red",
- "qaz": "blue"
- }
-}
-
-[object Object][object Object][object Object]
\ No newline at end of file
diff --git a/test/TweeWriter/test6.twee b/test/TweeWriter/test6.twee
deleted file mode 100644
index 0061f4f6..00000000
--- a/test/TweeWriter/test6.twee
+++ /dev/null
@@ -1,7 +0,0 @@
-:: StoryData
-{
- "ifid": "371ADF1A-CBF2-46BB-B17A-E19F7C921013",
- "start": "Start"
-}
-
-[object Object][object Object][object Object]
\ No newline at end of file
diff --git a/test/TweeWriter/test7.twee b/test/TweeWriter/test7.twee
deleted file mode 100644
index d84cb0c9..00000000
--- a/test/TweeWriter/test7.twee
+++ /dev/null
@@ -1,7 +0,0 @@
-:: StoryData
-{
- "ifid": "F183E248-D1CD-47FB-8C35-27BABB875CEA",
- "start": "Start"
-}
-
-[object Object][object Object][object Object]
\ No newline at end of file
diff --git a/test/Twine2HTMLParser.IGNORE.js b/test/Twine2HTMLParser.test.js
similarity index 85%
rename from test/Twine2HTMLParser.IGNORE.js
rename to test/Twine2HTMLParser.test.js
index 95d90fbb..ab1cd02b 100644
--- a/test/Twine2HTMLParser.IGNORE.js
+++ b/test/Twine2HTMLParser.test.js
@@ -14,8 +14,7 @@ describe('Twine2HTMLParser', () => {
it('Should be able to parse Twine 2 HTML for story name', () => {
const fr = FileReader.read('test/Twine2HTMLParser/twineExample.html');
const story = Twine2HTMLParser.parse(fr);
- const storyTitle = story.getPassageByName('StoryTitle');
- expect(storyTitle.text).toBe('twineExample');
+ expect(story.name).toBe('twineExample');
});
it('Should be able to parse Twine 2 HTML for correct number of passages', () => {
@@ -31,11 +30,10 @@ describe('Twine2HTMLParser', () => {
expect(p.tags).toHaveLength(2);
});
- it('Should set a missing name to Untitled', () => {
+ it('Should have default name', () => {
const fr = FileReader.read('test/Twine2HTMLParser/missingName.html');
const story = Twine2HTMLParser.parse(fr);
- const p = story.getPassageByName('StoryTitle');
- expect(p.text).toBe('Untitled');
+ expect(story.name).toBe('');
});
it('Should set a missing IFID to an empty string', () => {
@@ -118,27 +116,22 @@ describe('Twine2HTMLParser', () => {
expect(stylesheetPassages.length).toBe(1);
});
- it('Should not have start property if startNode does not exist when parsed', () => {
+ it('Should throw error if startNode is missing', () => {
const fr = FileReader.read('test/Twine2HTMLParser/missingStartnode.html');
- const story = Twine2HTMLParser.parse(fr);
- expect(story.start).toBe('');
+ expect(() => { Twine2HTMLParser.parse(fr); }).toThrow();
});
- it('Should not add passages without names', () => {
+ it('Should throw error if passage name is missing', () => {
const fr = FileReader.read('test/Twine2HTMLParser/missingPassageName.html');
- const story = Twine2HTMLParser.parse(fr);
- // There is only one passage, StoryTitle
- expect(story.size()).toBe(0);
+ expect(() => { Twine2HTMLParser.parse(fr); }).toThrow();
});
- it('Should not add passages without PID', () => {
+ it('Should throw error without PID', () => {
const fr = FileReader.read('test/Twine2HTMLParser/missingPID.html');
- const story = Twine2HTMLParser.parse(fr);
- // There is only one passage, StoryTitle
- expect(story.size()).toBe(0);
+ expect(() => { Twine2HTMLParser.parse(fr); }).toThrow();
});
- it('Parse tag colors', () => {
+ it('Should parse tag colors', () => {
const fr = FileReader.read('test/Twine2HTMLParser/tagColors.html');
const story = Twine2HTMLParser.parse(fr);
// Test for tag colors
@@ -146,14 +139,12 @@ describe('Twine2HTMLParser', () => {
expect(tagColors.a).toBe('red');
});
- it('Will not set start if startnode is missing', () => {
+ it('Should throw error if startnode is missing', () => {
const fr = FileReader.read('test/Twine2HTMLParser/missingStartnode.html');
- const story = Twine2HTMLParser.parse(fr);
- // Test for default start
- expect(story.start).toBe('');
+ expect(() => { Twine2HTMLParser.parse(fr); }).toThrow();
});
- it('Throw error when startnode has PID that does not exist', () => {
+ it('Should throw error when startnode has PID that does not exist', () => {
const fr = FileReader.read('test/Twine2HTMLParser/lyingStartnode.html');
expect(() => {
Twine2HTMLParser.parse(fr);