@dodona/papyros
Version:
Scratchpad for multiple programming languages in the browser.
139 lines • 4.83 kB
JavaScript
import { I18n } from "i18n-js";
import { TRANSLATIONS } from "../Translations";
import { LogType, papyrosLog } from "./Logging";
export const i18n = new I18n(TRANSLATIONS);
// Shorthand for ease of use
export const t = i18n.t.bind(i18n);
export function getLocales() {
return Object.keys(TRANSLATIONS);
}
/**
* Resolve an ElementIdentifier to the corresponding HTLMElement
* @param {ElementIdentifier} elementId The identifier for the element
* @return {T} The corresponding element
*/
export function getElement(elementId) {
if (typeof elementId === "string") {
return document.getElementById(elementId);
}
else {
return elementId;
}
}
/**
* Add a listener to an HTML element for an event on an attribute
* Element attributes tend to be strings, but string Enums can also be used
* by using the type-parameter T
* @param {ElementIdentifier} elementId Identifier for the element
* @param {function(T)} onEvent The listener for the event
* @param {string} eventType The type of the event
* @param {string} attribute The attribute affected by the event
*/
export function addListener(elementId, onEvent, eventType = "change", attribute = "value") {
const element = getElement(elementId);
element.addEventListener(eventType, () => {
onEvent(element[attribute] || element.getAttribute(attribute));
});
}
/**
* Unset the selected item of a select element to prevent a default selection
* @param {ElementIdentifier} selectId Identifier for the select element
*/
export function removeSelection(selectId) {
getElement(selectId).selectedIndex = -1;
}
/**
* Parse the data contained within a PapyrosEvent using its contentType
* Supported content types are: text/plain, text/json, img/png;base64
* @param {string} data The data to parse
* @param {string} contentType The content type of the data
* @return {any} The parsed data
*/
export function parseData(data, contentType) {
if (!contentType) {
return data;
}
const [baseType, specificType] = contentType.split("/");
switch (baseType) {
case "text": {
switch (specificType) {
case "plain": {
return data;
}
case "json": {
return JSON.parse(data);
}
case "integer": {
return parseInt(data);
}
case "float": {
return parseFloat(data);
}
}
break;
}
case "img": {
switch (specificType) {
case "png;base64": {
return data;
}
}
break;
}
case "application": {
// Content such as application/json does not need parsing as it is in the correct shape
return data;
}
}
papyrosLog(LogType.Important, `Unhandled content type: ${contentType}`);
return data;
}
export function downloadResults(data, filename) {
const blob = new Blob([data], { type: "text/plain" });
const elem = window.document.createElement("a");
// Cast URL to any as TypeScript doesn't recognize it properly
// error TS2339: Property 'revokeObjectURL' does not exist on type
const windowUrl = URL;
elem.href = windowUrl.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
windowUrl.revokeObjectURL(elem.href);
}
/**
* Obtain the url of the current page without hashes, identifiers, query params, ...
* @param {boolean} endingSlash Whether the url should end in a slash
* @return {string} The current url
*/
export function cleanCurrentUrl(endingSlash = false) {
let url = location.origin + location.pathname;
if (endingSlash && !url.endsWith("/")) {
url += "/";
}
else if (!endingSlash && url.endsWith("/")) {
url = url.slice(0, url.length - 1);
}
return url;
}
/**
* Focus an element, setting the user's caret at the end of the contents
* Needed to ensure focusing a contenteditable element works as expected
* @param {HTMLElement} el The element to focus
*/
export function placeCaretAtEnd(el) {
// Source: https://stackoverflow.com/questions/4233265/contenteditable-set-caret-at-the-end-of-the-text-cross-browser
el.focus();
if (typeof window.getSelection !== "undefined" &&
typeof document.createRange !== "undefined") {
const range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
const sel = window.getSelection();
if (sel) {
sel.removeAllRanges();
sel.addRange(range);
}
}
}
//# sourceMappingURL=Util.js.map