Skip to content

Commit bac3c41

Browse files
authored
RELEASE: v2.36.0
## Changelog: * Fixed an issue with the `selected` keyword in the `<<cycle>>` and `<<listbox>>` macros' `<<option>>` tags. * Fixed instances where using the `[img[]]` markup as an argument to macros would drop the `link-image` class. * Fixed `Config.history.maxStates` to disallow unlimited states (value: `0`). * Added the `init` special tag that, similar to `StoryInit`, allows pre-story-start initialization tasks. Intended for add-on/library use. * Added a `data-init-passage` content attribute to `StoryInterface` that allows content to be updated only once at initialization. * Added the `State.metadata.entries()` and `State.metadata.keys()` static methods. * Added a `once` keyword to the `<<cycle>>` macro that ends the cycle upon reaching the final option. * Added the `<Array>.countWith()` method. * Added the `Save` Events API. * Added support for template literals within TwineScript. * Added various accessibility improvements. * Updated the `<<done>>` macro to better serve when used to wait for DOM updates. * `<<widget>>` macro updates: * Added a `container` keyword that allows non-void/container widgets and an associated `_contents` special variable. * Added a new special arguments variable, `_args`, and deprecated the old variable, `$args`. * Updated the default value of `Config.history.maxStates` from `100` to `40`. * Updated passage objects to maintain the order of passage tags as specified in the data chunk. * Deprecated the `Config.saves.onLoad` and `Config.saves.onSave` settings in favor of the `Save` Events API. * Updated bundled library: `jQuery` to v3.6.0. * Updates to locale files: * Updated the localization template. Translators are asked to updated the locale files as necessary. * Added `nl.js` – Dutch. * Various documentation updates. * Various internal improvements. * Build system updates.
2 parents dab0534 + aa8f36b commit bac3c41

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+8720
-5662
lines changed

build.js

Lines changed: 30 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
#!/usr/bin/env node
22
/***********************************************************************************************************************
33
4-
build.js (v1.4.18, 2020-11-08)
4+
build.js (v1.6.0, 2021-12-19)
55
A Node.js-hosted build script for SugarCube.
66
7-
Copyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
7+
Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
88
Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.
99
1010
***********************************************************************************************************************/
1111
/* eslint-env node, es6 */
12-
/* eslint-disable strict */
1312
'use strict';
1413

1514
/*******************************************************************************
@@ -142,11 +141,18 @@ const CONFIG = {
142141
the replacement strings (e.g. '$&' within the application source).
143142
*/
144143

145-
const _fs = require('fs');
144+
const {
145+
log,
146+
die,
147+
fileExists,
148+
makePath,
149+
copyFile,
150+
readFileContents,
151+
writeFileContents,
152+
concatFiles
153+
} = require('./scripts/build-utils');
146154
const _path = require('path');
147-
148-
const _indent = ' -> ';
149-
const _opt = require('node-getopt').create([
155+
const _opt = require('node-getopt').create([
150156
['b', 'build=VERSION', 'Build only for Twine major version: 1 or 2; default: build for all.'],
151157
['d', 'debug', 'Keep debugging code; gated by DEBUG symbol.'],
152158
['u', 'unminified', 'Suppress minification stages.'],
@@ -186,7 +192,7 @@ if (_opt.options.build) {
186192
console.log('Starting builds...');
187193

188194
// Create the build ID file, if nonexistent.
189-
if (!_fs.existsSync('.build')) {
195+
if (!fileExists('.build')) {
190196
writeFileContents('.build', '0');
191197
}
192198

@@ -203,7 +209,7 @@ if (_opt.options.build) {
203209
patch : semver.patch(version),
204210
prerelease : prerelease && prerelease.length > 0 ? prerelease.join('.') : null,
205211
build : Number(readFileContents('.build')) + 1,
206-
date : (new Date()).toISOString(),
212+
date : new Date().toISOString(),
207213

208214
toString() {
209215
const prerelease = this.prerelease ? `-${this.prerelease}` : '';
@@ -277,93 +283,6 @@ console.log('\nBuilds complete! (check the "build" directory)');
277283
/*******************************************************************************
278284
Utility Functions
279285
*******************************************************************************/
280-
function log(message, indent) {
281-
console.log('%s%s', indent ? indent : _indent, message);
282-
}
283-
284-
// function warn(message) {
285-
// console.warn('%swarning: %s', _indent, message);
286-
// }
287-
288-
function die(message, error) {
289-
if (error) {
290-
console.error('error: %s\n[@: %d/%d] Trace:\n', message, error.line, error.col, error.stack);
291-
}
292-
else {
293-
console.error('error: %s', message);
294-
}
295-
296-
process.exit(1);
297-
}
298-
299-
function makePath(pathname) {
300-
const pathBits = _path.normalize(pathname).split(_path.sep);
301-
302-
for (let i = 0; i < pathBits.length; ++i) {
303-
const dirPath = i === 0 ? pathBits[i] : pathBits.slice(0, i + 1).join(_path.sep);
304-
305-
if (!_fs.existsSync(dirPath)) {
306-
_fs.mkdirSync(dirPath);
307-
}
308-
}
309-
}
310-
311-
function copyFile(srcFilename, destFilename) {
312-
const srcPath = _path.normalize(srcFilename);
313-
const destPath = _path.normalize(destFilename);
314-
let buf;
315-
316-
try {
317-
buf = _fs.readFileSync(srcPath);
318-
}
319-
catch (ex) {
320-
die(`cannot open file "${srcPath}" for reading (reason: ${ex.message})`);
321-
}
322-
323-
try {
324-
_fs.writeFileSync(destPath, buf);
325-
}
326-
catch (ex) {
327-
die(`cannot open file "${destPath}" for writing (reason: ${ex.message})`);
328-
}
329-
330-
return true;
331-
}
332-
333-
function readFileContents(filename) {
334-
const filepath = _path.normalize(filename);
335-
336-
try {
337-
// the replace() is necessary because Node.js only offers binary mode file
338-
// access, regardless of platform, so we convert DOS-style line terminators
339-
// to UNIX-style, just in case someone adds/edits a file and gets DOS-style
340-
// line termination all over it
341-
return _fs.readFileSync(filepath, { encoding : 'utf8' }).replace(/\r\n/g, '\n');
342-
}
343-
catch (ex) {
344-
die(`cannot open file "${filepath}" for reading (reason: ${ex.message})`);
345-
}
346-
}
347-
348-
function writeFileContents(filename, data) {
349-
const filepath = _path.normalize(filename);
350-
351-
try {
352-
_fs.writeFileSync(filepath, data, { encoding : 'utf8' });
353-
}
354-
catch (ex) {
355-
die(`cannot open file "${filepath}" for writing (reason: ${ex.message})`);
356-
}
357-
}
358-
359-
function concatFiles(filenames, callback) {
360-
const output = filenames.map(filename => {
361-
const contents = readFileContents(filename);
362-
return typeof callback === 'function' ? callback(contents, filename) : contents;
363-
});
364-
return output.join('\n');
365-
}
366-
367286
function assembleLibraries(filenames) {
368287
log('assembling libraries...');
369288

@@ -397,37 +316,24 @@ function compileJavaScript(filenameObj, options) {
397316
].join(';\n') + ';\n' + jsSource;
398317
}
399318

400-
try {
401-
const uglifyjs = require('uglify-js');
402-
const uglified = uglifyjs.minify(jsSource, {
403-
fromString : true,
404-
compress : {
405-
global_defs : {
406-
TWINE1 : !!options.twine1,
407-
DEBUG : _opt.options.debug || false
408-
},
409-
screw_ie8 : true
410-
},
411-
mangle : {
412-
screw_ie8 : true
319+
const uglifyjs = require('uglify-js');
320+
const minified = uglifyjs.minify(jsSource, {
321+
compress : {
322+
global_defs : {
323+
TWINE1 : !!options.twine1,
324+
DEBUG : _opt.options.debug || false
413325
},
414-
output : {
415-
screw_ie8 : true
416-
}
417-
});
418-
return uglified.code;
419-
}
420-
catch (ex) {
421-
let mesg = 'uglification error';
422-
423-
if (ex.line > 0) {
424-
const begin = ex.line > 4 ? ex.line - 4 : 0;
425-
const end = ex.line + 3 < jsSource.length ? ex.line + 3 : jsSource.length;
426-
mesg += ':\n >> ' + jsSource.split(/\n/).slice(begin, end).join('\n >> ');
427-
}
326+
keep_infinity : true
327+
},
328+
mangle : false
329+
});
428330

429-
die(mesg, ex);
331+
if (minified.error) {
332+
const { message, line, col, pos } = minified.error;
333+
die(`JavaScript minification error: ${message}\n[@: ${line}/${col}/${pos}]`);
430334
}
335+
336+
return minified.code;
431337
/* eslint-enable camelcase, prefer-template */
432338
}
433339

dist/format.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/api/api-config.md

Lines changed: 49 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,27 @@ Config.history.controls = false;
7575

7676
<!-- *********************************************************************** -->
7777

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

80-
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). A setting of `0` means that there is no limit to how large the history may grow, though doing so is not recommended.
80+
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).
81+
82+
<p role="note" class="tip"><b>Tip:</b>
83+
For game-oriented projects, as opposed to more story-oriented interactive fiction, a setting of <code>1</code> is <strong><em>strongly recommended</em></strong>.
84+
</p>
8185

8286
#### History:
8387

8488
* `v2.0.0`: Introduced.
89+
* `v2.36.0`: Reduced the default to `40`.
8590

8691
#### Examples:
8792

8893
```
89-
// No history limit (you should never do this!)
90-
Config.history.maxStates = 0;
91-
92-
// Limit the history to a single state
94+
// Limit the history to a single state (recommended for games)
9395
Config.history.maxStates = 1;
9496
95-
// Limit the history to 150 states
96-
Config.history.maxStates = 150;
97+
// Limit the history to 80 states
98+
Config.history.maxStates = 80;
9799
```
98100

99101

@@ -426,9 +428,16 @@ Config.saves.autoload = function () {
426428

427429
<!-- *********************************************************************** -->
428430

429-
### `Config.saves.autosave`*boolean* | *string array* | *function* (default: *none*) {#config-api-property-saves-autosave}
431+
### `Config.saves.autosave`*boolean* | *Array&lt;string&gt;* | *function* (default: *none*) {#config-api-property-saves-autosave}
432+
433+
Determines whether the autosave is created/updated when passages are displayed.
434+
435+
Valid values are:
430436

431-
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, an array of strings, which causes the autosave to be updated for each passage with at least one matching tag, or a function, which causes the autosave to be updated for each passage where its return value is truthy.
437+
* Boolean `true`, which causes the autosave to be updated with every passage.
438+
* Boolean `false`, which causes the autosave to never update automatically—i.e., you must do it manually via the <a href="#save-api-method-autosave-save"><code>Save.autosave.save()</code> static method</a>.
439+
* An array of strings, which causes the autosave to be updated for each passage with at least one matching tag.
440+
* A function, which causes the autosave to be updated for each passage where its return value is truthy.
432441

433442
<p role="note" class="warning"><b>Warning:</b>
434443
When setting the value to boolean <code>true</code>, you will likely also need to use the <a href="#config-api-property-saves-isallowed"><code>Config.saves.isAllowed</code> property</a> 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.
@@ -445,6 +454,9 @@ When setting the value to boolean <code>true</code>, you will likely also need t
445454
// Autosaves every passage
446455
Config.saves.autosave = true;
447456
457+
// Allows manual autosaving
458+
Config.saves.autosave = false;
459+
448460
// Autosaves on passages tagged with any of "bookmark" or "autosave"
449461
Config.saves.autosave = ["bookmark", "autosave"];
450462
@@ -490,93 +502,6 @@ Config.saves.isAllowed = function () {
490502

491503
<!-- *********************************************************************** -->
492504

493-
### `Config.saves.onLoad`*function* (default: *none*) {#config-api-property-saves-onload}
494-
495-
Performs any required pre-processing before the save data is loaded—e.g., upgrading out-of-date save data. The callback is passed one parameter, the save object to be processed. If it encounters an unrecoverable problem during its processing, it may throw an exception containing an error message; the message will be displayed to the player and loading of the save will be terminated.
496-
497-
#### History:
498-
499-
* `v2.0.0`: Introduced.
500-
501-
#### Callback parameters:
502-
503-
* **`save`:** (*object*) The save object to be pre-processed.
504-
505-
#### Save object:
506-
507-
<p role="note"><b>Note:</b>
508-
See the <a href="#save-api-save-objects">save objects</a> section of the <a href="#save-api">Save API</a> for information on the format of a save.
509-
</p>
510-
511-
#### Examples:
512-
513-
```
514-
Config.saves.onLoad = function (save) {
515-
/* code to pre-process the save object */
516-
};
517-
```
518-
519-
<!-- *********************************************************************** -->
520-
521-
### `Config.saves.onSave`*function* (default: *none*) {#config-api-property-saves-onsave}
522-
523-
Performs any required post-processing before the save data is saved. The callback is passed two parameters, the save object to be processed and save operation details object.
524-
525-
#### History:
526-
527-
* `v2.0.0`: Introduced.
528-
* `v2.33.0`: Added save operation details object parameter to the callback function.
529-
530-
#### Callback parameters:
531-
532-
* **`save`:** (*object*) The save object to be post-processed.
533-
* **`details`:** (*object*) The save operation details object.
534-
535-
#### Save object:
536-
537-
<p role="note"><b>Note:</b>
538-
See the <a href="#save-api-save-objects">save objects</a> section of the <a href="#save-api">Save API</a> for information on the format of a save.
539-
</p>
540-
541-
#### Save operation details object:
542-
543-
A save operation details object will have the following properties:
544-
545-
* **`type`:** (*string*) A string representing how the save operation came about—i.e., what caused it. Possible values are: `'autosave'`, `'disk'`, `'serialize'`, `'slot'`.
546-
547-
#### Examples:
548-
549-
##### Using only the save object parameter
550-
551-
```
552-
Config.saves.onSave = function (save) {
553-
/* code to post-process the save object */
554-
};
555-
```
556-
557-
##### Using both the save object and operation details parameters
558-
559-
```
560-
Config.saves.onSave = function (save, details) {
561-
switch (details.type) {
562-
case 'autosave':
563-
/* code to post-process the save object from autosaves */
564-
break;
565-
case 'disk':
566-
/* code to post-process the save object from disk saves */
567-
break;
568-
case 'serialize':
569-
/* code to post-process the save object from serialize saves */
570-
break;
571-
default: /* slot */
572-
/* code to post-process the save object from slot saves */
573-
break;
574-
}
575-
};
576-
```
577-
578-
<!-- *********************************************************************** -->
579-
580505
### `Config.saves.slots` *integer* (default: `8`) {#config-api-property-saves-slots}
581506

582507
Sets the maximum number of available save slots.
@@ -636,6 +561,33 @@ Config.saves.version = 3;
636561
Config.saves.version = "v3";
637562
```
638563

564+
<!-- *********************************************************************** -->
565+
566+
### <span class="deprecated">`Config.saves.onLoad`*function* (default: *none*)</span> {#config-api-property-saves-onload}
567+
568+
<p role="note" class="warning"><b>Deprecated:</b>
569+
This setting has been deprecated and should no longer be used. See the <a href="#save-api-method-onload-add"><code>Save.onLoad.add()</code></a> method for its replacement.
570+
</p>
571+
572+
#### History:
573+
574+
* `v2.0.0`: Introduced.
575+
* `v2.36.0`: Deprecated in favor of the [`Save` Events API](#save-api-events).
576+
577+
<!-- *********************************************************************** -->
578+
579+
### <span class="deprecated">`Config.saves.onSave`*function* (default: *none*)</span> {#config-api-property-saves-onsave}
580+
581+
<p role="note" class="warning"><b>Deprecated:</b>
582+
This setting has been deprecated and should no longer be used. See the <a href="#save-api-method-onsave-add"><code>Save.onSave.add()</code></a> method for its replacement.
583+
</p>
584+
585+
#### History:
586+
587+
* `v2.0.0`: Introduced.
588+
* `v2.33.0`: Added save operation details object parameter to the callback function.
589+
* `v2.36.0`: Deprecated in favor of the [`Save` Events API](#save-api-events).
590+
639591

640592
<!-- ***************************************************************************
641593
UI

0 commit comments

Comments
 (0)