mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-22 14:39:34 +01:00
269 lines
5.9 KiB
TypeScript
269 lines
5.9 KiB
TypeScript
/*
|
|
This file is part of the Notesnook project (https://notesnook.com/)
|
|
|
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Only WOFF and EOT mime types for fonts are 'real'
|
|
* see http://www.iana.org/assignments/media-types/media-types.xhtml
|
|
*/
|
|
const WOFF = "application/font-woff";
|
|
const JPEG = "image/jpeg";
|
|
|
|
const mimes = {
|
|
woff: WOFF,
|
|
woff2: WOFF,
|
|
ttf: "application/font-truetype",
|
|
eot: "application/vnd.ms-fontobject",
|
|
png: "image/png",
|
|
jpg: JPEG,
|
|
jpeg: JPEG,
|
|
gif: "image/gif",
|
|
tiff: "image/tiff",
|
|
svg: "image/svg+xml"
|
|
};
|
|
|
|
function parseExtension(url: string) {
|
|
const match = /\.([^./]*?)(\?|$)/g.exec(url);
|
|
if (match) return match[1];
|
|
else return "";
|
|
}
|
|
|
|
function mimeType(url: string) {
|
|
const extension = parseExtension(url).toLowerCase();
|
|
return mimes[extension as keyof typeof mimes] || "";
|
|
}
|
|
|
|
function isDataUrl(url: string) {
|
|
return url.search(/^(data:)/) !== -1;
|
|
}
|
|
|
|
function asBlob(canvas: HTMLCanvasElement) {
|
|
const binaryString = atob(canvas.toDataURL().split(",")[1]);
|
|
const length = binaryString.length;
|
|
const binaryArray = new Uint8Array(length);
|
|
|
|
for (let i = 0; i < length; i++) binaryArray[i] = binaryString.charCodeAt(i);
|
|
|
|
return new Blob([binaryArray], {
|
|
type: "image/png"
|
|
});
|
|
}
|
|
|
|
function canvasToBlob(canvas: HTMLCanvasElement) {
|
|
if (canvas.toBlob)
|
|
return new Promise<Blob | null>(function (resolve) {
|
|
canvas.toBlob(resolve);
|
|
});
|
|
|
|
return Promise.resolve(asBlob(canvas));
|
|
}
|
|
|
|
function resolveUrl(url: string, baseUrl: string) {
|
|
const doc = document.implementation.createHTMLDocument();
|
|
const base = doc.createElement("base");
|
|
doc.head.appendChild(base);
|
|
const a = doc.createElement("a");
|
|
doc.body.appendChild(a);
|
|
base.href = baseUrl;
|
|
a.href = url;
|
|
return a.href;
|
|
}
|
|
|
|
let index = 0;
|
|
function uid() {
|
|
return "u" + fourRandomChars() + index++;
|
|
|
|
function fourRandomChars() {
|
|
/* see http://stackoverflow.com/a/6248722/2519373 */
|
|
return (
|
|
"0000" + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)
|
|
).slice(-4);
|
|
}
|
|
}
|
|
|
|
function dataAsUrl(content: string, type: string) {
|
|
return "data:" + type + ";base64," + content;
|
|
}
|
|
|
|
function escape(string: string) {
|
|
return string.replace(/([.*+?^${}()|[\]/\\])/g, "\\$1");
|
|
}
|
|
|
|
function delay(ms: number) {
|
|
return function <T>(arg: T) {
|
|
return new Promise<T>(function (resolve) {
|
|
setTimeout(function () {
|
|
resolve(arg);
|
|
}, ms);
|
|
});
|
|
};
|
|
}
|
|
|
|
function asArray<T>(arrayLike: ArrayLike<T>) {
|
|
const array = [];
|
|
const length = arrayLike.length;
|
|
for (let i = 0; i < length; i++) array.push(arrayLike[i]);
|
|
return array;
|
|
}
|
|
|
|
function escapeXhtml(string: string) {
|
|
return string.replace(/%/g, "%25").replace(/#/g, "%23").replace(/\n/g, "%0A");
|
|
}
|
|
|
|
function width(node: HTMLElement) {
|
|
const leftBorder = px(node, "border-left-width");
|
|
const rightBorder = px(node, "border-right-width");
|
|
return node.scrollWidth + leftBorder + rightBorder;
|
|
}
|
|
|
|
function height(node: HTMLElement) {
|
|
const topBorder = px(node, "border-top-width");
|
|
const bottomBorder = px(node, "border-bottom-width");
|
|
return node.scrollHeight + topBorder + bottomBorder;
|
|
}
|
|
|
|
function px(node: HTMLElement, styleProperty: string) {
|
|
const value = getComputedStyle(node).getPropertyValue(styleProperty);
|
|
return parseFloat(value.replace("px", ""));
|
|
}
|
|
|
|
function injectCss(rules: string, id: string) {
|
|
const variableCss = document.getElementById(id);
|
|
const head = document.getElementsByTagName("head")[0];
|
|
if (variableCss) {
|
|
head.removeChild(variableCss);
|
|
}
|
|
const css = document.createElement("style");
|
|
css.type = "text/css";
|
|
css.id = id;
|
|
css.appendChild(document.createTextNode(rules));
|
|
|
|
head.insertBefore(css, getRootStylesheet());
|
|
}
|
|
|
|
function getRootStylesheet() {
|
|
for (const sty of document.querySelectorAll("style")) {
|
|
if (sty.innerHTML.includes("#root")) {
|
|
return sty;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const SVGElements = [
|
|
"altGlyph",
|
|
"altGlyphDef",
|
|
"altGlyphItem",
|
|
"animate",
|
|
"animateColor",
|
|
"animateMotion",
|
|
"animateTransform",
|
|
"circle",
|
|
"clipPath",
|
|
"color-profile",
|
|
"cursor",
|
|
"defs",
|
|
"desc",
|
|
"ellipse",
|
|
"feBlend",
|
|
"feColorMatrix",
|
|
"feComponentTransfer",
|
|
"feComposite",
|
|
"feConvolveMatrix",
|
|
"feDiffuseLighting",
|
|
"feDisplacementMap",
|
|
"feDistantLight",
|
|
"feFlood",
|
|
"feFuncA",
|
|
"feFuncB",
|
|
"feFuncG",
|
|
"feFuncR",
|
|
"feGaussianBlur",
|
|
"feImage",
|
|
"feMerge",
|
|
"feMergeNode",
|
|
"feMorphology",
|
|
"feOffset",
|
|
"fePointLight",
|
|
"feSpecularLighting",
|
|
"feSpotLight",
|
|
"feTile",
|
|
"feTurbulence",
|
|
"filter",
|
|
"font-face",
|
|
"font-face-format",
|
|
"font-face-name",
|
|
"font-face-src",
|
|
"font-face-uri",
|
|
"foreignObject",
|
|
"g",
|
|
"glyph",
|
|
"glyphRef",
|
|
"hkern",
|
|
"image",
|
|
"line",
|
|
"linearGradient",
|
|
"marker",
|
|
"mask",
|
|
"metadata",
|
|
"missing-glyph",
|
|
"mpath",
|
|
"path",
|
|
"pattern",
|
|
"polygon",
|
|
"polyline",
|
|
"radialGradient",
|
|
"rect",
|
|
"set",
|
|
"stop",
|
|
"svg",
|
|
"switch",
|
|
"symbol",
|
|
"text",
|
|
"textPath",
|
|
"title",
|
|
"tref",
|
|
"tspan",
|
|
"use",
|
|
"view",
|
|
"vkern"
|
|
].map((a) => a.toLowerCase());
|
|
|
|
function isSVGElement(element: HTMLElement) {
|
|
if (!element || !element.tagName) return false;
|
|
return SVGElements.includes(element.tagName.toLowerCase());
|
|
}
|
|
|
|
export {
|
|
injectCss,
|
|
escape,
|
|
parseExtension,
|
|
mimeType,
|
|
dataAsUrl,
|
|
isDataUrl,
|
|
canvasToBlob,
|
|
resolveUrl,
|
|
uid,
|
|
delay,
|
|
asArray,
|
|
escapeXhtml,
|
|
width,
|
|
height,
|
|
isSVGElement
|
|
};
|