utilsSC.js 7.08 KiB
/**
* circumvents SugarCube, allowing a plain HTML5 UI within it
*
* @param {function(HTMLElement): HTMLElement} passageFunction
*/
window.html5passage = function html5passage(passageFunction) {
$(document).one(":passagedisplay", (ev) => {
const element = document.createElement("div");
element.classList.add("passage");
document.getElementById("passages").appendChild(element);
passageFunction(element);
$(document).off(":passagedisplay");
});
};
/**
* If you want to include a SugarCube passage in a JS function use this. The result must be printed using the <<print>> macro.
* @param {string} passageTitle
* @returns {string}
*/
window.jsInclude = function(passageTitle) {
if (Story.has(passageTitle)) {
return Story.get(passageTitle).processText();
} else {
return `<span class="red">Error: Passage ${passageTitle} does not exist.</span>`;
}
};
/**
* Creates a HTML element with custom SugarCube attributes which works as a passage link
*
* The result works in the same way as the wiki markup in the SugarCube
* @see https://www.motoslave.net/sugarcube/2/docs/#markup-html-attribute
* @param {string} linkText link text
* @param {string} passage the passage name to link to
* @param {string} [setter=''] setter text (optional)
* @param {string} [tooltip=''] tooltip text (optional)
* @param {string} [elementType='a'] element type (optional) default is 'a'.
* Could be any of 'a', 'audio', img', 'source', 'video'
* @returns {string} element text
*
* @example
* // equal to [[Go to town|Town]]
* App.UI.passageLink("Go to town", "Town")
*/
App.UI.passageLink = function(linkText, passage, setter, tooltip = '', elementType = 'a') {
let res = `<${elementType} data-passage="${passage}"`;
if (setter) {
res += ` data-setter="${App.Utils.escapeHtml(setter)}"`;
}
if (tooltip) {
res += ` title="${tooltip}"`;
}
res += `>${linkText}</${elementType}>`;
return res;
};
App.UI.link = function() {
let counter = 0;
// reset all handlers for each passage
$(document).on(':passageinit', function() {
State.temporary.linkHandlers = {};
counter = 0;
});
return makeLink;
/**
* Creates a markup for a SugarCube link which executes given function with given arguments
*
* @param {string} linkText link text
* @param {*} handler callable object
* @param {*} args arguments
* @param {string} [passage] the passage name to link to
* @returns {string} link in SC markup
*/
function makeLink(linkText, handler, args = [], passage = '', tooltip = '') {
// pack handler and data
State.temporary.linkHandlers[counter] = {
f: handler,
args: Array.isArray(args) ? args : [args]
};
// can't say _linkHandlers here because SC does not recognize its own notation in "..._varName"
let SCHandlerText =
`State.temporary.linkHandlers[${counter}].f(...State.temporary.linkHandlers[${counter}].args);`;
++counter;
if (passage) {
return App.UI.passageLink(linkText, passage, SCHandlerText, tooltip);
} else {
if (tooltip) {
throw "Tooltips are not supported by the <<link>> markup.";
}
// data-passage scheme does not work with empty passage name
return `<<link "${linkText}">><<run ${SCHandlerText}>><</link>>`;
}
}
}();
/**
* Replaces contents of the element, identified by the given selector, with wiki'ed new content
*
* The function is an analogue to the SugarCube <<replace>> macro (and is a simplified version of it)
* @param {string} selector
* @param {string} newContent
*/
App.UI.replace = function(selector, newContent) {
let ins = jQuery(document.createDocumentFragment());
ins.wiki(newContent);
const target = $(selector);
target.empty();
target.append(ins);
};
/**
* A simple macro which allows to create wrapping html elements with dynamic IDs.
*
* idea blatantly robbed from the spanMacroJS.tw but expanded to a more generic case, allowing <div>,
* <button> or whatever you want elements, default is for the div though.
* In addition, you can pass an object in as the first argument instead of an id, and each of the
* object's attributes will become attributes of the generate tag.
*
* @example
* htag('test', "red") // <div id="red">test</div>
* htag('test', {class: red}); // <div class="red">test</div>
* htag('test', {class: red, id: green}); // <div class="red" id="green">test</div>
* @param {string} text
* @param {string|object} attributes
* @param {string} [tag='div']
* @returns {string}
*/
App.UI.htag = function(text, attributes, tag = 'div') {
const payload = text.replace(/(^\n+|\n+$)/, "");
if (typeof attributes === "object") {
attributes = $.map(attributes, (val, key) => `${key}="${val}"`).join(" ");
} else {
attributes = `id="${attributes.trim()}"`;
}
return `<${tag} ${attributes}>${payload}</${tag}>`;
};
App.UI.tabbar = function() {
return {
openTab: openTab,
tabButton: tabButton,
makeTab: makeTab,
handlePreSelectedTab: handlePreSelectedTab,
tabChoiceVarName: tabChoiceVarName
};
function openTab(evt, tabName) {
/* var passage = passage().trim().replace(/ /g,"+");*/
const tabcontent = document.getElementsByClassName("tabcontent");
for (let i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
const tablinks = document.getElementsByClassName("tablinks");
for (let i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
V.tabChoice[tabChoiceVarName()] = tabName; /* The regex strips spaces and " ' " from passage names, making "Servants' Quarters" into "ServantsQuarters" and allowing it to be used as a label in this object. */
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
}
/**
* @param {string} name
* @param {string} text
* @returns {string}
*/
function tabButton(name, text) {
return `<button class="tablinks" onclick="App.UI.tabbar.openTab(event, '${name}')" id="tab ${name}">${text}</button>`;
}
/**
* @param {string} name
* @param {string} content
* @returns {string}
*/
function makeTab(name, content) {
return `<div id="${name}" class="tabcontent"><div class="content">${content}</div></div>`;
}
function handlePreSelectedTab(defaultTab = "assign", immidiate = false) {
let selectedTab = State.variables.tabChoice[tabChoiceVarName()];
if (!selectedTab) { selectedTab = defaultTab; }
function selectTab() {
let tabBtn = document.getElementById(`tab ${selectedTab}`);
if (!tabBtn) {
tabBtn = document.getElementsByClassName('tablinks').item(0);
}
if (tabBtn) { tabBtn.click(); }
}
if (immidiate) {
selectTab();
} else {
$(document).one(':passagedisplay', selectTab);
}
}
function tabChoiceVarName() {
return passage().trim().replace(/ |'/g, '');
}
}();
/**
* Creates a span for an link with tooltip containing the reasons why it is disabled
* @param {string} link
* @param {string[]} reasons
* @returns {string}
*/
App.UI.disabledLink = function(link, reasons) {
const tooltips = reasons.length === 1 ?
`<span class="tooltip">${reasons}</span>` :
`<div class="tooltip"><ul>${reasons.map(e => `<li>${e}</li>`).join('')}</ul></div>`;
return `<span class="textWithTooltip">${link}${tooltips}</span>`;
};