-
Notifications
You must be signed in to change notification settings - Fork 0
/
ico.js
115 lines (99 loc) · 4.18 KB
/
ico.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// icons from directory
// [u1-ico] {
// --u1-ico-dir:'https://cdn.jsdelivr.net/npm/teenyicons@0.4.1/outline/x-{icon-name}.svg';
// }
const uIco = class extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
if (this.firstElementChild) return; // skip if not text-only
// fetch svg if --ui-ico-directory is set
let dir = getComputedStyle(this).getPropertyValue('--u1-ico-dir').trim();
if (dir) {
if (dir[0]!=='"' && dir[0]!=="'") console.error('the value of --u1-ico-dir must be surrounded by quotes');
dir = dir.slice(1, -1);
const inner = this.innerHTML.trim();
const name = this.getAttribute('icon') || inner;
this.setAttribute('icon',name);
const path = dirTemplateToUrl(dir, name);
this.setAttribute('state','loading');
//return;
loadSvgString(path).then(svg=>{
// if (path.origin !== location.origin) {} todo: sanitize svg
// requestAnimationFrame??
this.innerHTML = svg;
this.querySelectorAll('[id]').forEach(el=>el.removeAttribute('id')); // remove ids
if (inner) { // if the name was the content of the element, it was intended to be the label
this.firstElementChild.setAttribute('aria-label', inner);
} else {
this.firstElementChild.setAttribute('aria-hidden', 'true');
}
this.setAttribute('state','loaded');
}).catch(err=>{
console.error(err);
this.setAttribute('state','fail');
});
return;
}
// at the moment, "loaded" indicates to css, that it uses --u1-ico-dir
// let font = getComputedStyle(this).getPropertyValue('font-family');
// if (font) {
// font = '1rem '+font;
// this.setAttribute('state','loading');
// document.fonts.load(font).then(()=>{
// this.setAttribute('state','loaded');
// }).catch(err=>{
// console.error(err);
// this.setAttribute('state','fail');
// });
// }
}
}
customElements.define('u1-ico', uIco);
function dirTemplateToUrl(dir, name) {
let [prefix, firstWord, between='', nextWord, suffix] = dir.split(/{(icon)([^n]*)?(name)?}/i);
// old: if (!suffix) suffix = '.svg';
if (!suffix) {
suffix = prefix.includes('#') ? '' : '.svg';
}
if (!nextWord) between = '-'; // if just: {icon}
// icon naming convertion
let fileName = name;
const upperFirst = firstWord?.[0] === 'I';
const upperWords = nextWord?.[0] === 'N';
if (upperFirst) fileName = fileName.replace(/^([a-z])/, g => g[0].toUpperCase()); // first upper
if (upperWords) fileName = fileName.replace(/-([a-z])/g, g => g[1].toUpperCase()); // camel-case
if (between !== '-') fileName = fileName.replace(/-/g, between);
return new URL(prefix + fileName + suffix, location.href);
}
async function loadSvgString(url) {
const hash = url.hash.substring(1);
// internal element
if (url.origin === location.origin && url.pathname === location.pathname) { // svg on the same document
if (hash) {
const element = document.getElementById(hash);
if (element) {
return element.outerHTML;
} else {
throw new Error("Element with id " + hash + " not found in the document");
}
}
}
const res = await fetch(url, {cache: "force-cache"}); // "force-cache": why is the response not cached like direct in the browser?
if (!res.ok) throw new Error("Not 2xx response");
const svg = await res.text();
// external element
if (hash) { // todo, cache the svgDoc?
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svg, "image/svg+xml");
const element = svgDoc.getElementById(hash);
if (element) {
return element.outerHTML;
} else {
throw new Error("Element with id " + hash + " not found in the document");
}
}
// external document
return svg;
}