1import {encode, decode} from 'uint8-to-base64';
2
3// transform /path/to/file.ext to file.ext
4export function basename(path = '') {
5 const lastSlashIndex = path.lastIndexOf('/');
6 return lastSlashIndex === -1 ? path : path.substring(lastSlashIndex + 1);
7}
8
9// transform /path/to/file.ext to .ext
10export function extname(path = '') {
11 const lastPointIndex = path.lastIndexOf('.');
12 return lastPointIndex === -1 ? '' : path.substring(lastPointIndex);
13}
14
15// test whether a variable is an object
16export function isObject(obj) {
17 return Object.prototype.toString.call(obj) === '[object Object]';
18}
19
20// returns whether a dark theme is enabled
21export function isDarkTheme() {
22 const style = window.getComputedStyle(document.documentElement);
23 return style.getPropertyValue('--is-dark-theme').trim().toLowerCase() === 'true';
24}
25
26// strip <tags> from a string
27export function stripTags(text) {
28 return text.replace(/<[^>]*>?/g, '');
29}
30
31export function parseIssueHref(href) {
32 const path = (href || '').replace(/[#?].*$/, '');
33 const [_, owner, repo, type, index] = /([^/]+)\/([^/]+)\/(issues|pulls)\/([0-9]+)/.exec(path) || [];
34 return {owner, repo, type, index};
35}
36
37// parse a URL, either relative '/path' or absolute 'https://localhost/path'
38export function parseUrl(str) {
39 return new URL(str, str.startsWith('http') ? undefined : window.location.origin);
40}
41
42// return current locale chosen by user
43export function getCurrentLocale() {
44 return document.documentElement.lang;
45}
46
47// given a month (0-11), returns it in the documents language
48export function translateMonth(month) {
49 return new Date(Date.UTC(2022, month, 12)).toLocaleString(getCurrentLocale(), {month: 'short', timeZone: 'UTC'});
50}
51
52// given a weekday (0-6, Sunday to Saturday), returns it in the documents language
53export function translateDay(day) {
54 return new Date(Date.UTC(2022, 7, day)).toLocaleString(getCurrentLocale(), {weekday: 'short', timeZone: 'UTC'});
55}
56
57// convert a Blob to a DataURI
58export function blobToDataURI(blob) {
59 return new Promise((resolve, reject) => {
60 try {
61 const reader = new FileReader();
62 reader.addEventListener('load', (e) => {
63 resolve(e.target.result);
64 });
65 reader.addEventListener('error', () => {
66 reject(new Error('FileReader failed'));
67 });
68 reader.readAsDataURL(blob);
69 } catch (err) {
70 reject(err);
71 }
72 });
73}
74
75// convert image Blob to another mime-type format.
76export function convertImage(blob, mime) {
77 return new Promise(async (resolve, reject) => {
78 try {
79 const img = new Image();
80 const canvas = document.createElement('canvas');
81 img.addEventListener('load', () => {
82 try {
83 canvas.width = img.naturalWidth;
84 canvas.height = img.naturalHeight;
85 const context = canvas.getContext('2d');
86 context.drawImage(img, 0, 0);
87 canvas.toBlob((blob) => {
88 if (!(blob instanceof Blob)) return reject(new Error('imageBlobToPng failed'));
89 resolve(blob);
90 }, mime);
91 } catch (err) {
92 reject(err);
93 }
94 });
95 img.addEventListener('error', () => {
96 reject(new Error('imageBlobToPng failed'));
97 });
98 img.src = await blobToDataURI(blob);
99 } catch (err) {
100 reject(err);
101 }
102 });
103}
104
105export function toAbsoluteUrl(url) {
106 if (url.startsWith('http://') || url.startsWith('https://')) {
107 return url;
108 }
109 if (url.startsWith('//')) {
110 return `${window.location.protocol}${url}`; // it's also a somewhat absolute URL (with the current scheme)
111 }
112 if (url && !url.startsWith('/')) {
113 throw new Error('unsupported url, it should either start with / or http(s)://');
114 }
115 return `${window.location.origin}${url}`;
116}
117
118// Encode an ArrayBuffer into a URLEncoded base64 string.
119export function encodeURLEncodedBase64(arrayBuffer) {
120 return encode(arrayBuffer)
121 .replace(/\+/g, '-')
122 .replace(/\//g, '_')
123 .replace(/=/g, '');
124}
125
126// Decode a URLEncoded base64 to an ArrayBuffer string.
127export function decodeURLEncodedBase64(base64url) {
128 return decode(base64url
129 .replace(/_/g, '/')
130 .replace(/-/g, '+'));
131}
132
133const domParser = new DOMParser();
134const xmlSerializer = new XMLSerializer();
135
136export function parseDom(text, contentType) {
137 return domParser.parseFromString(text, contentType);
138}
139
140export function serializeXml(node) {
141 return xmlSerializer.serializeToString(node);
142}
143
144export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));