Skip to content

Commit

Permalink
Merge pull request #449 from videlais/443-allow-publishing-stories-wi…
Browse files Browse the repository at this point in the history
…thout-a-starting-passage-set

443 allow publishing stories without a starting passage set
  • Loading branch information
videlais authored Jan 12, 2024
2 parents 044f6b5 + fbdbc62 commit ba632a8
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 218 deletions.
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { parse as parseTWS } from './src/TWS/parse.js';
import { compile as compileTwine1HTML } from './src/Twine1HTML/compile.js';
import { compile as compileTwine2HTML } from './src/Twine2HTML/compile.js';
import { compile as compileTwine2ArchiveHTML } from './src/Twine2ArchiveHTML/compile.js';
import { generate as generateIFID } from './src/IFID/generate.js';
import { Story } from './src/Story.js';
import Passage from './src/Passage.js';
import StoryFormat from './src/StoryFormat.js';
Expand All @@ -23,6 +24,7 @@ export {
compileTwine1HTML,
compileTwine2HTML,
compileTwine2ArchiveHTML,
generateIFID,
Story,
Passage,
StoryFormat
Expand Down
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@babel/eslint-parser": "^7.23.3",
"@babel/eslint-plugin": "^7.23.5",
"@babel/preset-env": "^7.23.8",
"@types/uuid": "^9.0.7",
"babel-loader": "^9.1.3",
"clean-jsdoc-theme": "^4.2.17",
"core-js": "^3.35.0",
Expand Down
14 changes: 14 additions & 0 deletions src/IFID/generate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { v4 } from 'uuid';

/**
* Generates an IFID based the Treaty of Babel.
* @see {@link https://babel.ifarchive.org/babel_rev11.html#the-ifid-for-an-html-story-file}
* @function generate
* @description Generates a new IFID.
* @returns {string} IFID
*/
function generate () {
return v4().toUpperCase();
}

export { generate };
4 changes: 4 additions & 0 deletions src/Passage.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export default class Passage {

/**
* @param {string} s - Name to replace
* @throws {Error} Name must be a String!
*/
set name (s) {
if (typeof s === 'string') {
Expand All @@ -74,6 +75,7 @@ export default class Passage {

/**
* @param {Array} t - Replacement array
* @throws {Error} Tags must be an array!
*/
set tags (t) {
// Test if tags is an array
Expand All @@ -93,6 +95,7 @@ export default class Passage {

/**
* @param {object} m - Replacement object
* @throws {Error} Metadata must be an object literal!
*/
set metadata (m) {
// Test if metadata was an object
Expand All @@ -111,6 +114,7 @@ export default class Passage {

/**
* @param {string} t - Replacement text
* @throws {Error} Text should be a String!
*/
set text (t) {
// Test if text is a String
Expand Down
167 changes: 103 additions & 64 deletions src/Story.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Passage from './Passage.js';
import { v4 as uuidv4 } from 'uuid';
import { generate as generateIFID } from './IFID/generate.js';
import { encode } from 'html-entities';

const creatorName = 'extwee';
Expand Down Expand Up @@ -74,7 +74,7 @@ class Story {
* Creates a story.
* @param {string} name - Name of the story.
*/
constructor (name = '') {
constructor (name = 'Untitled Story') {
// Every story has a name.
this.name = name;
// Store the creator.
Expand Down Expand Up @@ -119,13 +119,13 @@ class Story {
}

/**
* Interactive Fiction ID (IFID) of Story
* Interactive Fiction ID (IFID) of Story.
* @returns {string} IFID
*/
get IFID () { return this.#_IFID; }

/**
* @param {string} i - Replacement IFID
* @param {string} i - Replacement IFID.
*/
set IFID (i) {
if (typeof i === 'string') {
Expand Down Expand Up @@ -297,7 +297,7 @@ class Story {
// If it does, we ignore it and return.
if (this.getPassageByName(p.name) !== null) {
// Warn user
console.warn('Ignored passage with same name as existing one!');
console.warn('Warning: Ignored passage with same name as existing one!');
//
return;
}
Expand Down Expand Up @@ -456,7 +456,7 @@ class Story {
if (this.IFID === '') {
// Generate a new IFID for this work.
// Twine 2 uses v4 (random) UUIDs, using only capital letters.
metadata.ifid = uuidv4().toUpperCase();
metadata.ifid = generateIFID();
} else {
// Use existing (non-default) value.
metadata.ifid = this.IFID;
Expand Down Expand Up @@ -521,27 +521,41 @@ class Story {
*
* See: Twine 2 HTML Output
* (https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-htmloutput-spec.md)
*
* The only required attributes are `name` and `ifid`. All others are optional.
*
* @returns {string} Twine 2 HTML string
*/
toTwine2HTML () {
// Prepare HTML content.
// Twine 2 HTML starts with a <tw-storydata> element.
// See: Twine 2 HTML Output

// name: (string) Required. The name of the story.
//
// Maps to <tw-storydata name>.
//
let storyData = `<tw-storydata name="${ encode( this.name ) }"`;
// Passage Identification (PID) counter.
// (Twine 2 starts with 1, so we mirror that.)
let PIDcounter = 1;

// Does start exist?
if (this.start === '') {
// We can't create a Twine 2 HTML file without a starting passage.
throw new Error('No starting passage!');
// 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).
//
// Maps to <tw-storydata ifid>.
//
// Check if IFID exists.
if (this.IFID !== '') {
// Write the existing IFID.
storyData += ` ifid="${ this.IFID }"`;
} else {
// Generate a new IFID.
// Twine 2 uses v4 (random) UUIDs, using only capital letters.
storyData += ` ifid="${ generateIFID() }"`;
}

// Try to find starting passage.
// If it doesn't exist, we throw an error.
if (this.getPassageByName(this.start) === null) {
// We can't create a Twine 2 HTML file without a starting passage.
throw new Error('Starting passage not found');
}
// Passage Identification (PID) counter.
// (Twine 2 starts with 1, so we mirror that.)
let PIDcounter = 1;

// Set initial PID value.
let startPID = 1;
Expand All @@ -557,66 +571,89 @@ class Story {
PIDcounter++;
});

// Set starting passage PID.
storyData += ` startnode="${startPID}"`;

// Defaults to 'extwee' if missing.
storyData += ` creator="${ encode( this.creator ) }"`;

// Default to extwee version.
storyData += ` creator-version="${this.creatorVersion}"`;

// Check if IFID exists.
if (this.IFID !== '') {
// Write the existing IFID.
storyData += ` ifid="${this.IFID}"`;
} else {
// Generate a new IFID.
// Twine 2 uses v4 (random) UUIDs, using only capital letters.
storyData += ` ifid="${uuidv4().toUpperCase()}"`;
// startnode: (integer) Optional.
//
// Maps to <tw-storydata startnode>.
//
// Check if startnode exists.
if(this.start !== '') {
// Set starting passage PID.
storyData += ` startnode="${startPID}"`;
}

// creator: (string) Optional. The name of the program that created the story.
// Maps to <tw-storydata creator>.
if(this.creator !== '') {
// Write existing creator.
storyData += ` creator="${ encode( this.creator ) }"`;
}

// Write existing or default value.
storyData += ` zoom="${this.zoom}"`;
// creator-version: (string) Optional. The version of the program that created the story.
// Maps to <tw-storydata creator-version>.
if(this.creatorVersion !== '') {
// Default to extwee version.
storyData += ` creator-version="${this.creatorVersion}"`;
}

// Write existing or default value.
storyData += ` format="${ encode(this.#_format) }"`;
// zoom: (decimal) Optional. The zoom level of the story.
// Maps to <tw-storydata zoom>.
if(this.zoom !== 0) {
// Write existing or default value.
storyData += ` zoom="${this.zoom}"`;
}

// Write existing or default value.
storyData += ` format-version="${this.#_formatVersion}"`;
// format: (string) Optional. The format of the story.
// Maps to <tw-storydata format>.
if(this.format !== '') {
// Write existing or default value.
storyData += ` format="${this.format}"`;
}

// format-version: (string) Optional. The version of the format of the story.
// Maps to <tw-storydata format-version>.
if(this.formatVersion !== '') {
// Write existing or default value.
storyData += ` format-version="${this.formatVersion}"`;
}

// Add the default attributes.
storyData += ' options hidden>\n';

// Start the STYLE.
storyData += '\t<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">';

// Get stylesheet passages.
// Get any stylesheet passages.
const stylesheetPassages = this.getPassagesByTag('stylesheet');

// Concatenate passages.
stylesheetPassages.forEach((passage) => {
// Add text of passages.
storyData += passage.text;
});
// Were there any stylesheet passages?
if (stylesheetPassages.length > 0) {
// Start the STYLE.
storyData += '\t<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">';

// Close the STYLE.
storyData += '</style>\n';
// Concatenate passages.
stylesheetPassages.forEach((passage) => {
// Add text of passages.
storyData += passage.text;
});

// Start the SCRIPT.
storyData += '\t<script role="script" id="twine-user-script" type="text/twine-javascript">';
// Close the STYLE.
storyData += '</style>\n';
}

// Get stylesheet passages.
// Get any stylesheet passages.
const scriptPassages = this.getPassagesByTag('script');

// Concatenate passages.
scriptPassages.forEach((passage) => {
// Add text of passages.
storyData += passage.text;
});
// Were there any script passages?
if (scriptPassages.length > 0) {
// Start the SCRIPT.
storyData += '\t<script role="script" id="twine-user-script" type="text/twine-javascript">';

// Close SCRIPT.
storyData += '</script>\n';
// Concatenate passages.
scriptPassages.forEach((passage) => {
// Add text of passages.
storyData += passage.text;
});

// Close SCRIPT.
storyData += '</script>\n';
}

// Reset the PID counter.
PIDcounter = 1;
Expand Down Expand Up @@ -656,6 +693,8 @@ class Story {
// Return Twine 1 HTML content.
return outputContents;
}


}

export { Story, creatorName, creatorVersion };
Loading

0 comments on commit ba632a8

Please sign in to comment.