const loadedWidgets = {}; const getButtons = (inputGroup) => { return { play: inputGroup.querySelector(".bergamot-play"), reset: inputGroup.querySelector(".bergamot-reset"), close: inputGroup.querySelector(".bergamot-close"), } } const setRunning = (inputGroup, running) => { if (inputGroup !== null) { const buttons = getButtons(inputGroup); if (buttons.play) buttons.play.classList.toggle("bergamot-hidden", running); if (buttons.reset) buttons.reset.classList.toggle("bergamot-hidden", !running); if (buttons.close) buttons.close.classList.toggle("bergamot-hidden", !running); } } // The object language parsing is handling by a separate standalone Elm // application in the ObjectLanguage module, which has two ports: // // * `parseString` requests a string to be parsed // * `parsedString` returns the parsed string, or null // // We want there to be a single global ObjectLanguage object, but it works // on a "subscription" model (we have to give a callback to its port). // Configure this callback to invoke `resolve` functions from a list, // so that callers can just get a promise. This way we aren't continuously // registering more and more handlers for each parsed string, and we can // use a convenient promise API. const parsingPromiseResolvers = {}; const ensureObjectLanguage = () => { if (!window.Bergamot.ObjectLanguage) { window.Bergamot.ObjectLanguage = Elm.Bergamot.ObjectLanguage.init({}); window.Bergamot.ObjectLanguage.ports.parsedString.subscribe(({ string, term }) => { if (string in parsingPromiseResolvers) { for (const resolver of parsingPromiseResolvers[string]) { resolver(term); } parsingPromiseResolvers[string] = []; } }); } return window.Bergamot.ObjectLanguage; } const parseBergamotObjectLanguage = (str) => { if (!(str in parsingPromiseResolvers)) { parsingPromiseResolvers[str] = []; } return new Promise(resolve => { parsingPromiseResolvers[str].push(resolve); ensureObjectLanguage().ports.parseString.send(str); }); } window.Bergamot = {}; window.Bergamot.run = (inputGroup, nodeId, inputModes, inputPrompt, rules, renderPreset, input) => { var app = Elm.Main.init({ node: document.getElementById(nodeId), flags: { inputModes, renderRules: window.Bergamot.renderPresets[renderPreset], rules, input } }); app.ports.convertInput.subscribe(async ({ mode, input }) => { if (!(mode in window.Bergamot.inputModes)) { app.ports.receiveConverted.send({ input, result: { error: "Improperly configured desugaring function (this is the website developer's fault)" } }); } let query = await (window.Bergamot.inputModes[mode])(input); if (query !== null) { query = inputPrompt.replace("TERM", query); app.ports.receiveConverted.send({ input, result: { query } }); } else { app.ports.receiveConverted.send({ input, result: { error: "Unable to parse object language term" } }); } }); loadedWidgets[nodeId] = { app, parentNode: inputGroup ? inputGroup.parentElement : null }; setRunning(inputGroup, true); }; window.Bergamot.runPreset = (inputGroup, nodeId, presetName) => { const preset = window.Bergamot.presets[presetName]; window.Bergamot.run(inputGroup, nodeId, preset.inputModes, preset.inputPrompt, preset.rules, preset.renderPreset, preset.query || ""); }; window.Bergamot.close = (inputGroup, nodeId) => { if (!(nodeId in loadedWidgets)) return; const placeholderDiv = document.createElement('div'); placeholderDiv.id = nodeId; const widget = loadedWidgets[nodeId]; const elmRoot = widget.parentNode.querySelector(".bergamot-root"); elmRoot.replaceWith(placeholderDiv) delete loadedWidgets[nodeId]; setRunning(inputGroup, false); } window.Bergamot.inputModes = { "Bergamot Object Language": parseBergamotObjectLanguage }; window.Bergamot.presets = {}; window.Bergamot.renderPresets = {};