2020-06-06 00:03:37 +02:00
|
|
|
"use strict";
|
2016-03-31 23:18:08 +02:00
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
const markdown = require("./markdown.js");
|
|
|
|
const uri = require("./uri.js");
|
|
|
|
const settings = require("../models/settings.js");
|
2019-05-22 23:08:26 +02:00
|
|
|
|
2016-07-22 13:27:52 +02:00
|
|
|
function decamelize(str, sep) {
|
2020-06-06 00:03:37 +02:00
|
|
|
sep = sep === undefined ? "-" : sep;
|
2016-07-22 13:27:52 +02:00
|
|
|
return str
|
2020-06-06 00:03:37 +02:00
|
|
|
.replace(/([a-z\d])([A-Z])/g, "$1" + sep + "$2")
|
|
|
|
.replace(/([A-Z]+)([A-Z][a-z\d]+)/g, "$1" + sep + "$2")
|
2016-07-22 13:27:52 +02:00
|
|
|
.toLowerCase();
|
|
|
|
}
|
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
function* range(start = 0, end = null, step = 1) {
|
2020-06-04 20:09:35 +02:00
|
|
|
if (end === null) {
|
2016-04-07 22:54:45 +02:00
|
|
|
end = start;
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = start; i < end; i += step) {
|
|
|
|
yield i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-22 22:39:31 +02:00
|
|
|
function _formatUnits(number, base, suffixes, callback) {
|
|
|
|
if (!number && number !== 0) {
|
|
|
|
return NaN;
|
|
|
|
}
|
|
|
|
number *= 1.0;
|
|
|
|
let suffix = suffixes.shift();
|
|
|
|
while (number >= base && suffixes.length > 0) {
|
|
|
|
suffix = suffixes.shift();
|
|
|
|
number /= base;
|
|
|
|
}
|
|
|
|
if (callback === undefined) {
|
|
|
|
callback = (number, suffix) => {
|
|
|
|
return suffix ? number.toFixed(1) + suffix : number;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return callback(number, suffix);
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatFileSize(fileSize) {
|
|
|
|
return _formatUnits(
|
|
|
|
fileSize,
|
|
|
|
1024,
|
2020-06-06 00:03:37 +02:00
|
|
|
["B", "K", "M", "G"],
|
2016-05-22 22:39:31 +02:00
|
|
|
(number, suffix) => {
|
2020-06-06 00:03:37 +02:00
|
|
|
const decimalPlaces = number < 20 && suffix !== "B" ? 1 : 0;
|
2016-05-22 22:39:31 +02:00
|
|
|
return number.toFixed(decimalPlaces) + suffix;
|
2020-06-06 00:03:37 +02:00
|
|
|
}
|
|
|
|
);
|
2016-05-22 22:39:31 +02:00
|
|
|
}
|
|
|
|
|
2016-03-31 23:18:08 +02:00
|
|
|
function formatRelativeTime(timeString) {
|
|
|
|
if (!timeString) {
|
2020-06-06 00:03:37 +02:00
|
|
|
return "never";
|
2016-03-31 23:18:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const then = Date.parse(timeString);
|
|
|
|
const now = Date.now();
|
|
|
|
const difference = Math.abs(now - then) / 1000.0;
|
|
|
|
const future = now < then;
|
|
|
|
|
|
|
|
const descriptions = [
|
2020-06-06 00:03:37 +02:00
|
|
|
[60, "a few seconds", null],
|
|
|
|
[60 * 2, "a minute", null],
|
|
|
|
[60 * 60, "% minutes", 60],
|
|
|
|
[60 * 60 * 2, "an hour", null],
|
|
|
|
[60 * 60 * 24, "% hours", 60 * 60],
|
|
|
|
[60 * 60 * 24 * 2, "a day", null],
|
|
|
|
[60 * 60 * 24 * 30.42, "% days", 60 * 60 * 24],
|
|
|
|
[60 * 60 * 24 * 30.42 * 2, "a month", null],
|
|
|
|
[60 * 60 * 24 * 30.42 * 12, "% months", 60 * 60 * 24 * 30.42],
|
|
|
|
[60 * 60 * 24 * 30.42 * 12 * 2, "a year", null],
|
|
|
|
[8640000000000000 /* max*/, "% years", 60 * 60 * 24 * 30.42 * 12],
|
2016-03-31 23:18:08 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
let text = null;
|
|
|
|
for (let kv of descriptions) {
|
|
|
|
const multiplier = kv[0];
|
|
|
|
const template = kv[1];
|
|
|
|
const divider = kv[2];
|
|
|
|
if (difference < multiplier) {
|
|
|
|
text = template.replace(/%/, Math.round(difference / divider));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
if (text === "a day") {
|
|
|
|
return future ? "tomorrow" : "yesterday";
|
2016-03-31 23:18:08 +02:00
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
return future ? "in " + text : text + " ago";
|
2016-03-31 23:18:08 +02:00
|
|
|
}
|
|
|
|
|
2016-05-29 12:26:29 +02:00
|
|
|
function formatMarkdown(text) {
|
2016-09-10 11:09:24 +02:00
|
|
|
return markdown.formatMarkdown(text);
|
2016-05-29 12:26:29 +02:00
|
|
|
}
|
|
|
|
|
2016-09-10 11:11:07 +02:00
|
|
|
function formatInlineMarkdown(text) {
|
|
|
|
return markdown.formatInlineMarkdown(text);
|
|
|
|
}
|
|
|
|
|
2016-07-03 13:46:49 +02:00
|
|
|
function splitByWhitespace(str) {
|
2020-06-06 00:03:37 +02:00
|
|
|
return str.split(/\s+/).filter((s) => s);
|
2016-07-03 13:46:49 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 23:59:24 +02:00
|
|
|
function unindent(callSite, ...args) {
|
|
|
|
function format(str) {
|
|
|
|
let size = -1;
|
|
|
|
return str.replace(/\n(\s+)/g, (m, m1) => {
|
|
|
|
if (size < 0) {
|
2020-06-06 00:03:37 +02:00
|
|
|
size = m1.replace(/\t/g, " ").length;
|
2016-05-20 23:59:24 +02:00
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
return "\n" + m1.slice(Math.min(m1.length, size));
|
2016-05-20 23:59:24 +02:00
|
|
|
});
|
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
if (typeof callSite === "string") {
|
2016-05-20 23:59:24 +02:00
|
|
|
return format(callSite);
|
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
if (typeof callSite === "function") {
|
2016-05-20 23:59:24 +02:00
|
|
|
return (...args) => format(callSite(...args));
|
|
|
|
}
|
|
|
|
let output = callSite
|
|
|
|
.slice(0, args.length + 1)
|
2020-06-06 00:03:37 +02:00
|
|
|
.map((text, i) => (i === 0 ? "" : args[i - 1]) + text)
|
|
|
|
.join("");
|
2016-05-20 23:59:24 +02:00
|
|
|
return format(output);
|
|
|
|
}
|
|
|
|
|
2016-06-11 17:41:28 +02:00
|
|
|
function enableExitConfirmation() {
|
2020-06-06 00:03:37 +02:00
|
|
|
window.onbeforeunload = (e) => {
|
|
|
|
return (
|
|
|
|
"Are you sure you want to leave? " +
|
|
|
|
"Data you have entered may not be saved."
|
|
|
|
);
|
2016-06-11 17:41:28 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function disableExitConfirmation() {
|
|
|
|
window.onbeforeunload = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function confirmPageExit() {
|
|
|
|
if (!window.onbeforeunload) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (window.confirm(window.onbeforeunload())) {
|
|
|
|
disableExitConfirmation();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 23:12:34 +02:00
|
|
|
function makeCssName(text, suffix) {
|
2020-06-06 00:03:37 +02:00
|
|
|
return suffix + "-" + text.replace(/[^a-z0-9]/g, "_");
|
2016-06-22 23:12:34 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 11:41:52 +02:00
|
|
|
function escapeHtml(unsafe) {
|
2020-06-06 00:03:37 +02:00
|
|
|
return unsafe
|
|
|
|
.toString()
|
|
|
|
.replace(/&/g, "&")
|
|
|
|
.replace(/</g, "<")
|
|
|
|
.replace(/>/g, ">")
|
|
|
|
.replace(/"/g, """)
|
|
|
|
.replace(/'/g, "'");
|
2016-06-23 11:41:52 +02:00
|
|
|
}
|
|
|
|
|
2016-08-28 23:48:50 +02:00
|
|
|
function arraysDiffer(source1, source2, orderImportant) {
|
2016-07-22 13:27:52 +02:00
|
|
|
source1 = [...source1];
|
|
|
|
source2 = [...source2];
|
2016-08-28 23:48:50 +02:00
|
|
|
if (orderImportant === true) {
|
|
|
|
if (source1.length !== source2.length) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for (let i = 0; i < source1.length; i++) {
|
|
|
|
if (source1[i] !== source2[i]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-26 20:49:48 +02:00
|
|
|
return (
|
2020-06-06 00:03:37 +02:00
|
|
|
source1.filter((value) => !source2.includes(value)).length > 0 ||
|
|
|
|
source2.filter((value) => !source1.includes(value)).length > 0
|
|
|
|
);
|
2016-07-26 20:49:48 +02:00
|
|
|
}
|
|
|
|
|
2016-09-29 22:45:40 +02:00
|
|
|
function escapeSearchTerm(text) {
|
2020-06-06 00:03:37 +02:00
|
|
|
return text.replace(/([a-z_-]):/g, "$1\\:");
|
2016-09-29 22:45:40 +02:00
|
|
|
}
|
|
|
|
|
2017-01-07 11:07:51 +01:00
|
|
|
function dataURItoBlob(dataURI) {
|
2020-06-06 00:03:37 +02:00
|
|
|
const chunks = dataURI.split(",");
|
|
|
|
const byteString =
|
|
|
|
chunks[0].indexOf("base64") >= 0
|
|
|
|
? window.atob(chunks[1])
|
|
|
|
: unescape(chunks[1]);
|
|
|
|
const mimeString = chunks[0].split(":")[1].split(";")[0];
|
2017-01-07 11:07:51 +01:00
|
|
|
const data = new Uint8Array(byteString.length);
|
2020-06-04 20:09:35 +02:00
|
|
|
for (let i = 0; i < byteString.length; i++) {
|
2017-01-07 11:07:51 +01:00
|
|
|
data[i] = byteString.charCodeAt(i);
|
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
return new Blob([data], { type: mimeString });
|
2017-01-07 11:07:51 +01:00
|
|
|
}
|
|
|
|
|
2019-05-22 23:08:26 +02:00
|
|
|
function getPrettyTagName(tag) {
|
2019-05-24 02:27:59 +02:00
|
|
|
if (settings.get().tagUnderscoresAsSpaces) {
|
2019-05-22 23:08:26 +02:00
|
|
|
return tag.replace(/_/g, " ");
|
|
|
|
}
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
2016-09-29 11:16:55 +02:00
|
|
|
module.exports = {
|
2020-06-04 20:09:35 +02:00
|
|
|
range: range,
|
|
|
|
formatRelativeTime: formatRelativeTime,
|
|
|
|
formatFileSize: formatFileSize,
|
|
|
|
formatMarkdown: formatMarkdown,
|
|
|
|
formatInlineMarkdown: formatInlineMarkdown,
|
|
|
|
unindent: unindent,
|
|
|
|
enableExitConfirmation: enableExitConfirmation,
|
2016-09-29 11:16:55 +02:00
|
|
|
disableExitConfirmation: disableExitConfirmation,
|
2020-06-04 20:09:35 +02:00
|
|
|
confirmPageExit: confirmPageExit,
|
|
|
|
escapeHtml: escapeHtml,
|
|
|
|
makeCssName: makeCssName,
|
|
|
|
splitByWhitespace: splitByWhitespace,
|
|
|
|
arraysDiffer: arraysDiffer,
|
|
|
|
decamelize: decamelize,
|
|
|
|
escapeSearchTerm: escapeSearchTerm,
|
|
|
|
dataURItoBlob: dataURItoBlob,
|
|
|
|
getPrettyTagName: getPrettyTagName,
|
2016-09-29 11:16:55 +02:00
|
|
|
};
|