I don’t buy the pitch—I think you’re overselling things a bit—but it doesn’t make me any difference what you use, so get your template on I guess.
I do, however, have some suggestions on how to monkey patch Underscore.js templates into the render pipeline. Both methods require some configuration—they’re also untested.
[size=125]Adding Underscore.js, via an external file (local or online), and patching in its template system: (goes in Story JavaScript or the equivalent)[/size]
[spoiler][code]
/*
This code is asynchronous, thus requires some special finagling.
Underscore.js templates may be used anywhere except the StoryInit
special passage.
*/
(function () {
// Set the URL to Underscore.js here. Local or remote files are acceptable.
var libUrl = ‘PASTE UNDERSCORE.JS URL HERE’;
// Grab a lock on the loading screen.
var lockId = LoadScreen.lock();
// Set up a utility function to release our lock on the loading
// screen and reshow the the starting passage with Underscore.js
// templates enabled.
var unlockAndShow = function () {
LoadScreen.unlock(lockId);
Engine.show();
};
// Import Underscore.js and patch
importScripts(libUrl).then(function () {
// Monkey patch in our customized `<Passage>.processText()`.
Passage.prototype.processText = function () {
let processed = this.text;
// Handle image passage transclusion.
if (this.tags.includes('Twine.image')) {
processed = '[img[' + processed + ']]';
}
// Normal passage handling.
else {
// Process <% %> blocks, remove all leading & trailing
// newlines, and compact all internal sequences of
// newlines into two newlines.
processed = _.template(processed)({
Dialog : Dialog,
Engine : Engine,
LoadScreen : LoadScreen,
Macro : Macro,
Save : Save,
Setting : Setting,
State : State,
Story : Story,
UI : UI,
UIBar : UIBar
})
.replace(/^\n+|\n+$/g, '')
.replace(/\n\n+/g, '\n\n');
// Handle `Config.passages.nobr` and the `nobr` tag.
if (Config.passages.nobr || this.tags.includes('nobr')) {
// Compact all internal sequences of newlines into single spaces.
processed = processed.replace(/\n+/g, ' ');
}
}
return processed;
};
// The starting passage has already been played.
if (Engine.lastPlay !== null) {
unlockAndShow();
}
// The starting passage is currently rendering.
else if (Engine.isRendering()) {
postdisplay['#monkey-patch-underscore.js'] = function (taskName) {
delete postdisplay[taskName]; // single-use task
unlockAndShow();
};
}
// The starting passage's play state is unknown.
else {
setTimeout(unlockAndShow, 250);
}
}, function () {
// Release our lock on the loading screen.
LoadScreen.unlock(lockId);
// The library failed to load, so we complain, though there's
// likely already a error waiting for the player.
throw new Error('Import of Underscore.js failed (url: "' + libUrl + '").');
});
})();
[/code][/spoiler]
[size=125]Adding Underscore.js, directly via a wrapper, and patching in its template system: (goes in Story JavaScript or the equivalent)[/size]
[spoiler][code]
/*
This code is synchronous, thus doesn’t require any special finagling.
Underscore.js templates may be used anywhere.
/
(function (define, exports) {
/ PASTE UNDERSCORE.JS LIBRARY HERE */
}).call(window);
// Monkey patch in our customized <Passage>.processText()
.
Passage.prototype.processText = function () {
let processed = this.text;
// Handle image passage transclusion.
if (this.tags.includes('Twine.image')) {
processed = '[img[' + processed + ']]';
}
// Normal passage handling.
else {
// Process <% %> blocks, remove all leading & trailing
// newlines, and compact all internal sequences of
// newlines into two newlines.
processed = _.template(processed)({
Dialog : Dialog,
Engine : Engine,
LoadScreen : LoadScreen,
Macro : Macro,
Save : Save,
Setting : Setting,
State : State,
Story : Story,
UI : UI,
UIBar : UIBar
})
.replace(/^\n+|\n+$/g, '')
.replace(/\n\n+/g, '\n\n');
// Handle `Config.passages.nobr` and the `nobr` tag.
if (Config.passages.nobr || this.tags.includes('nobr')) {
// Compact all internal sequences of newlines into single spaces.
processed = processed.replace(/\n+/g, ' ');
}
}
return processed;
};
[/code][/spoiler]