code:
◆はじめに
この度は同人サークル「満天工房」の処女作、「ゲームタイトル」をご購入頂きありがとうございます。
-------------------------------------------------
◆起動方法
「Game.exe」を実行してください。
-------------------------------------------------
◆諸注意
・本作品は性的描写を含む18禁のゲームです。18歳未満の方の購入、プレイを禁止します。
・本作品をプレイする事によって生じる全ての損害に対して、一切の責任を取りません。
・本作品の著作権は同人ゲーム制作サークル「満天工房」に帰属します。本作品に含まれる全てのデータの無断配布、無断転載を禁止します。
・本作品の内容はすべてフィクションです。実在の人物・団体・事件には一切関係有りません。
-------------------------------------------------
◆体験版からの引継ぎ
・体験版からの引継ぎには対応していません。
-------------------------------------------------
◆謝辞
本作品の制作にあたって下記の素材を使用させていただきました。
厚く御礼申し上げます。
-------------------------------------------------
◆アイコン素材
・建設中の茶柱　様：http://green.zero.jp/greentea/
(Emailで使用して良いか確認)
-------------------------------------------------
◆MAP素材
・沼江蛙 様
・HanapapaStudio 様
https://tetsu0910a.wixsite.com/hanapapastudio
・創作工房春巻 様
http://harumakikukan.web.fc2.com/index.html
・マップチップ習作所 様
-------------------------------------------------
◆エネミー画像素材
・ふわふわにゃんこ　様
https://www.dlsite.com/home/work/=/product_id/RJ274804.html
・マゼラン　様
https://ci-en.net/creator/13616
-------------------------------------------------
◆キャラ画像素材
・三日月アルペジオ 様
http://roughsketch.en-grey.com/
・白黒洋菓子店　様
https://noir-et-blanc-patisserie.amebaownd.com/
-------------------------------------------------
◆背景画像素材
・サファイアソフト　様
https://www.dlsite.com/home/circle/profile/=/maker_id/RG29505.html
・ぐったりにゃんこ　様
http://guttari8.sakura.ne.jp/kiyaku2.html
・きまぐれアフター　様
http://www5d.biglobe.ne.jp/~gakai/HTML/kiyaku.html
・消失点　様
http://www.aj.undo.jp/material/material.html
・Studio-74　様
http://www.studio-74.net/index-f.html
・OKUMONO　様
https://sozaino.site/qa
・DIMENSHOP　様
https://oblsk.sakura.ne.jp/3d/?page_id=184
・creativefreaks　様
https://creativefreaks.net/%e3%83%95%e3%83%aa%e3%83%bc%e7%b4%a0%e6%9d%90/
・みにくる　様
https://minikle.onlinestores.jp/
・meiz背景素材　様
https://booth.pm/ja/items/3805266
・NanbuWorks　様
https://nanbuyasumi.booth.pm/
・ササブネ屋　様
http://sasabuneya.starfree.jp/
・WinddorfBlog 様
https://winddorf.net
・七三ゆきのアトリエ　様
https://nanamiyuki.com/
・アキ二号機　様
https://www.pixiv.net/users/61071305
・みんちりえ　様
https://min-chi.material.jp/
-------------------------------------------------
◆キャラクターチップ素材
・BB ENTERTAINMENT　様
http://bb-entertainment-blog.blogspot.jp/
・ぴぽや　様
https://pipoya.net/sozai/
・冒険画材　様
https://gazai.5ing-myway.com/
・エタナラ　様
https://etanara.fungamemake.com
-------------------------------------------------
◆アニメーション素材
・tktk 様
https://materialforwardvfx.wixsite.com/materialforward
・Dreams-Circle　様
https://dreams-circle.itch.io/
-------------------------------------------------
◆サウンド素材
・ユーフルカ　様
https://wingless-seraph.net/material-music.html
・魔王魂　様
https://maou.audio/
・しんさんわーくす　様
https://www.dlsite.com/maniax/work/=/product_id/RJ269095.html
・グラネタ　様
https://everblasting.info/
・MusMus　様
https://musmus.main.jp/
・OtoLogic　様
https://otologic.jp/
・ayato sound create　様
https://ayatosoundcreate.wordpress.com/
・サウンドスケープライブラリ　様
http://soundscape.xyz/
・MoGura　様
https://twitter.com/MoGura87004851
・なぐりもず　様
https://nagumorizu.com/
・月に憑かれたピエロ　様
http://uyuu.jp/index.html
-------------------------------------------------
◆キャラクターボイス素材
・ボイスレック　様
https://www.voicerecest.com/
・Pincree　様
https://pincree.jp/
-------------------------------------------------
◆プラグイン素材
・トリアコンタン　様：https://triacontane.blogspot.com/
・tomoaky 様：https://twitter.com/tomoaky
・砂川赳　様：http://newrpg.seesaa.net/
・kido　様：https://twitter.com/kido0617
・ゆわか　様：https://twitter.com/yuwaka
・DarkPlasma　様：https://twitter.com/plasma_dark
・SumRndmDde　様：https://fungamemake.com/archives/9339
・むーてぃ　様：https://twitter.com/moooty10
・村人A　様：https://twitter.com/rpgmaker_villaA
・Galv　様：https://galvs-scripts.com/
・まっつＵＰ　様：https://twitter.com/mattuup
・COBRA　様：http://cobrara.blogspot.com/
・Yana　様：http://triacontane.blogspot.jp/
・神無月サスケ　様：https://twitter.com/ktakaki00
・シトラス　様：https://twitter.com/HnVdgnc
・さうと　様：https://shirakamisauto.hatenablog.com/
・坂本昌一郎　様：https://twitter.com/Jun_A_sakamoto
・Yoji Ojima　様：http://store.tkool.jp/a/rpgmaker/mv
・ルルの教会　様：https://nine-yusha.com/
・にゃたま　様：https://twitter.com/nyatama
・やな　様：https://twitter.com/yanatsuki_/
・かめお　様：http://ytomy.sakura.ne.jp/index.html
・くらむぼん　様：https://twitter.com/krmbn0576
・尾角 つの　様：https://twitter.com/vitsuno
・蔦森くいな　様：https://twitter.com/Kuina_T
・Yanfly　様：http://yanfly.moe/
・Trb　様：https://twitter.com/Trb_surasura
・gentlawk　様：https://twitter.com/gentlawk
・サンシロ　様：https://twitter.com/rev2nym
・ayatam　様：https://ayatam.wixsite.com/index
・TYPE74RX-T　様：https://mdc-light.jpn.org/TYPE74RX-T/
・さば缶　様：https://twitter.com/sabakan03
・かめお　様：https://plugin.fungamemake.com/archives/11209
・イロスマRPG制作委員会 様：https://w.atwiki.jp/nakasuma/pages/1.html
・しおいぬ　様：https://twitter.com/co_inu
・鳥小屋プラグイン置き場　様：https://torigoya-plugin.rutan.dev/
・Moghunter　様：https://mogplugins.wordpress.com/
-------------------------------------------------
◆制作ソフト
・RPGツクールMV　(C) 2015 KADOKAWA CORPORATION./YOJI OJIMA
-------------------------------------------------
◆ゲーム制作協力者様
・アクター画像製作
たか１　様

・タイトルロゴ、ゲームUI、PV動画
鴨野波子　様


code:
# WARNING: The `defaults` query must be explicitly spelled out or various
# plugins—e.g., `@babel/preset-env`—will not properly process the aggregate
# query.

> 1% or last 3 versions or last 10 Chrome versions or last 10 Firefox versions or IE >= 9 or Opera >= 12

path: .browserslistrc


code:
# ignore directories
src/templates/*
src/vendor/*

# ignore _{name} directories and files
**/_*

path: .eslintignore


array-bracket-spacing: error never

arrow-body-style: error as-needed

arrow-parens: error as-needed

properties: always

class-methods-use-this: error

comma-dangle: error never

comma-style: error last

consistent-return: off

default-case: off

dot-location: error property

dot-notation: error

eol-last: error

null: ignore

func-call-spacing: error never

func-name-matching: error always

generator-star-spacing: error after

exceptions: O P T _ a b i j m w x y

ArrayExpression: first

arguments: first

parameters: first

ObjectExpression: first

align: colon

keyword-spacing: error

linebreak-style: error unix

before: never

after: always

multiline-ternary: off

new-cap: error

new-parens: error

newline-after-var: off always

newline-before-return: off

no-alert: error

no-array-constructor: error

no-case-declarations: error

no-compare-neg-zero: error

allow: assert debug error info warn

no-const-assign: error

no-control-regex: error

no-debugger: error

no-div-regex: error

no-dupe-args: error

no-dupe-class-members: error

no-dupe-keys: error

no-duplicate-case: error

no-duplicate-imports: error

no-else-return: error

no-eval: error

no-extra-boolean-cast: error

no-extra-label: error

no-extra-semi: error

no-fallthrough: error

no-floating-decimal: error

no-global-assign: error

no-import-assign: error

no-iterator: error

no-label-var: error

no-loop-func: error

no-mixed-spaces-and-tabs: off smart-tabs

no-multi-assign: off

no-multi-spaces: off

no-nested-ternary: error

no-new: off

no-new-func: error

no-new-object: error

no-new-require: error

no-new-symbol: error

no-new-wrappers: error

no-plusplus: off

no-regex-spaces: error

no-restricted-globals: error isFinite isNaN

no-restricted-syntax: error WithStatement

no-return-await: error

no-sequences: error

no-template-curly-in-string: error

no-throw-literal: error

no-undef: error

no-undef-init: error

no-unneeded-ternary: error

no-unsafe-finally: error

no-unsafe-negation: error

vars: all

args: after-used

no-useless-call: error

no-useless-computed-key: error

no-useless-concat: error

no-useless-constructor: error

no-useless-escape: error

no-useless-rename: error

no-useless-return: error

no-var: error

no-whitespace-before-property: error

nonblock-statement-body-position: error beside

object-curly-spacing: error always

one-var: error never

padded-blocks: error never

prefer-arrow-callback: error

prefer-const: error

prefer-numeric-literals: error

prefer-promise-reject-errors: error


code:
## Auto detect text files and perform LF normalization
* text=auto
* text eol=lf

## The following files are binary and should be left untouched
# (note: `binary` is a macro attribute shorthand for `-text -diff`)

# Fonts
*.eot binary
*.ttf binary
*.woff binary
*.woff2 binary

# Images
*.gif binary
*.jpeg binary
*.jpg binary
*.pdf binary
*.png binary
*.ttf binary

# Archives
*.7z binary
*.gz binary
*.zip binary

path: .gitattributes


code:
# These are supported funding model platforms

# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
# github:

# Replace with a single Patreon username
patreon: thomasmedwards

# Replace with a single Open Collective username
# open_collective:

# Replace with a single Ko-fi username
ko_fi: thomasmedwards

# Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# tidelift:

# Replace with a single Community Bridge project-name e.g., cloud-foundry
# community_bridge:

# Replace with a single Liberapay username
# liberapay:

# Replace with a single IssueHunt username
# issuehunt:

# Replace with a single Otechie username
# otechie:

# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: https://cash.me/$thomasmedwards


code:
---
name: 🐞 Bug report
about: Report an issue with SugarCube v2
title: ''
labels: bug
assignees: ''

---

**Describe the bug.**
A clear and concise description of what the bug is.

**To Reproduce:**
Steps to reproduce the behavior:
1. Go to […]
2. Click on […]
Etc.

**Expected behavior.**
A clear and concise description of what you expected to happen.

**Screenshots.**
If applicable, add screenshots to help explain your problem.

**Project details.**
 - SugarCube Version: [e.g., 2.34.1]
 - Included Add-ons: [e.g., Chapel's Notify Macro, Cyrus' Click to Proceed Macro]
 - Included Libraries: [e.g., Moment.js, Tracery]
 - Compiler & Version: [e.g., Twine 2.3.13, Tweego 2.1.1]

**Desktop details.**
 - OS & Version: [e.g., Windows 10]
 - Browser & Version [e.g., Chrome 88 (64-bit), Firefox 85 (64-bit)]

**Smartphone/tablet details.**
 - Device: [e.g., iPhone Xs Max]
 - OS & Version: [e.g., iOS 14.4]
 - Browser & Version [e.g., Safari 14.4, Firefox for iOS 31]

**Additional context.**
Add any other context about the problem here.


code:
contact_links:
  - name: 🗣 Ask questions and discuss SugarCube v2
    url: https://github.com/tmedwards/sugarcube-2/discussions
    about: Get help using SugarCube v2


code:
---
name: 📝 Feature request
about: Request a new feature for SugarCube v2
title: ''
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem?**
A clear and concise description of what the problem is.  E.g., I'm always frustrated when […]

**Describe the solution you'd like.**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered.**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context.**
Add any other context or screenshots about the feature request here.


code:
# Scripts
*.sh

# Documentation
*.html

# Development
build/
node_modules/
.build
*.code-workspace
.tern-project
.vscode

# Miscellaneous
_*
CHANGELOG*
TODO*

path: .gitignore


code:
# JavaScript
**/*.js

# Vendored
vendor/

# Development
build/
node_modules/

path: .stylelintignore


code:
# SugarCube v2

[SugarCube](http://www.motoslave.net/sugarcube/) is a free (gratis and libre) story format for [Twine/Twee](http://twinery.org/).

Downloads and documentation may be found at [SugarCube website](http://www.motoslave.net/sugarcube/).

If you believe that you've found a bug in SugarCube or simply wish to make a suggestion, you may do so by [creating a new issue](https://github.com/tmedwards/sugarcube-2/issues).  SugarCube also has a [work log](https://github.com/tmedwards/sugarcube-2/projects/1) that may be of interest.

## INSTALLATION

You may either download one of the precompiled packages from [SugarCube's website](http://www.motoslave.net/sugarcube/) or build SugarCube from source—see **BUILDING FROM SOURCE** below.

## BUILDING FROM SOURCE

If you want to build SugarCube from scratch, rather than grabbing one of the pre-built packages off of its website, then these instructions are for you.

SugarCube uses Node.js (currently ≥v16) as the core of its build system, so you'll need to install it if you don't already have it.  Additionally, to retrieve SugarCube's source code from this repository, you'll need to install Git.

1. [Download and install the Node.js JavaScript runtime (`https://nodejs.org/`)](https://nodejs.org/)
2. [Download and install the Git source control management tool (`https://git-scm.com/`)](https://git-scm.com/)

Once all the tooling has been installed and set up, the next step is to fetch the SugarCube source code.  Open a shell to wherever you wish to store the code and run the following command to clone the repository:

```
git clone https://github.com/tmedwards/sugarcube-2.git
```

Next, change to the directory that the previous command created, which is your local clone of the repository:

```
cd sugarcube-2
```

There are two major branches within the repository:

* `master`: The stable release branch
* `develop`: The main development branch

Be sure to switch to the branch you wish to work on by issuing the appropriate `git checkout` command.

Once you're on the correct branch, fetch SugarCube's development dependencies:

```
npm install
```

You should now have SugarCube and all dependencies downloaded, so you may build it by running the following command:

```
node build.js
```

Assuming that completed with no errors, the story format, in Twine 1 and Twine 2 flavors, should be output to the `dist` directory.  Congratulations!

**NOTE:** SugarCube's development dependencies are occasionally updated.  If you receive errors when attempting to build, then you probably need to update your cached dependencies.  You may do this via the `npm update` command or, in extreme cases, by running `npm uninstall` and `npm install` in order.

**TIP:** If you'd like additional options when building—e.g., debug builds, limiting the build to a particular version of Twine, etc.—then you may request help from `build.js` by specifying the help (`-h`, `--help`) option.  For example:

```
node build.js -h
```


code:
#!/usr/bin/env node
/***********************************************************************************************************************

	build.js (v1.7.1, 2021-12-21)
		A Node.js-hosted build script for SugarCube.

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* eslint-env node, es2021 */
'use strict';

/*******************************************************************************
	Configuration
*******************************************************************************/

const CONFIG = {
	js : {
		files : [
			// The ordering herein is significant.
			'src/lib/alert.js',
			'src/lib/patterns.js',
			'src/lib/extensions.js',
			'src/lib/browser.js',
			'src/lib/has.js',
			'src/lib/visibility.js',
			'src/lib/fullscreen.js',
			'src/lib/helpers.js',
			'src/lib/jquery-plugins.js',
			'src/lib/util.js',
			'src/lib/simplestore/simplestore.js',
			'src/lib/simplestore/adapters/FCHost.Storage.js',
			'src/lib/simplestore/adapters/webstorage.js',
			'src/lib/simplestore/adapters/cookie.js',
			'src/lib/debugview.js',
			'src/lib/nodetyper.js',
			'src/stupidrandom.js',
			'src/lib/stylewrapper.js',
			'src/util/enumfrom.js',
			'src/lib/diff.js',
			'src/l10n/l10n.js',
			'src/l10n/legacy.js',
			'src/l10n/strings.js',
			'src/config.js',
			'src/simpleaudio.js',
			'src/state.js',
			'src/markup/scripting.js',
			'src/markup/lexer.js',
			'src/markup/wikifier.js',
			'src/markup/parserlib.js',
			'src/markup/template.js',
			'src/macros/macro.js',
			'src/macros/macrocontext.js',
			'src/macros/macrolib.js',
			'src/util/gettypeof.js',
			'src/dialog.js',
			'src/engine.js',
			'src/passage.js',
			'src/save.js',
			'src/setting.js',
			'src/story.js',
			'src/ui.js',
			'src/uibar.js',
			'src/debugbar.js',
			'src/loadscreen.js',
			'src/idb_backend.js',
			'src/hotkeys.js',
			'src/sugarcube.js'
		],
		wrap : {
			intro : 'src/templates/intro.js',
			outro : 'src/templates/outro.js'
		}
	},
	css : {
		mixins : 'src/css/_mixins.css',
		files  : [
			// The ordering herein is significant.
			'src/vendor/normalize.css',
			'src/css/init-screen.css',
			'src/css/font.css',
			'src/css/core.css',
			'src/css/core-display.css',
			'src/css/core-passage.css',
			'src/css/core-macro.css',
			'src/css/ui-dialog.css',
			'src/css/ui.css',
			'src/css/ui-bar.css',
			'src/css/idb_backend.css',
			'src/css/ui-debug.css'
		]
	},
	libs : [
		// The ordering herein is significant.
		'src/vendor/classList.min.js',
		'src/vendor/jquery.min.js',
		'src/vendor/jquery.ba-throttle-debounce.min.js',
		'src/vendor/imagesloaded.pkgd.min.js',
		'src/vendor/lz-string.min.js',
		'src/vendor/FileSaver.min.js',
		'src/vendor/console-hack.min.js'
	],
	twine1 : {
		build : {
			src  : 'src/templates/twine1/html.tpl',
			dest : 'build/twine1/sugarcube-2/header.html'
		},
		copy : [
			{
				src  : 'src/templates/twine1/sugarcube-2.py',
				dest : 'build/twine1/sugarcube-2/sugarcube-2.py'
			},
			{
				src  : 'LICENSE',
				dest : 'build/twine1/sugarcube-2/LICENSE'
			}
		]
	},
	twine2 : {
		build : {
			src  : 'src/templates/twine2/html.tpl',
			dest : 'build/twine2/sugarcube-2/format.js',
			json : 'src/templates/twine2/config.json'
		},
		copy : [
			{
				src  : 'icon.svg',
				dest : 'build/twine2/sugarcube-2/icon.svg'
			},
			{
				src  : 'LICENSE',
				dest : 'build/twine2/sugarcube-2/LICENSE'
			}
		]
	}
};

/*******************************************************************************
	Main Script
*******************************************************************************/

// NOTICE!
//
// Where string replacements are done, we use the replacement function style to
// disable all special replacement patterns, since some of them may exist within
// the replacement strings—e.g., `$&` within the HTML or JavaScript sources.

const {
	log,
	die,
	fileExists,
	makePath,
	copyFile,
	readFileContents,
	writeFileContents,
	concatFiles
} = require('./scripts/build-utils');
const _path = require('path');
const _opt  = require('node-getopt').create([
	['b', 'build=VERSION', 'Build only for Twine major version: 1 or 2; default: build for 2.'],
	['d', 'debug',         'Keep debugging code; gated by DEBUG symbol.'],
	['u', 'unminified',    'Suppress minification stages.'],
	['n', 'no-transpile',  'Suppress JavaScript transpilation stages. (unused)'],
	['t', 'transpile',     'Enable JavaScript transpilation stages.'],
	['h', 'help',          'Print this help, then exit.']
])
	.bindHelp()
	.parseSystem();

let _buildForTwine1 = false;
let _buildForTwine2 = true;

if (_opt.options.build) {
	switch (_opt.options.build) {
	case '1':
		_buildForTwine1 = true;
		_buildForTwine2 = false;
		break;

	case '2':
		_buildForTwine1 = false;
		break;

	default:
		die(`unknown Twine major version: ${_opt.options.build}; valid values: 1 or 2`);
		break;
	}
}

// Build the project.
(async () => {
	console.log('Starting builds...');

	// Create the build ID file, if nonexistent.
	if (!fileExists('.build')) {
		writeFileContents('.build', '0');
	}

	// Get the version info and build metadata.
	const version = (() => {
		const semver = require('semver');
		const { name, version } = require('./package.json'); // relative path must be prefixed ('./')
		const prerelease = semver.prerelease(version);

		return {
			title      : name,
			major      : semver.major(version),
			minor      : semver.minor(version),
			patch      : semver.patch(version),
			prerelease : prerelease && prerelease.length > 0 ? prerelease.join('.') : null,
			build      : Number(readFileContents('.build')) + 1,
			date       : new Date().toISOString(),

			toString() {
				const prerelease = this.prerelease ? `-${this.prerelease}` : '';
				return `${this.major}.${this.minor}.${this.patch}${prerelease}`;
			}
		};
	})();

	// Build for Twine 1.x.
	if (_buildForTwine1 && CONFIG.twine1) {
		console.log('\nBuilding Twine 1.x version:');

		// Process the header templates and write the outfiles.
		projectBuild({
			build     : CONFIG.twine1.build,
			version   : version, // eslint-disable-line object-shorthand
			libSource : assembleLibraries(CONFIG.libs),                        // combine the libraries
			appSource : await compileJavaScript(CONFIG.js, { twine1 : true }), // combine and minify the app JS
			cssSource : compileStyles(CONFIG.css)                              // combine and minify the app CSS
		});

		// Process the files that simply need copied into the build.
		projectCopy(CONFIG.twine1.copy);
	}

	// Build for Twine 2.x.
	if (_buildForTwine2 && CONFIG.twine2) {
		console.log('\nBuilding Twine 2.x version:');

		// Process the story format templates and write the outfiles.
		projectBuild({
			build     : CONFIG.twine2.build,
			version   : version, // eslint-disable-line object-shorthand
			libSource : assembleLibraries(CONFIG.libs),                         // combine the libraries
			appSource : await compileJavaScript(CONFIG.js, { twine1 : false }), // combine and minify the app JS
			cssSource : compileStyles(CONFIG.css),                              // combine and minify the app CSS

			postProcess(sourceString) {
				// Load the output format.
				let output = require(`./${_path.normalize(this.build.json)}`); // relative path must be prefixed ('./')

				// Merge data into the output format.
				output = Object.assign(output, {
					description : output.description.replace(
						/(['"`])\{\{BUILD_VERSION_MAJOR\}\}\1/g,
						() => this.version.major
					),
					version : this.version.toString(),
					source  : sourceString
				});

				// Wrap the output in the `storyFormat()` function.
				output = `window.storyFormat(${JSON.stringify(output)});`;

				return output;
			}
		});

		// Process the files that simply need copied into the build.
		projectCopy(CONFIG.twine2.copy);
	}

	// Update the build ID.
	writeFileContents('.build', String(version.build));
})()
	.then(() => console.log('\nBuilds complete!  (check the "build" directory)'))
	.catch(reason => console.log('\nERROR:', reason));

/*******************************************************************************
	Utility Functions
*******************************************************************************/
function assembleLibraries(filenames) {
	log('assembling libraries...');

	return concatFiles(filenames, contents => contents.replace(/^\n+|\n+$/g, ''));
}

function compileJavaScript(filenameObj, options) {
	log('compiling JavaScript...');

	// Join the files.
	let bundle = concatFiles(filenameObj.files);

	// Transpile to ES5 with Babel.
	if (_opt.options.transpile) {
		const { transform } = require('@babel/core');
		bundle = transform(bundle, {
			// babelHelpers : 'bundled',
			code     : true,
			compact  : false,
			presets  : [['@babel/preset-env']],
			filename : 'sugarcube.bundle.js'
		}).code;
	}

	bundle = `${readFileContents(filenameObj.wrap.intro)}\n${bundle}\n${readFileContents(filenameObj.wrap.outro)}`;

	return (async source => {
		if (_opt.options.unminified) {
			return [
				`window.TWINE1=${Boolean(options.twine1)}`,
				`window.DEBUG=${_opt.options.debug || false}`,
				source
			].join(';\n');
		}

		// Minify the code with Terser.
		const { minify } = require('terser');
		const minified   = await minify(source, {
			compress : {
				global_defs : { // eslint-disable-line camelcase
					TWINE1 : !!options.twine1,
					DEBUG  : _opt.options.debug || false
				}
			},
			mangle : false
		});

		if (minified.error) {
			const { message, line, col, pos } = minified.error;
			die(`JavaScript minification error: ${message}\n[@: ${line}/${col}/${pos}]`);
		}

		return minified.code;
	})(bundle);
}

function compileStyles(config) {
	log('compiling CSS...');

	const mixins       = require('postcss-mixins');
	const postcss      = require('postcss');
	const CleanCSS     = require('clean-css');
	const excludeRE    = /(?:normalize)\.css$/;
	const mixinContent = readFileContents(config.mixins);

	return concatFiles(config.files, (contents, filename) => {
		let css = contents;

		// Do not run the postcss plugins on files that match the exclusion regexp.
		if (!excludeRE.test(filename)) {
			css = `${mixinContent}\n${css}`;

			const processed = postcss([mixins]).process(css, { from : filename });

			css = processed.css;

			processed.warnings().forEach(mesg => console.warn(mesg.text));
		}

		if (!_opt.options.unminified) {
			css = new CleanCSS({
				level         : 1,
				compatibility : '*'
			})
				.minify(css)
				.styles;
		}

		const fileSlug = _path.basename(filename, '.css').toLowerCase().replace(/[^0-9a-z]+/g, '-');

		return `<style id="style-${fileSlug}" type="text/css">${css}</style>`;
	});
}

function projectBuild(project) {
	const infile  = _path.normalize(project.build.src);
	const outfile = _path.normalize(project.build.dest);

	log(`building: "${outfile}"`);

	// Load the story format template.
	let output = readFileContents(infile);

	// Process the source replacement tokens. (First!)
	output = output.replace(/(['"`])\{\{BUILD_LIB_SOURCE\}\}\1/, () => project.libSource);
	output = output.replace(/(['"`])\{\{BUILD_APP_SOURCE\}\}\1/, () => project.appSource);
	output = output.replace(/(['"`])\{\{BUILD_CSS_SOURCE\}\}\1/, () => project.cssSource);

	// Process the build replacement tokens.
	const prerelease = JSON.stringify(project.version.prerelease);
	const date       = JSON.stringify(project.version.date);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_MAJOR\}\}\1/g, () => project.version.major);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_MINOR\}\}\1/g, () => project.version.minor);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_PATCH\}\}\1/g, () => project.version.patch);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_PRERELEASE\}\}\1/g, () => prerelease);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_BUILD\}\}\1/g, () => project.version.build);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_DATE\}\}\1/g, () => date);
	output = output.replace(/(['"`])\{\{BUILD_VERSION_VERSION\}\}\1/g, () => project.version);

	// Post-process hook.
	if (typeof project.postProcess === 'function') {
		output = project.postProcess(output);
	}

	// Write the outfile.
	makePath(_path.dirname(outfile));
	writeFileContents(outfile, output);
}

function projectCopy(fileObjs) {
	fileObjs.forEach(file => {
		const infile  = _path.normalize(file.src);
		const outfile = _path.normalize(file.dest);

		log(`copying : "${outfile}"`);

		makePath(_path.dirname(outfile));
		copyFile(infile, outfile);
	});
}


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" version="1.1" inkscape:version="0.48.0 r9654">
  <defs id="defs4"/>
  <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="11.313708" inkscape:cx="27.203376" inkscape:cy="26.578528" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="1280" inkscape:window-height="968" inkscape:window-x="-4" inkscape:window-y="-4" inkscape:window-maximized="1">
    <inkscape:grid type="xygrid" id="grid2985" empspacing="5" visible="true" enabled="true" snapvisiblegridlinesonly="true"/>
  </sodipodi:namedview>
  <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1004.3622)">
    <g id="g3000" transform="translate(-6,1003)">
      <path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path2987" d="m 30,2.3621826 -20,10.0000004 20,10 20,-10 z" style="fill:#f9f9f9;stroke:none"/>
      <path style="fill:#ececec;stroke:none" d="m 30,22.362183 0,25 20,-10 0,-25 z" id="path2989" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/>
      <path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path2991" d="m 30,22.362183 0,25 -20,-10 0,-25 z" style="fill:#cccccc;stroke:none"/>
      <path inkscape:connector-curvature="0" id="path2993" d="M 30,2.3749996 10,12.375 l 0,25 20,10 20,-10 0,-25 L 30,2.3749996 z" style="fill:none;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/>
    </g>
  </g>
  <metadata>
    <rdf:RDF>
      <cc:Work>
        <dc:format>image/svg+xml</dc:format>
        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
        <cc:license rdf:resource="http://creativecommons.org/licenses/publicdomain/"/>
        <dc:publisher>
          <cc:Agent rdf:about="http://openclipart.org/">
            <dc:title>Open Clip Art Library</dc:title>
          </cc:Agent>
        </dc:publisher>
        <dc:title>Sugar Cube icon</dc:title>
        <dc:date>2010-10-10T11:46:52</dc:date>
        <dc:description>A sugar cube.</dc:description>
        <dc:source>http://openclipart.org/detail/89407/sugar-cube-icon-by-jhnri4</dc:source>
        <dc:creator>
          <cc:Agent>
            <dc:title>jhnri4</dc:title>
          </cc:Agent>
        </dc:creator>
        <dc:subject>
          <rdf:Bag>
            <rdf:li>SVG</rdf:li>
            <rdf:li>block</rdf:li>
            <rdf:li>clip art</rdf:li>
            <rdf:li>clipart</rdf:li>
            <rdf:li>cube</rdf:li>
            <rdf:li>icon</rdf:li>
            <rdf:li>sugar</rdf:li>
            <rdf:li>white</rdf:li>
          </rdf:Bag>
        </dc:subject>
      </cc:Work>
      <cc:License rdf:about="http://creativecommons.org/licenses/publicdomain/">
        <cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
        <cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
        <cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
      </cc:License>
    </rdf:RDF>
  </metadata>
</svg>


code:
# `Config` API {#config-api}

The `Config` object controls various aspects of SugarCube's behavior.

Note:
Config object settings should be placed within your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage).

## Audio Settings {#config-api-audio}

### `Config.audio.pauseOnFadeToZero` ↔ *boolean* (default: `true`) {#config-api-property-audio-pauseonfadetozero}

Determines whether the audio subsystem automatically pauses tracks that have been faded to `0` volume (silent).

#### History:

* `v2.28.0`: Introduced.

#### Examples:

```
Config.audio.pauseOnFadeToZero = false;
```

### `Config.audio.preloadMetadata` ↔ *boolean* (default: `true`) {#config-api-property-audio-preloadmetadata}

Determines whether the audio subsystem attempts to preload track metadata—meaning information about the track (e.g., duration), not its audio frames.

Note:
It is unlikely that you will ever want to disable this setting.

#### History:

* `v2.28.0`: Introduced.

#### Examples:

```
Config.audio.preloadMetadata = false;
```

## History Settings {#config-api-history}

### `Config.history.controls` ↔ *boolean* (default: `true`) {#config-api-property-history-controls}

Determines whether the story's history controls (Backward, Jump To, & Forward buttons) are enabled within the UI bar.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.history.controls = false;
```

### `Config.history.maxStates` ↔ *integer* (default: `40`) {#config-api-property-history-maxstates}

Sets the maximum number of states (moments) to which the history is allowed to grow.  Should the history exceed the limit, states will be dropped from the past (oldest first).

Tip:
For game-oriented projects, as opposed to more story-oriented interactive fiction, a setting of 1 is strongly recommended.

#### History:

* `v2.0.0`: Introduced.
* `v2.36.0`: Reduced the default to `40`.

#### Examples:

```
// Limit the history to a single state (recommended for games)
Config.history.maxStates = 1;

// Limit the history to 80 states
Config.history.maxStates = 80;
```

## Macros Settings {#config-api-macros}

### `Config.macros.ifAssignmentError` ↔ *boolean* (default: `true`) {#config-api-property-macros-ifassignmenterror}

Determines whether the [`<<if>>` macro](#macros-macro-if) returns an error when the `=` assignment operator is used within its conditional—e.g., `<<if $suspect = "Bob">>`.  Does not flag other assignment operators.

Note:
This setting exists because it's unlikely that you'll ever want to actually perform an assignment within a conditional expression and typing = when you meant === (or ==) is a fairly easy to mistake make—either from a finger slip or because you just don't know the difference between the operators.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// No error is returned when = is used within an <<if>> or <<elseif>> conditional
Config.macros.ifAssignmentError = false;
```

### `Config.macros.maxLoopIterations` ↔ *integer* (default: `1000`) {#config-api-property-macros-maxloopiterations}

Sets the maximum number of iterations allowed before the [`<<for>>` macro](#macros-macro-for) conditional forms are terminated with an error.

Note:
This setting exists to prevent a misconfigured loop from making the browser unresponsive.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Allow only 5000 iterations
Config.macros.maxLoopIterations = 5000;
```

### `Config.macros.typeSkipKey` ↔ *string* (default: `" "`, space) {#config-api-property-macros-typeskipkey}

Sets the default [`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value that causes the currently running [`<<type>>` macro](#macros-macro-type) instance to finish typing its content immediately.

#### History:

* `v2.33.1`: Introduced.

#### Examples:

```
// Change the default skip key to Control (CTRL)
Config.macros.typeSkipKey = "Control";
```

### `Config.macros.typeVisitedPassages` ↔ *boolean* (default: `true`) {#config-api-property-macros-typevisitedpassages}

Determines whether the [`<<type>>` macro](#macros-macro-type) types out content on previously visited passages or simply outputs it immediately.

#### History:

* `v2.32.0`: Introduced.

#### Examples:

```
// Do not type on previously visited passages
Config.macros.typeVisitedPassages = false;
```

## Navigation Settings {#config-api-navigation}

### `Config.navigation.override` ↔ *function* (default: *none*) {#config-api-property-navigation-override}

Allows the destination of passage navigation to be overridden.  The callback is passed one parameter, the original destination passage title.  If its return value is falsy, the override is cancelled and navigation to the original destination continues unperturbed.  If its return value is truthy, the override succeeds and that value is used as the new destination of the navigation.

#### History:

* `v2.13.0`: Introduced.

#### Examples:

```
Config.navigation.override = function (destinationPassage) {
	/* code */
};
```

##### Based upon a story variable

```
// Force the player to the "You Died" passage if they let $health get too low.
Config.navigation.override = function (dest) {
	var sv = State.variables;

	// If $health is less-than-or-equal to 0, go to the "You Died" passage instead.
	if (sv.health <= 0) {
		return "You Died";
	}
};
```

## Passages Settings {#config-api-passages}

### `Config.passages.descriptions` ↔ *boolean* | *object* | *function* (default: *none*) {#config-api-property-passages-descriptions}

Determines whether alternate passage descriptions are used by the *Saves* and *Jump To* menus—by default an excerpt from the passage is used.  Valid values are boolean `true`, which simply causes the passages' titles to be used, an object, which maps passages' titles to their descriptions, or a function, which should return the passages' description.

Note:

As boolean: You should ensure that all encounterable passage titles are meaningful.

As object: If the mapping object does not contain an entry for the passage in question, then the passage's excerpt is used instead.

As function: The function is called with the passage in question as its this value.  If the function returns falsy, then the passage's excerpt is used instead.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Uses passages' titles
Config.passages.descriptions = true;

// Uses the description mapped by the title
Config.passages.descriptions = {
	"title" : "description"
};

// Returns the description to be used
Config.passages.descriptions = function () {
	if (this.title === "title") {
		return "description";
	}
};
```

### `Config.passages.displayTitles` ↔ *boolean* (default: `false`) {#config-api-property-passages-displaytitles}

Determines whether passage titles are combined with the story title, within the browser's/tab's titlebar, when passages are displayed.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.passages.displayTitles = true;
```

### `Config.passages.nobr` ↔ *boolean* (default: `false`) {#config-api-property-passages-nobr}

Determines whether rendering passages have their leading/trailing newlines removed and all remaining sequences of newlines replaced with single spaces before they're rendered.  Equivalent to including the [`nobr` special tag](#special-tag-nobr) on every passage.

Note:
Does not affect script or stylesheet tagged passages, for Twine 1/Twee, or the Story JavaScript or Story Stylesheet sections, for Twine 2.

#### History:

* `v2.19.0`: Introduced.

#### Examples:

```
Config.passages.nobr = true;
```

### `Config.passages.onProcess` ↔ *function* (default: *none*) {#config-api-property-passages-onprocess}

Allows custom processing of passage text.  The function is invoked each time the [`<Passage>.processText()` method](#passage-api-prototype-method-processtext) is called.  It is passed an abbreviated version of the associated passage's [`Passage` instance](#passage-api)—containing only the `tags`, `text`, and `title` properties.  Its return value should be the post-processed text.

Note:
Does not affect script or stylesheet tagged passages, for Twine 1/Twee, or the Story JavaScript or Story Stylesheet sections, for Twine 2.

Note:
The function will be called just before the built-in no-break passage processing if you're also using that—see the Config.passages.nobr setting and nobr special tag.

#### History:

* `v2.30.0`: Introduced.

#### Examples:

```
/* Change instancess of "cat" to "dog". */
Config.passages.onProcess = function (p) {
	return p.text.replace(/\bcat(s?)\b/g, 'dog$1');
};
```

### `Config.passages.start` ↔ *string* (Twine 2 default: *user-selected*; Twine 1/Twee default: `"Start"`) {#config-api-property-passages-start}

Sets the starting passage, the very first passage that will be displayed.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.passages.start = "That Other Starting Passage";
```

### `Config.passages.transitionOut` ↔ *string* | *integer* (default: *none*) {#config-api-property-passages-transitionout}

Determines whether outgoing passage transitions are enabled.  Valid values are the name of the property being animated, which causes the outgoing passage element to be removed once that transition animation is complete, or an integer delay (in milliseconds), which causes the outgoing passage element to be removed once the delay has expired.  You will also need some CSS styles to make this work—examples given below.

Note:
If using an integer delay, ideally, it should probably be slightly longer than the outgoing transition delay that you intend to use—e.g., an additional 10ms or so should be sufficient.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Remove outgoing elements when their opacity animation ends
Config.passages.transitionOut = "opacity";

// Remove outgoing elements after 1010ms (1.01s)
Config.passages.transitionOut = 1010;
```

#### CSS styles:

At the very least you will need to specify a `.passage-out` style that defines the transition's end state.  For example:

```
.passage-out {
	opacity: 0;
}
```

That probably won't be very pleasing to the eye, however, so you will likely need several styles to make something that looks half-decent.  For example, the following will give you a basic crossfade:

```
#passages {
	position: relative;
}
.passage {
	left: 0;
	position: absolute;
	top: 0;
	transition: opacity 1s ease;
}
.passage-out {
	opacity: 0;
}
```

## Saves Settings {#config-api-saves}

### `Config.saves.autoload` ↔ *boolean* | *string* | *function* (default: *none*) {#config-api-property-saves-autoload}

Determines whether the autosave, if it exists, is automatically loaded upon story startup.  Valid values are boolean `true`, which simply causes the autosave to be loaded, the string `"prompt"`, which prompts the player via a dialog to load the autosave, or a function, which causes the autosave to be loaded if its return value is truthy.

Note:
If the autosave cannot be loaded, for any reason, then the start passage is loaded instead.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Automatically loads the autosave
Config.saves.autoload = true;

// Prompts the player about loading the autosave
Config.saves.autoload = "prompt";

// Loads the autosave if it returns a truthy value
Config.saves.autoload = function () {
	/* code */
};
```

### `Config.saves.autosave` ↔ *boolean* | *Array<string>* | *function* (default: *none*) {#config-api-property-saves-autosave}

Determines whether the autosave is created/updated when passages are displayed.

Valid values are:

* Boolean `true`, which causes the autosave to be updated with every passage.
* Boolean `false`, which causes the autosave to never update automatically—i.e., you must do it manually via the Save.autosave.save() static method.
* An array of strings, which causes the autosave to be updated for each passage with at least one matching tag.
* A function, which causes the autosave to be updated for each passage where its return value is truthy.

Warning:
When setting the value to boolean true, you will likely also need to use the Config.saves.isAllowed property to disallow saving on the start passage.  Or, if you use the start passage as real part of your story and allow the player to reenter it, rather than just as the initial landing/cover page, then you may wish to only disallow saving on the start passage the very first time it's displayed—i.e., at story startup.

#### History:

* `v2.0.0`: Introduced.
* `v2.30.0`: Added function values and deprecated string values.

#### Examples:

```
// Autosaves every passage
Config.saves.autosave = true;

// Allows manual autosaving
Config.saves.autosave = false;

// Autosaves on passages tagged with any of "bookmark" or "autosave"
Config.saves.autosave = ["bookmark", "autosave"];

// Autosaves on passages if it returns a truthy value
Config.saves.autosave = function () {
	/* code */
};
```

### `Config.saves.id` ↔ *string* (default: *slugified story title*) {#config-api-property-saves-id}

Sets the story ID associated with saves.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.saves.id = "a-big-huge-story-part-1";
```

### `Config.saves.isAllowed` ↔ *function* (default: *none*) {#config-api-property-saves-isallowed}

Determines whether saving is allowed within the current context.  The callback is invoked each time a save is requested.  If its return value is falsy, the save is disallowed.  If its return value is truthy, the save is allowed to continue unperturbed.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.saves.isAllowed = function () {
	/* code */
};
```

### `Config.saves.slots` *integer* (default: `8`) {#config-api-property-saves-slots}

Sets the maximum number of available save slots.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.saves.slots = 4;
```

### `Config.saves.tryDiskOnMobile` ↔ *boolean* (default: `true`) {#config-api-property-saves-trydiskonmobile}

Determines whether saving to disk is enabled on mobile devices—i.e., smartphones, tablets, etc.

Warning:
Mobile browsers can be fickle, so saving to disk may not work as expected in all browsers.

#### History:

* `v2.34.0`: Introduced.

#### Examples:

```
/* To disable saving to disk on mobile devices. */
Config.saves.tryDiskOnMobile = false;
```

### `Config.saves.version` ↔ *any* (default: *none*) {#config-api-property-saves-version}

Sets the `version` property of saves.

Note:
This setting is only used to set the version property of saves.  Thus, it is only truly useful if you plan to upgrade out-of-date saves via a Config.saves.onLoad callback.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// As an integer (recommended)
Config.saves.version = 3;

// As a string (not recommended)
Config.saves.version = "v3";
```

### `Config.saves.onLoad` ↔ *function* (default: *none*) {#config-api-property-saves-onload}

Deprecated:
This setting has been deprecated and should no longer be used.  See the Save.onLoad.add() method for its replacement.

#### History:

* `v2.0.0`: Introduced.
* `v2.36.0`: Deprecated in favor of the [`Save` Events API](#save-api-events).

### `Config.saves.onSave` ↔ *function* (default: *none*) {#config-api-property-saves-onsave}

Deprecated:
This setting has been deprecated and should no longer be used.  See the Save.onSave.add() method for its replacement.

#### History:

* `v2.0.0`: Introduced.
* `v2.33.0`: Added save operation details object parameter to the callback function.
* `v2.36.0`: Deprecated in favor of the [`Save` Events API](#save-api-events).

## UI Settings {#config-api-ui}

### `Config.ui.stowBarInitially` ↔ *boolean* | *integer* (default: `800`) {#config-api-property-ui-stowbarinitially}

Determines whether the UI bar (sidebar) starts in the stowed (shut) state initially.  Valid values are boolean `true`/`false`, which causes the UI bar to always/never start in the stowed state, or an integer, which causes the UI bar to start in the stowed state if the viewport width is less-than-or-equal-to the specified number of pixels.

#### History:

* `v2.11.0`: Introduced.

#### Examples:

```
// As a boolean; always start stowed
Config.ui.stowBarInitially = true;

// As a boolean; never start stowed
Config.ui.stowBarInitially = false;

// As an integer; start stowed if the viewport is 800px or less
Config.ui.stowBarInitially = 800;
```

### `Config.ui.updateStoryElements` ↔ *boolean* (default: `true`) {#config-api-property-ui-updatestoryelements}

Determines whether certain elements within the UI bar are updated when passages are displayed.  The affected elements are the story: banner, subtitle, author, caption, and menu.

Note:
The story title is not included in updates because SugarCube uses it as the basis for the key used to store and load data used when playing the story and for saves.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// If you don't need those elements to update
Config.ui.updateStoryElements = false;
```

## Miscellaneous Settings {#config-api-miscellaneous}

### `Config.addVisitedLinkClass` ↔ *boolean* (default: `false`) {#config-api-property-addvisitedlinkclass}

Determines whether the `link-visited` class is added to internal passage links that go to previously visited passages—i.e., the passage already exists within the story history.

Note:
You must provide your own styling for the link-visited class as none is provided by default.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.addVisitedLinkClass = true;
```

#### CSS styles:

You will also need to specify a `.link-visited` style that defines the properties visited links should have. For example:

```
.link-visited {
	color: purple;
}
```

### `Config.cleanupWikifierOutput` ↔ *boolean* (default: `false`) {#config-api-property-cleanupwikifieroutput}

Determines whether the output of the Wikifier is post-processed into more sane markup—i.e., where appropriate, it tries to transition the plethora of `<br>` elements into `<p>` elements.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Config.cleanupWikifierOutput = true;
```

### `Config.debug` ↔ *boolean* (default: `false`) {#config-api-property-debug}

Indicates whether SugarCube is running in test mode, which enables debug views.  See the [*Test Mode* guide](#guide-test-mode) for more information.

Note:
This property is automatically set based on whether you're using a testing mode in a Twine compiler—i.e., Test mode in Twine 2, Test Play From Here in Twine 1, or the test mode option (-t, --test) in Tweego.  You may, however, forcibly enable it if you need to for some reason—e.g., if you're using another compiler, which doesn't offer a way to enable test mode.

#### History:

* `v2.2.0`: Introduced.

#### Examples:

```
// Forcibly enable test mode
Config.debug = true;

// Check if test mode is enabled in JavaScript
if (Config.debug) {
	/* do something debug related */
}

// Check if test mode is enabled via the <<if>> macro
<<if Config.debug>>
	/* do something debug related */
<</if>>
```

### `Config.loadDelay` ↔ *integer* (default: `0`) {#config-api-property-loaddelay}

Sets the integer delay (in milliseconds) before the loading screen is dismissed, once the document has signaled its readiness.  Not generally necessary, however, some browsers render slower than others and may need a little extra time to get a media-heavy page done.  This allows you to fine tune for those cases.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Delay the dismissal of the loading screen by 2000ms (2s)
Config.loadDelay = 2000;
```


code:
# `Dialog` API {#dialog-api}

### `Dialog.append(content)` → *`Dialog` object* {#dialog-api-method-append}

Appends the given content to the dialog's content area.  Returns a reference to the `Dialog` object for chaining.

Note:
If your content contains any SugarCube markup, you'll need to use the Dialog.wiki() method instead.

#### History:

* `v2.9.0`: Introduced.

#### Parameters:

* **`content`:** The content to append.  As this method is essentially a shortcut for `jQuery(Dialog.body()).append(…)`, see [jQuery's `append()`](https://api.jquery.com/append/) method for the range of valid content types.

#### Examples:

```
Dialog.append("Cry 'Havoc!', and let slip the <em>ponies</em> of <strong>friendship</strong>.");

Dialog.append( /* some DOM nodes */ );
```

### `Dialog.body()` → *`HTMLElement` object* {#dialog-api-method-body}

Returns a reference to the dialog's content area.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
jQuery(Dialog.body()).append("Cry 'Havoc!', and let slip the <em>ponies</em> of <strong>friendship</strong>.");

jQuery(Dialog.body()).wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");
```

### `Dialog.close()` → *`Dialog` object* {#dialog-api-method-close}

Closes the dialog.  Returns a reference to the `Dialog` object for chaining.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Dialog.close();
```

### `Dialog.isOpen([classNames])` → *boolean* {#dialog-api-method-isopen}

Returns whether the dialog is currently open.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`classNames`:** (optional, *string*) The space-separated-list of classes to check for when determining the state of the dialog.  Each of the built-in dialogs contains a name-themed class that can be tested for in this manner—e.g., the Saves dialog contains the class `saves`.

#### Examples:

```
if (Dialog.isOpen()) {
	/* code to execute if the dialog is open… */
}

if (Dialog.isOpen("saves")) {
	/* code to execute if the Saves dialog is open… */
}
```

### `Dialog.open([options [, closeFn]])` → *`Dialog` object* {#dialog-api-method-open}

Opens the dialog.  Returns a reference to the `Dialog` object for chaining.

Note:
Call this only after populating the dialog with content.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`options`:** (optional, *null* | *object*) The options to be used when opening the dialog.
* **`closeFn`:** (optional, *null* | *function*) The function to execute whenever the dialog is closed.

#### Options object:

An options object should have some of the following properties:

* **`top`:** Top y-coordinate of the dialog (default: `50`; in pixels, but without the unit).

#### Examples:

```
Dialog.open();
```

### `Dialog.setup([title [, classNames]])` → *`HTMLElement` object* {#dialog-api-method-setup}

Prepares the dialog for use and returns a reference to its content area.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`title`:** (optional, *string*) The title of the dialog.
* **`classNames`:** (optional, *string*) The space-separated-list of classes to add to the dialog.

#### Examples:

```
// Basic example.
Dialog.setup();
Dialog.wiki("Blah //blah// ''blah''.");
Dialog.open();

// Adding a title to the dialog.
Dialog.setup("Character Sheet");
Dialog.wiki(Story.get("PC Sheet").processText());
Dialog.open();

// Adding a title and class to the dialog.
Dialog.setup("Character Sheet", "charsheet");
Dialog.wiki(Story.get("PC Sheet").processText());
Dialog.open();
```

### `Dialog.wiki(wikiMarkup)` → *`Dialog` object* {#dialog-api-method-wiki}

Renders the given [markup](#markup) and appends it to the dialog's content area.  Returns a reference to the `Dialog` object for chaining.

Note:
If your content consists of DOM nodes, you'll need to use the Dialog.append() method instead.

#### History:

* `v2.9.0`: Introduced.

#### Parameters:

* **`wikiMarkup`:** The markup to render.

#### Examples:

```
Dialog.wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");
```

### `Dialog.addClickHandler(targets [, options [, startFn [, doneFn [, closeFn]]]])` {#dialog-api-method-addclickhandler}

Deprecated:
This method has been deprecated and should no longer be used.  The core of what it does is simply to wrap a call to Dialog.open() within a call to <jQuery>.ariaClick(), which can be done directly and with greater flexibility.

#### History:

* `v2.0.0`: Introduced.
* `v2.29.0`: Deprecated.


code:
# `Engine` API {#engine-api}

### `Engine.lastPlay` → *number* {#engine-api-getter-lastplay}

Returns a timestamp representing the last time `Engine.play()` was called.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Engine.lastPlay  → The timestamp at which Engine.play() was last called
```

### `Engine.state` → *string* {#engine-api-getter-state}

Returns the current state of the engine (`"idle"`, `"playing"`, `"rendering"`).

#### History:

* `v2.7.0`: Introduced.

#### States:

* **`"idle"`:** The engine is idle, awaiting the triggering of passage navigation—the default state.
* **`"playing"`:** Passage navigation has been triggered and a turn is being processed.
* **`"rendering"`:** The incoming passage is being rendered and added to the page—takes place during turn processing, so implies `"playing"`.

#### Examples:

```
Engine.state  → Returns the current state of the engine
```

### `Engine.backward()` → *boolean* {#engine-api-method-backward}

Moves backward one moment within the full history (past + future), if possible, activating and showing the moment moved to.  Returns whether the history navigation was successful (should only fail if already at the beginning of the full history).

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.backward()  → Rewinds the full history by one moment—i.e., undoes the moment
```

### `Engine.forward()` → *boolean* {#engine-api-method-forward}

Moves forward one moment within the full history (past + future), if possible, activating and showing the moment moved to.  Returns whether the history navigation was successful (should only fail if already at the end of the full history).

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.forward()  → Fast forwards the full history by one moment—i.e., redoes the moment
```

### `Engine.go(offset)` → *boolean* {#engine-api-method-go}

Activates the moment at the given offset from the active (present) moment within the full state history and show it.  Returns whether the history navigation was successful (should only fail if the offset from the active (present) moment is not within the bounds of the full history).

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`offset`:** (*integer*) The offset from the active (present) moment of the moment to go to.

#### Examples:

```
Engine.go(2)   → Fast forwards the full history by two moments—i.e., redoes the moments
Engine.go(-4)  → Rewinds the full history by four moments—i.e., undoes the moments
```

### `Engine.goTo(index)` → *boolean* {#engine-api-method-goto}

Activates the moment at the given index within the full state history and show it.  Returns whether the history navigation was successful (should only fail if the index is not within the bounds of the full history).

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`index`:** (*integer*) The index of the moment to go to.

#### Examples:

```
Engine.goTo(0)  → Goes to the first moment
Engine.goTo(9)  → Goes to the tenth moment
```

### `Engine.isIdle()` → *boolean* {#engine-api-method-isidle}

Returns whether the engine is idle.

#### History:

* `v2.16.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.isIdle()  → Returns whether the engine is idle
```

### `Engine.isPlaying()` → *boolean* {#engine-api-method-isplaying}

Returns whether the engine is processing a turn—i.e., passage navigation has been triggered.

#### History:

* `v2.16.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.isPlaying()  → Returns whether the engine is playing
```

### `Engine.isRendering()` → *boolean* {#engine-api-method-isrendering}

Returns whether the engine is rendering the incoming passage.

#### History:

* `v2.16.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.isRendering()  → Returns whether the engine is rendering
```

### `Engine.play(passageTitle [, noHistory])` → *`HTMLElement` object* {#engine-api-method-play}

Renders and displays the passage referenced by the given title, optionally without adding a new moment to the history.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`passageTitle`:** (*string*) The title of the passage to play.
* **`noHistory`:** (optional, *boolean*) Disables the update of the history—i.e., no moment is added to the history.

#### Examples:

```
Engine.play("Foo")        → Renders, displays, and adds a moment for the passage "Foo" to the history
Engine.play("Foo", true)  → Renders and displays the passage "Foo", but does not add new history
```

### `Engine.restart()` {#engine-api-method-restart}

Restarts the story.

Warning:
The player will not be prompted and all unsaved state will be lost.

Note:
In general, you should not call this method directly.  Instead, call the UI.restart() static method, which prompts the player with an OK/Cancel dialog before itself calling Engine.restart(), if they accept.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.restart()  → Restarts the story
```

### `Engine.show()` → *`HTMLElement` object* {#engine-api-method-show}

Renders and displays the active (present) moment's associated passage without adding a new moment to the history.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Engine.show()  → Renders and displays the present passage without adding new history
```


code:
# `Fullscreen` API {#fullscreen-api}

Provides access to browsers' fullscreen functionality.

#### Backgrounds in fullscreen {#fullscreen-api-backgrounds}

If you wish to use custom backgrounds, either simply colors or with images, then you should place them on the `body` element.  For example:

```
body {
	background: #111 fixed url("images/background.png") center / contain no-repeat;
}
```

Warning:
It is strongly recommended that you do not place background properties on the html element in addition to the body element as this can cause background jitter in Internet Explorer when scrolling outside of fullscreen mode.

Warning:
If setting a background image via the background shorthand property, then you should also specify a background-color value with it or include a separate background-color property after the background property.  The reason being is that the background property resets the background color, so if you do not set one either as one of its values or via a following background-color property, then the browser's default background color could show through if the background image does not cover the entire viewport or includes transparency.

#### Fullscreen limitations {#fullscreen-api-limitations}

The Fullscreen API comes with some built-in limitations:

1. Fullscreen requests must be initiated by the player, generally via click/touch—i.e., the request must be made as a result of player interaction; e.g., activating a button/link/etc whose code makes the request.

### `Fullscreen.element` → *`HTMLElement` object* | *null* {#fullscreen-api-getter-element}

Returns the current fullscreen element or, if fullscreen mode is not active, `null`.

#### History:

* `v2.31.0`: Introduced.

#### Examples:

```
Fullscreen.element  → The current fullscreen element
```

### `Fullscreen.isEnabled()` → *boolean* {#fullscreen-api-method-isenabled}

Returns whether fullscreen is both supported and enabled.

#### History:

* `v2.31.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Fullscreen.isEnabled()  → Whether fullscreen mode is available
```

### `Fullscreen.isFullscreen()` → *boolean* {#fullscreen-api-method-isfullscreen}

Returns whether fullscreen mode is currently active.

#### History:

* `v2.31.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Fullscreen.isFullscreen()  → Whether fullscreen mode is active
```

### `Fullscreen.request([options [, requestedEl]])` → *`Promise` object* {#fullscreen-api-method-request}

Request that the browser enter fullscreen mode.

See:
Backgrounds and limitations.

#### History:

* `v2.31.0`: Introduced.

#### Parameters:

* **`options`:** (optional, *object*) The fullscreen options object.
* **`requestedEl`:** (optional, *`HTMLElement` object*) The element to enter fullscreen mode with.  If omitted, defaults to the entire page.

#### Options object:

A fullscreen options object should have some of the following properties:

* **`navigationUI`:** (*string*) Determines the fullscreen navigation UI preference.  Valid values are (default: `"auto"`):
	* `"auto"`: Indicates no preference.
	* `"hide"`: Request that the browser's navigation UI be hidden.  The full dimensions of the screen will be used to display the element.
	* `"show"`: Request that the browser's navigation UI be shown.  The screen dimensions allocated to the element will be clamped to leave room for the UI.

Note:
Browsers are not currently required to honor the navigationUI setting.

#### Examples:

##### Basic usage (recommended)

```
/* Request to enter fullscreen mode. */
Fullscreen.request();
```

##### With options and a specified element

```
/* Request to enter fullscreen mode while showing its navigation UI and with the given element. */
Fullscreen.request({ navigationUI : "show" }, myElement);
```

### `Fullscreen.exit()` → *`Promise` object* {#fullscreen-api-method-exit}

Request that the browser exit fullscreen mode.

#### History:

* `v2.31.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
/* Request to exit fullscreen mode. */
Fullscreen.exit();
```

### `Fullscreen.toggle([options [, requestedEl]])` → *`Promise` object* {#fullscreen-api-method-toggle}

Request that the browser toggle fullscreen mode—i.e., enter or exit as appropriate.

#### History:

* `v2.31.0`: Introduced.

#### Parameters:

* **`options`:** (optional, *object*) The fullscreen options object.  See [`Fullscreen.request()`](#fullscreen-api-method-request) for more information.
* **`requestedEl`:** (optional, *`HTMLElement` object*) The element to toggle fullscreen mode with.  If omitted, defaults to the entire page.

#### Examples:

##### Basic usage (recommended)

```
/* Request to toggle fullscreen mode. */
Fullscreen.toggle();
```

##### With options and a specified element

```
/* Request to toggle fullscreen mode while showing its navigation UI and with the given element. */
Fullscreen.toggle({ navigationUI : "show" }, myElement);
```

### `Fullscreen.onChange(handlerFn [, requestedEl])` {#fullscreen-api-method-onchange}

Attaches fullscreen change event handlers.

#### History:

* `v2.31.0`: Introduced.

#### Parameters:

* **`handlerFn`:** (*function*) The function to invoke when fullscreen mode is changed.
* **`requestedEl`:** (optional, *`HTMLElement` object*) The element to attach the handler to.

#### Examples:

##### Basic usage (recommended)

```
/* Attach a hander to listen for fullscreen change events. */
Fullscreen.onChange(function (ev) {
	/* Fullscreen mode changed, so do something. */
});
```

##### With a specified element

```
/* Attach a hander to the given element to listen for fullscreen change events. */
Fullscreen.onChange(function (ev) {
	/* Fullscreen mode changed on myElement, so do something. */
}, myElement);
```

### `Fullscreen.offChange([handlerFn [, requestedEl]])` {#fullscreen-api-method-offchange}

Removes fullscreen change event handlers.

#### History:

* `v2.31.0`: Introduced.

#### Parameters:

* **`handlerFn`:** (optional, *function*) The function to remove.  If omitted, will remove all handler functions.
* **`requestedEl`:** (optional, *`HTMLElement` object*) The element to remove the handler(s) from.

#### Examples:

##### Basic usage (recommended)

```
/* Remove all fullscreen change event handers. */
Fullscreen.offChange();
```

```
/* Remove the given fullscreen change event hander. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offChange(originalHandlerFn);
```

##### With a specified element

```
/* Remove all fullscreen change event handers from myElement. */
Fullscreen.offChange(null, myElement);
```

```
/* Remove the given fullscreen change event hander from myElement. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offChange(originalHandlerFn, myElement);
```

### `Fullscreen.onError(handlerFn [, requestedEl])` {#fullscreen-api-method-onerror}

Attaches fullscreen error event handlers.

#### History:

* `v2.31.0`: Introduced.

#### Parameters:

* **`handlerFn`:** (*function*) The function to invoke when fullscreen mode encounters an error.
* **`requestedEl`:** (optional, *`HTMLElement` object*) The element to attach the handler to.

#### Examples:

##### Basic usage (recommended)

```
/* Attach a hander to listen for fullscreen error events. */
Fullscreen.onError(function (ev) {
	/* Fullscreen mode changed, so do something. */
});
```

##### With a specified element

```
/* Attach a hander to the given element to listen for fullscreen error events. */
Fullscreen.onError(function (ev) {
	/* Fullscreen mode changed on myElement, so do something. */
}, myElement);
```

### `Fullscreen.offError([handlerFn [, requestedEl]])` {#fullscreen-api-method-offerror}

Removes fullscreen error event handlers.

#### History:

* `v2.31.0`: Introduced.

#### Parameters:

* **`handlerFn`:** (optional, *function*) The function to remove.  If omitted, will remove all handler functions.
* **`requestedEl`:** (optional, *`HTMLElement` object*) The element to remove the handler(s) from.

#### Examples:

##### Basic usage (recommended)

```
/* Remove all fullscreen error event handers. */
Fullscreen.offError();
```

```
/* Remove the given fullscreen error event hander. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offError(originalHandlerFn);
```

##### With a specified element

```
/* Remove all fullscreen error event handers from myElement. */
Fullscreen.offError(null, myElement);
```

```
/* Remove the given fullscreen error event hander from myElement. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offError(originalHandlerFn, myElement);
```


code:
# `LoadScreen` API {#loadscreen-api}

Note:
To simply add a delay to the dismissal of the loading screen to hide initial flashes of unstyled content (FOUC)—e.g., style changes and page reflows—you do not need to use this API.  See the Config.loadDelay configuration setting.

### `LoadScreen.lock()` → *number* {#loadscreen-api-method-lock}

Acquires a loading screen lock and returns its ID.  Displays the loading screen, if necessary.

#### History:

* `v2.15.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
LoadScreen.lock()  → Locks the loading screen and returns the lock ID
```

### `LoadScreen.unlock(lockId)` {#loadscreen-api-method-unlock}

Releases the loading screen lock with the given ID.  Hides the loading screen, if no other locks exist.

#### History:

* `v2.15.0`: Introduced.

#### Parameters:

* **`lockId`:** (*integer*) The loading screen lock ID.

#### Examples:

```
var lockId = LoadScreen.lock();

// Do something whose timing is unpredictable that should be hidden by the loading screen

LoadScreen.unlock(lockId);
```


code:
# `Macro` API {#macro-api}

See Also:
MacroContext API.

### `Macro.add(name , definition)` {#macro-api-method-add}

Add new macro(s).

#### History:

* `v2.0.0`: Introduced
* `v2.33.0`: Obsoleted the `deep` parameter.

#### Parameters:

* **`name`:** (*string* | *Array<string>*) Name, or array of names, of the macro(s) to add.  **NOTE:** Names must consist of characters from the basic Latin alphabet and start with a letter, which may be optionally followed by any number of letters, numbers, the underscore, or the hyphen.
* **`definition`:** (*object* | *string*) Definition of the macro(s) or the name of an existing macro whose definition to copy.

#### Definition object:

A macro definition object should have some of the following properties (only `handler` is absolutely required):

* **`skipArgs`:** (optional, *boolean* | *Array<string>*) Disables parsing argument strings into discrete arguments.  Used by macros that only use the raw/full argument strings.  Boolean `true` to affect all tags or an array of tag names to affect.
* **`tags`:** (optional, *null* | *Array<string>*) Signifies that the macro is a container macro—i.e., not self-closing.  An array child tag names or `null`, if there are no child tags.
* **`handler`:** (*function*) The macro's main function.  It will be called without arguments, but with its `this` set to a [macro context object](#macrocontext-api).

Additional properties may be added for internal use.

#### Examples:

```
/*
	Example of a very simple/naive <<if>>/<<elseif>>/<<else>> macro implementation.
*/
Macro.add('if', {
	skipArgs : true,
	tags     : ['elseif', 'else'],
	handler  : function () {
		try {
			for (var i = 0, len = this.payload.length; i < len; ++i) {
				if (
					this.payload[i].name === 'else' ||
					!!Scripting.evalJavaScript(this.payload[i].args.full)
				) {
					jQuery(this.output).wiki(this.payload[i].contents);
					break;
				}
			}
		}
		catch (ex) {
			return this.error('bad conditional expression: ' + ex.message);
		}
	}
});
```

### `Macro.delete(name)` {#macro-api-method-delete}

Remove existing macro(s).

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`name`:** (*string* | *Array<string>*) Name, or array of names, of the macro(s) to remove.

#### Examples:

```
Macro.delete("amacro")
Macro.delete(["amacro", "bmacro"])
```

### `Macro.get(name)` → *object* {#macro-api-method-get}

Return the named macro definition, or `null` on failure.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`name`:** (*string*) Name of the macro whose definition should be returned.

#### Examples:

```
Macro.get("print")
```

### `Macro.has(name)` → *boolean* {#macro-api-method-has}

Returns whether the named macro exists.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`name`:** (*string*) Name of the macro to search for.

#### Examples:

```
Macro.has("print")
```

### `Macro.tags.get(name)` → *Array<string>* {#macro-api-method-tags-get}

Return the named macro tag's parents array (includes the names of all macros who have registered the tag as a child), or `null` on failure.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`name`:** (*string*) Name of the macro tag whose parents array should be returned.

#### Examples:

```
Macro.tags.get("else")  → For the standard library, returns: ["if"]
```

### `Macro.tags.has(name)` → *boolean* {#macro-api-method-tags-has}

Returns whether the named macro tag exists.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`name`:** (*string*) Name of the macro tag to search for.

#### Examples:

```
Macro.tags.has("else")
```


code:
# `MacroContext` API {#macrocontext-api}

See Also:
Macro API.

Macro handlers are called with no arguments, but with their `this` set to a macro (execution) context object.  Macro context objects contain the following data and method properties.

### `<MacroContext>.args` → *Array<any>* {#macrocontext-api-prototype-property-args}

An array of discrete arguments parsed from the argument string.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Given: <<someMacro "a" "b" "c">>
this.args.length  → Returns 3
this.args[0]      → Returns 'a'
this.args[1]      → Returns 'b'
this.args[2]      → Returns 'c'
```

### `<MacroContext>.args.full` → *string* {#macrocontext-api-prototype-property-args-full}

The argument string after converting all TwineScript syntax elements into their native JavaScript counterparts.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Given: <<if "a" is "b">>
this.args.full  → Returns '"a" === "b"'
```

### `<MacroContext>.args.raw` → *string* {#macrocontext-api-prototype-property-args-raw}

The unprocessed argument string.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Given: <<if "a" is "b">>
this.args.raw  → Returns '"a" is "b"'
```

### `<MacroContext>.name` → *string* {#macrocontext-api-prototype-property-name}

The name of the macro.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
// Given: <<someMacro …>>
this.name  → Returns 'someMacro'
```

### `<MacroContext>.output` → *`HTMLElement` object* {#macrocontext-api-prototype-property-output}

The current output element.

#### History:

* `v2.0.0`: Introduced.

### `<MacroContext>.parent` → *null* | *object* {#macrocontext-api-prototype-property-parent}

The (execution) context object of the macro's parent, or `null` if the macro has no parent.

#### History:

* `v2.0.0`: Introduced.

### `<MacroContext>.parser` → *object* {#macrocontext-api-prototype-property-parser}

The parser instance that generated the macro call.

#### History:

* `v2.0.0`: Introduced.

### `<MacroContext>.payload` → *null* | *Array<object>* {#macrocontext-api-prototype-property-payload}

The text of a container macro parsed into discrete payload objects by tag.  Payload objects have the following properties:

* **`name`:** (*string*) Name of the current tag.
* **`args`:** (*Array<any>*) The current tag's argument string parsed into an array of discrete arguments.  Equivalent in function to [`<MacroContext>.args`](#macrocontext-api-prototype-property-args).
	* **`args.full`:** (*string*) The current tag's argument string after converting all TwineScript syntax elements into their native JavaScript counterparts.  Equivalent in function to [`<MacroContext>.args.full`](#macrocontext-api-prototype-property-args-full).
	* **`args.raw`:** (*string*) The current tag's unprocessed argument string.  Equivalent in function to [`<MacroContext>.args.raw`](#macrocontext-api-prototype-property-args-raw).
* **`contents`:** (*string*) The current tag's contents—i.e., the text between the current tag and the next.

#### History:

* `v2.0.0`: Introduced.

### `<MacroContext>.self` → *object* {#macrocontext-api-prototype-property-self}

The macro's definition—created via [`Macro.add()`](#macro-api-method-add).

#### History:

* `v2.0.0`: Introduced.

### `<MacroContext>.contextHas(filter)` → *boolean* {#macrocontext-api-prototype-method-contexthas}

Returns whether any of the macro's ancestors passed the test implemented by the given filter function.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`filter`:** (*function*) The function used to test each ancestor execution context object, which is passed in as its sole parameter.

#### Examples:

```
var includeAncestor = function (ctx) { return ctx.name === "include"; };
this.contextHas(includeAncestor);  → Returns true if any ancestor was an <<include>> macro
```

### `<MacroContext>.contextSelect(filter)` → *null* | *object* {#macrocontext-api-prototype-method-contextselect}

Returns the first of the macro's ancestors that passed the test implemented by the given filter function or `null`, if no members pass.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`filter`:** (*function*) The function used to test each ancestor execution context object, which is passed in as its sole parameter.

#### Examples:

```
var includeAncestor = function (ctx) { return ctx.name === "include"; };
this.contextSelect(includeAncestor);  → Returns the first <<include>> macro ancestor
```

### `<MacroContext>.contextSelectAll(filter)` → *Array<object>* {#macrocontext-api-prototype-method-contextselectall}

Returns a new array containing all of the macro's ancestors that passed the test implemented by the given filter function or an empty array, if no members pass.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`filter`:** (*function*) The function used to test each ancestor execution context object, which is passed in as its sole parameter.

#### Examples:

```
var includeAncestor = function (ctx) { return ctx.name === "include"; };
this.contextSelectAll(includeAncestor);  → Returns an array of all <<include>> macro ancestors
```

### `<MacroContext>.createShadowWrapper(callback [, doneCallback [, startCallback]])` → *function* {#macrocontext-api-prototype-method-createshadowwrapper}

Returns a callback function that wraps the specified callback functions to provide access to the variable shadowing system used by the [`<<capture>>` macro](#macros-macro-capture).

Note:
All of the specified callbacks are invoked as the wrapper is invoked—meaning, with their this set to the this of the wrapper and with whatever parameters were passed to the wrapper.

Warning:
Only useful when you have an asynchronous callback that invokes code/content that needs to access story and/or temporary variables shadowed by <<capture>>.  If you don't know what that means, then this API is likely not for you.

#### History:

* `v2.14.0`: Introduced.
* `v2.23.3`: Fixed an issue where shadows would fail for multiple layers of nested asynchronous code due to loss of context.

#### Parameters:

* **`callback`:** (*function*) The main callback function, executed when the wrapper is invoked.  Receives access to variable shadows.
* **`doneCallback`:** (optional, *function*) The finalization callback function, executed after the main `callback` returns.  Does not receive access to variable shadows.
* **`startCallback`:** (optional, *function*) The initialization callback function, executed before the main `callback` is invoked.  Does not receive access to variable shadows.

#### Examples:

##### Basic usage

```
$someElement.on('some_event', this.createShadowWrapper(function (ev) {
	/* main asynchronous code */
}));
```

##### With an optional `doneCallback`

```
$someElement.on('some_event', this.createShadowWrapper(
	function (ev) {
		/* main asynchronous code */
	},
	function (ev) {
		/* asynchronous code invoked after the main code */
	}
));
```

##### With an optional `doneCallback` and `startCallback`

```
$someElement.on('some_event', this.createShadowWrapper(
	function (ev) {
		/* main asynchronous code */
	},
	function (ev) {
		/* asynchronous code invoked after the main code */
	},
	function (ev) {
		/* asynchronous code invoked before the main code */
	}
));
```

### `<MacroContext>.error(message)` → *boolean:false* {#macrocontext-api-prototype-method-error}

Renders the message prefixed with the name of the macro and returns `false`.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`message`:** (*string*) The error message to output.

#### Examples:

```
// Given: <<someMacro …>>
return this.error("oops");  → Outputs '<<someMacro>>: oops'
```


code:
<!-- ***********************************************************************************************
	Passage API
************************************************************************************************ -->
# `Passage` API {#passage-api}

Instances of the `Passage` object are returned by the [`Story.get()`](#story-api-method-get) static method.

All properties of `Passage` objects should be treated as if they were ***read-only***, as modifying them could result in unexpected behavior.

<!-- *********************************************************************** -->

### `<Passage>.domId` → *string* {#passage-api-prototype-getter-domid}

The DOM ID of the passage, created from the slugified passage title.

#### History:

* `v2.0.0`: Introduced.

<!-- *********************************************************************** -->

### `<Passage>.tags` → *Array<string>* {#passage-api-prototype-getter-tags}

The tags of the passage.

#### History:

* `v2.0.0`: Introduced.

<!-- *********************************************************************** -->

### `<Passage>.text` → *string* {#passage-api-prototype-getter-text}

The raw text of the passage.

#### History:

* `v2.0.0`: Introduced.

<!-- *********************************************************************** -->

### `<Passage>.title` → *string* {#passage-api-prototype-getter-title}

The title of the passage.

#### History:

* `v2.0.0`: Introduced.

<!-- *********************************************************************** -->

### `<Passage>.description()` → *string* {#passage-api-prototype-method-description}

Returns the description of the passage, created from either an excerpt of the passage or the [`Config.passages.descriptions` setting](#config-api-property-passages-descriptions).

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var passage = Story.get("The Ducky");

passage.description()  → Returns the description of "The Ducky" passage
```

<!-- *********************************************************************** -->

### `<Passage>.processText()` → *string* {#passage-api-prototype-method-processtext}

Returns the processed text of the passage, created from applying `nobr` tag and image passage processing to its raw text.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var passage = Story.get("The Ducky");

passage.processText()  → Returns the fully processed text of "The Ducky" passage
```