/** * Return an endpoint URL that has a protocol, domain and path */ function normalizedEndpoint(endpoint) { let matches = endpoint.match(/^(https?:\/\/)?([^\/]+)(\/.*)?$/i); if (!matches) throw Error("Invalid endpoint URL"); let protocol = matches[1] || "https://"; let domain = matches[2]; let path = matches[3] || "/api/v4"; return `${protocol}${domain}${path}`; } /** * Return the endpoint as it should be shown to the user */ function humanReadableEndpoint(endpoint) { let matches = endpoint.match(/^(https?:\/\/.+)\/api\/v4$/i); if (!matches) throw Error("Invalid endpoint URL"); return matches[1]; } /** * Shorthand for document.getElementById */ function byId(id, nullOk=false) { const el = document.getElementById(id); if (!el && !nullOk) { console.error(`No element #${id}`); } return el; } function removeClass(el, className) { el.className = el.className.split(" ").filter(x => x !== className).join(" "); } function addClass(el, className) { el.className = el.className.split(" ").filter(x => x !== className).join(" ") + ` ${className}`; } /** * Wrap a function so that it receives `this` as first argument */ function thisToArg(f) { return function(...rest) { return f(this, ...rest); } } /** * Extend an object in-place with own properties of a second one */ function extend(obj1, obj2) { for (let key in Object.getOwnPropertyNames(obj1)) { obj1[key] = obj2[key]; } } function arrayToHashmap(array, key) { let result = Object.create(null); for (let x of array) { result[x[key]] = x; } return result; } function flattenHashmaps(hashmaps) { let result = Object.create(null); for (let x of hashmaps) { Object.assign(result, x); } return result; } function flattenArrays(arrays) { let result = []; for (let x of arrays) { Object.assign(result, x); Array.prototype.push.apply(result, x) } return result; } function startsWith(string, start) { return string.slice(0, start.length) == start; } function padLeft(input, width, padding=" ") { text = input + ""; while (text.length < width) { text = padding + text; } return text; } function toTimeZone(date, timezoneString) { // https://stackoverflow.com/questions/10087819/convert-date-to-another-timezone-in-javascript return new Date(date.toLocaleString("en-US", {timeZone: timezoneString})); } function formatYyyyMmDdWeekday(date) { const weekday = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][date.getDay()]; return `${date.getFullYear()}-${padLeft(date.getMonth() + 1, 2, "0")}-${padLeft(date.getDate(), 2, "0")}, ${weekday}`; } function formatHhMm(date) { return `${padLeft(date.getHours(), 2, "0")}:${padLeft(date.getMinutes(), 2, "0")}`; } const DATE_SIMILARITY = { none: 0, year: 1, month: 2, date: 3, hours: 4, minutes: 5, seconds: 6 } function dateSimilarity(d1, d2) { if (!d1 || !d2) return DATE_SIMILARITY.none; if (d1.getFullYear() != d2.getFullYear()) return DATE_SIMILARITY.none; if (d1.getMonth() != d2.getMonth()) return DATE_SIMILARITY.year; if (d1.getDate() != d2.getDate()) return DATE_SIMILARITY.month; if (d1.getHours() != d2.getHours()) return DATE_SIMILARITY.date; if (d1.getMinutes() != d2.getMinutes()) return DATE_SIMILARITY.hours; if (d1.getSeconds() != d2.getSeconds()) return DATE_SIMILARITY.minutes; return DATE_SIMILARITY.seconds; } class AssertionError extends Error {} /** * Throw an AssertionError if the first argument is not true */ function assert(condition, message) { if (!condition) { if (message) { throw new AssertionError(`Assertion failed: ${message}`); } else { throw new AssertionError("Assertion failed"); } } } const MATTERMOST_ID_REGEXP = /^[a-z0-9]{26}$/; function assertIsMattermostId(string, name="") { assert(MATTERMOST_ID_REGEXP.test(string), `${name} has the form of a Mattermost ID`); } function assertIsNullOrMattermostId(string, name="") { assert(string === null || MATTERMOST_ID_REGEXP.test(string), `${name} is null or has the form of a Mattermost ID`); }