-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloader.js
161 lines (127 loc) · 4.65 KB
/
loader.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// loader.js
//
// This is a loader implementation to load Single File Components (SFC) from a web server
// and use the as regular web components.
// Copyright
// The UComponent class acts as a intermediate class between user defined SFC and the generic HTMLElement class.
// It implements the generation of the shadow dom and css according to the style and template.
class UComponent extends HTMLElement {
constructor() {
super();
// create inner / document Style
const c = this.constructor;
const shadow = this.attachShadow({ mode: 'open' });
if (c.uStyle) {
if (c.uStyle.hasAttribute('scoped')) {
shadow.appendChild(c.uStyle.cloneNode(true));
} else {
document.head.appendChild(c.uStyle.cloneNode(true));
}
}
// create shadow DOM
if (c.uTemplate) {
shadow.appendChild(document.importNode(c.uTemplate.content, true));
}
} // constructor()
// Web Component is initiated and connected to a page.
// * load template and css
// * further initialization by using the init() callback
connectedCallback() {
// this.debug('connectedCallback()');
this.super = Object.getPrototypeOf(this);
const def = this.constructor;
// add event listeners
Object.getOwnPropertyNames(def.prototype).forEach(key => {
const fn = def.prototype[key];
if (key === 'onTouchstart') {
this.addEventListener('touchstart', fn.bind(this), { passive: true });
} else if (key.startsWith('on')) {
this.addEventListener(key.substring(2).toLowerCase(), fn.bind(this), false);
}
});
// add attribute data
Object.entries(def).forEach(([key, value]) => {
if (value == null || value.constructor !== Function) {
// set a default-value
if (!this[key]) {
this[key] = value;
}
} else {
// attach method
this[key] = value;
}
});
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', this.init.bind(this));
} else {
window.requestAnimationFrame(this.init.bind(this));
}
}
adoptedCallback() {
// this.info("adoptedCallback");
}
attributeChangedCallback(name, oldValue, newValue) {
// this.debug("attributeChanged", name, oldValue, newValue);
}
// logging functions
info() {
console.info(`U::${this.tagName}`, ...arguments);
};
debug() {
console.debug(`U::${this.tagName}.${this.id}`, ...arguments);
};
// The init function is called by UComponent when the whole DOM of the SFC is available.
init() {
// debugger;
};
} // class UComponent
window.loadComponent = (function() {
// plain script includes can read document.currentScript.src
// modules can use meta
const loaderURL = new URL(document.currentScript.src);
console.debug('SFC', `loadComponent...`);
async function fetchSFC(tagName, folder = undefined) {
let def;
const sfcImpl = tagName + '.vue';
let baseUrl = loaderURL;
if (folder) {
baseUrl = new URL(folder, document.location.href);
}
const url = new URL(sfcImpl, baseUrl);
console.debug('SFC', `loading module ${tagName} from ${url.href}...`);
// get DOM from sfc-file
const dom = await fetch(url)
.then(response => response.text())
.then(html => (new DOMParser()).parseFromString(html, 'text/html'));
// create class from script
const scriptObj = dom.querySelector('script');
if (scriptObj && scriptObj.textContent) {
const jsFile = new Blob([scriptObj.textContent], { type: 'application/javascript' });
const module = await import(URL.createObjectURL(jsFile));
def = module.default;
} else {
console.error('SFC', `No class defined in ${url}`);
def = UComponent;
}
// make template and style available to object constructor()
def.uTemplate = dom.querySelector('template');
def.uStyle = dom.querySelector('style');
def.for = scriptObj.getAttribute('for');
if (def.for) {
customElements.define(tagName, def, { extends: def.for });
if (def.uStyle) {
document.head.appendChild(def.uStyle.cloneNode(true));
}
console.debug('SFC', `${def.for}.${tagName} defined.`);
} else {
customElements.define(tagName, def);
console.debug('SFC', `${tagName} defined.`);
}
}; // fetchSFC()
function loadComponent(tags, folder) {
if (typeof tags === 'string') tags = tags.split(',');
return (Promise.all(tags.map((tag) => fetchSFC(tag, folder))));
}
return loadComponent;
}());
// EOF