2020-03-31 12:09:33 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-06-08 23:24:59 +02:00
|
|
|
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}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-31 12:09:33 +02:00
|
|
|
/**
|
|
|
|
* Wrap a function so that it receives `this` as first argument
|
|
|
|
*/
|
|
|
|
function thisToArg(f) {
|
|
|
|
return function(...rest) {
|
|
|
|
return f(this, ...rest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 18:12:03 +02:00
|
|
|
|
2021-02-07 21:10:36 +01:00
|
|
|
/**
|
|
|
|
* 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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-06-08 23:24:59 +02:00
|
|
|
function arrayToHashmap(array, key) {
|
|
|
|
let result = Object.create(null);
|
|
|
|
for (let x of array) {
|
|
|
|
result[x[key]] = x;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-06-09 00:29:24 +02:00
|
|
|
function startsWith(string, start) {
|
|
|
|
return string.slice(0, start.length) == start;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-07 21:47:59 +01:00
|
|
|
function padLeft(input, width, padding=" ") {
|
|
|
|
text = input + "";
|
|
|
|
while (text.length < width) {
|
|
|
|
text = padding + text;
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2022-06-09 15:27:50 +02:00
|
|
|
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}));
|
|
|
|
}
|
|
|
|
|
2022-06-11 17:25:12 +02:00
|
|
|
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}`;
|
2021-02-07 21:47:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-31 18:12:03 +02:00
|
|
|
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`);
|
|
|
|
}
|