@optionfactory/ful 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/ful-client-errors.iife.min.js.map +1 -1
- package/dist/ful.css +2 -0
- package/dist/ful.css.map +1 -0
- package/dist/ful.iife.js +581 -247
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +575 -247
- package/dist/ful.mjs.map +1 -1
- package/package.json +9 -5
package/dist/ful.iife.js
CHANGED
|
@@ -1,105 +1,6 @@
|
|
|
1
1
|
var ful = (function (exports) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
/* global CSS */
|
|
5
|
-
|
|
6
|
-
function extract(extractors, el) {
|
|
7
|
-
const maybeExtractor = extractors[el.dataset['bindExtractor']] || extractors[el.dataset['bindProvide']];
|
|
8
|
-
if (maybeExtractor) {
|
|
9
|
-
return maybeExtractor(el);
|
|
10
|
-
}
|
|
11
|
-
if (el.getAttribute('type') === 'radio') {
|
|
12
|
-
if (!el.checked) {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
return el.dataset['bindType'] === 'boolean' ? el.value === 'true' : el.value;
|
|
16
|
-
}
|
|
17
|
-
if (el.getAttribute('type') === 'checkbox') {
|
|
18
|
-
return el.checked;
|
|
19
|
-
}
|
|
20
|
-
if (el.dataset['bindType'] === 'boolean') {
|
|
21
|
-
return !el.value ? null : el.value === 'true';
|
|
22
|
-
}
|
|
23
|
-
return el.value || null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function mutate(mutators, el, raw, key, values) {
|
|
27
|
-
const maybeMutator = mutators[el.dataset['bindMutator']] || mutators[el.dataset['bindProvide']];
|
|
28
|
-
if (maybeMutator) {
|
|
29
|
-
maybeMutator(el, raw, key, values);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (el.getAttribute('type') === 'radio') {
|
|
33
|
-
el.checked = el.getAttribute('value') === raw;
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (el.getAttribute('type') === 'checkbox') {
|
|
37
|
-
el.checked = raw;
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
el.value = raw;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function providePath(result, path, value) {
|
|
45
|
-
const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
|
|
46
|
-
let current = result;
|
|
47
|
-
let previous = null;
|
|
48
|
-
for (let i = 0; ; ++i) {
|
|
49
|
-
const ckey = keys[i];
|
|
50
|
-
const pkey = keys[i - 1];
|
|
51
|
-
if (Number.isInteger(ckey) && !Array.isArray(current)) {
|
|
52
|
-
if (previous !== null) {
|
|
53
|
-
previous[pkey] = current = [];
|
|
54
|
-
} else {
|
|
55
|
-
result = current = [];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (i === keys.length - 1) {
|
|
59
|
-
//when value is undefined we only want to define the property if it's not defined
|
|
60
|
-
current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
if (current[ckey] === undefined) {
|
|
64
|
-
current[ckey] = {};
|
|
65
|
-
}
|
|
66
|
-
previous = current;
|
|
67
|
-
current = current[ckey];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
class Bindings {
|
|
72
|
-
|
|
73
|
-
constructor( {extractors, mutators, ignoredChildrenSelector, valueHoldersSelector}) {
|
|
74
|
-
this.extractors = extractors || {};
|
|
75
|
-
this.mutators = mutators || {};
|
|
76
|
-
this.valueHoldersSelector = valueHoldersSelector || 'input[name], select[name], textarea[name]';
|
|
77
|
-
this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none';
|
|
78
|
-
}
|
|
79
|
-
setValues(el, values) {
|
|
80
|
-
for (let k in values) {
|
|
81
|
-
if (!values.hasOwnProperty(k)) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
Array.from(el.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
|
|
85
|
-
mutate(this.mutators, el, values[k], k, values);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
getValues(el) {
|
|
90
|
-
return Array.from(el.querySelectorAll(this.valueHoldersSelector))
|
|
91
|
-
.filter((el) => {
|
|
92
|
-
if (el.dataset['bindInclude'] === 'never') {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
return el.dataset['bindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
|
|
96
|
-
})
|
|
97
|
-
.reduce((result, el) => {
|
|
98
|
-
return providePath(result, el.getAttribute('name'), extract(this.extractors, el));
|
|
99
|
-
}, {});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
4
|
class Base64 {
|
|
104
5
|
static encode(arrayBuffer, dialect) {
|
|
105
6
|
const d = dialect || Base64.URL_SAFE;
|
|
@@ -172,78 +73,54 @@ var ful = (function (exports) {
|
|
|
172
73
|
}
|
|
173
74
|
}
|
|
174
75
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
class Form {
|
|
179
|
-
constructor(el, bindings, {globalErrorsEl, fieldContainerSelector, errorClass, hideClass}) {
|
|
180
|
-
this.el = el;
|
|
181
|
-
this.bindings = bindings;
|
|
182
|
-
this.globalErrorsEl = globalErrorsEl;
|
|
183
|
-
this.fieldContainerSelector = fieldContainerSelector !== undefined ? fieldContainerSelector : Form.DEFAULT_FIELD_CONTAINER_SELECTOR;
|
|
184
|
-
this.errorClass = errorClass || Form.DEFAULT_ERROR_CLASS;
|
|
185
|
-
this.hideClass = hideClass || Form.DEFAULT_HIDE_CLASS;
|
|
186
|
-
}
|
|
187
|
-
setValues(values) {
|
|
188
|
-
return this.bindings.setValues(this.el, values);
|
|
189
|
-
}
|
|
190
|
-
getValues() {
|
|
191
|
-
return this.bindings.getValues(this.el);
|
|
76
|
+
class Observable {
|
|
77
|
+
constructor() {
|
|
78
|
+
this.listeners = {};
|
|
192
79
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
.filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
|
|
199
|
-
.forEach((e) => {
|
|
200
|
-
const name = e.context.replace("[", ".").replace("].", ".");
|
|
201
|
-
Array.from(this.el.querySelectorAll(`[name='${CSS.escape(name)}']`))
|
|
202
|
-
.map(el => this.fieldContainerSelector ? el.closest(this.fieldContainerSelector) : el)
|
|
203
|
-
.filter(el => el !== null)
|
|
204
|
-
.forEach(label => {
|
|
205
|
-
label.classList.add(this.errorClass);
|
|
206
|
-
label.dataset['error'] = e.reason;
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
if (this.globalErrorsEl) {
|
|
210
|
-
const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
|
|
211
|
-
this.globalErrorsEl.innerHTML = globalErrors.map(e => e.reason).join("\n");
|
|
212
|
-
if (globalErrors.length !== 0) {
|
|
213
|
-
this.globalErrorsEl.classList.remove(this.hideClass);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (!scrollFirstErrorIntoView) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
const yOffsets = Array.from(this.el.querySelectorAll('.${CSS.escape(this.errorClass)}'))
|
|
220
|
-
.map((label) => label.getBoundingClientRect().y + window.scrollY);
|
|
221
|
-
const firstErrorScrollY = Math.min(...yOffsets);
|
|
222
|
-
if (firstErrorScrollY !== Infinity) {
|
|
223
|
-
window.scroll(window.scrollX, firstErrorScrollY > 100 ? firstErrorScrollY - 100 : 0);
|
|
80
|
+
fireSync(event, data, initialAcc) {
|
|
81
|
+
const listeners = this.listeners[event] || [];
|
|
82
|
+
let acc = initialAcc;
|
|
83
|
+
for (const l of listeners) {
|
|
84
|
+
acc = l(data, this, acc);
|
|
224
85
|
}
|
|
86
|
+
return acc;
|
|
225
87
|
}
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
88
|
+
async fire(event, data, initialAcc) {
|
|
89
|
+
const listeners = this.listeners[event] || [];
|
|
90
|
+
let acc = initialAcc;
|
|
91
|
+
for (const l of listeners) {
|
|
92
|
+
acc = await l(data, this, acc);
|
|
231
93
|
}
|
|
94
|
+
return acc;
|
|
95
|
+
}
|
|
96
|
+
on(event, listener) {
|
|
97
|
+
this.listeners[event] = this.listeners[event] || [];
|
|
98
|
+
this.listeners[event].push(listener);
|
|
99
|
+
}
|
|
100
|
+
un(event, listener) {
|
|
101
|
+
const listeners = this.listeners[event] || [];
|
|
102
|
+
const idx = listeners.indexOf(listener);
|
|
103
|
+
return idx === -1 ? [] : listeners.splice(idx, 1);
|
|
104
|
+
}
|
|
105
|
+
static mixin(self) {
|
|
106
|
+
self.listeners = {};
|
|
107
|
+
self.fireSync = Observable.prototype.fireSync;
|
|
108
|
+
self.fire = Observable.prototype.fire;
|
|
109
|
+
self.on = Observable.prototype.on;
|
|
110
|
+
self.un = Observable.prototype.un;
|
|
232
111
|
}
|
|
233
|
-
}
|
|
234
112
|
|
|
235
|
-
|
|
236
|
-
Form.DEFAULT_ERROR_CLASS = 'has-error';
|
|
237
|
-
Form.DEFAULT_HIDE_CLASS = 'd-none';
|
|
113
|
+
}
|
|
238
114
|
|
|
239
115
|
class ContextInterceptor {
|
|
240
116
|
constructor() {
|
|
241
117
|
const context = document.querySelector("meta[name='context']").getAttribute("content");
|
|
242
118
|
this.context = context.endsWith("/") ? context.substring(0, context.length - 1) : context;
|
|
243
119
|
}
|
|
244
|
-
|
|
120
|
+
async intercept(request, chain){
|
|
245
121
|
const separator = request.resource.startsWith("/") ? "" : "/";
|
|
246
122
|
request.resource = this.context + separator + request.resource;
|
|
123
|
+
return await chain.proceed(request);
|
|
247
124
|
}
|
|
248
125
|
}
|
|
249
126
|
|
|
@@ -252,10 +129,11 @@ var ful = (function (exports) {
|
|
|
252
129
|
this.k = document.querySelector("meta[name='_csrf_header']").getAttribute("content");
|
|
253
130
|
this.v = document.querySelector("meta[name='_csrf']").getAttribute("content");
|
|
254
131
|
}
|
|
255
|
-
|
|
132
|
+
async intercept(request, chain){
|
|
256
133
|
const headers = new Headers(request.options.headers);
|
|
257
134
|
headers.set(this.k, this.v);
|
|
258
135
|
request.options.headers = headers;
|
|
136
|
+
return await chain.proceed(request);
|
|
259
137
|
}
|
|
260
138
|
}
|
|
261
139
|
|
|
@@ -263,9 +141,10 @@ var ful = (function (exports) {
|
|
|
263
141
|
constructor(redirectUri) {
|
|
264
142
|
this.redirectUri = redirectUri;
|
|
265
143
|
}
|
|
266
|
-
|
|
144
|
+
async intercept(request, chain){
|
|
145
|
+
const response = await chain.proceed(request);
|
|
267
146
|
if (response.status !== 401) {
|
|
268
|
-
return;
|
|
147
|
+
return response;
|
|
269
148
|
}
|
|
270
149
|
window.location.href = redirectUri;
|
|
271
150
|
}
|
|
@@ -322,31 +201,35 @@ var ful = (function (exports) {
|
|
|
322
201
|
}
|
|
323
202
|
}
|
|
324
203
|
|
|
204
|
+
class HttpCall {
|
|
205
|
+
async intercept(request, chain){
|
|
206
|
+
return await fetch(request.resource, request.options);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
class HttpInterceptorChain {
|
|
211
|
+
constructor(interceptors, current){
|
|
212
|
+
this.interceptors = interceptors;
|
|
213
|
+
this.current = current;
|
|
214
|
+
}
|
|
215
|
+
async proceed(request){
|
|
216
|
+
const interceptor = this.interceptors[this.current];
|
|
217
|
+
return await interceptor.intercept(request, new HttpInterceptorChain(this.interceptors, this.current + 1));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
325
222
|
class HttpClient {
|
|
326
223
|
static builder() {
|
|
327
224
|
return new HttpClientBuilder();
|
|
328
225
|
}
|
|
329
|
-
constructor(
|
|
226
|
+
constructor({interceptors}){
|
|
330
227
|
this.interceptors = interceptors || [];
|
|
331
228
|
}
|
|
332
229
|
async fetch(resource, options) {
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
await
|
|
336
|
-
if (!i.before) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
await i.before(request);
|
|
340
|
-
});
|
|
341
|
-
const response = await fetch(request.resource, request.options);
|
|
342
|
-
await is.forEach(async (i) => {
|
|
343
|
-
if (!i.after) {
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
await i.after(request, response);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
return response;
|
|
230
|
+
const interceptors = [...this.interceptors, ...options.interceptors || [], new HttpCall()];
|
|
231
|
+
const chain = new HttpInterceptorChain(interceptors, 0);
|
|
232
|
+
return await chain.proceed({resource, options});
|
|
350
233
|
}
|
|
351
234
|
async json(resource, options) {
|
|
352
235
|
try {
|
|
@@ -369,30 +252,456 @@ var ful = (function (exports) {
|
|
|
369
252
|
}]);
|
|
370
253
|
}
|
|
371
254
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* global Infinity, CSS */
|
|
258
|
+
|
|
259
|
+
class CustomElements {
|
|
260
|
+
static id = 0;
|
|
261
|
+
static uid(prefix) {
|
|
262
|
+
return `${prefix}-${++CustomElements.id}`;
|
|
263
|
+
}
|
|
264
|
+
static forwardAttributes(from, to, except) {
|
|
265
|
+
from.getAttributeNames().filter(a => except.indexOf(a) === -1)
|
|
266
|
+
.filter(a => a[0] === '@')
|
|
267
|
+
.forEach(a => {
|
|
268
|
+
if (a === '@class') {
|
|
269
|
+
to.classList.add(...from.getAttribute("@class").split(" ").filter(a => a.length));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
to.setAttribute(a.substring(1), from.getAttribute(a));
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
static extractSlots(el) {
|
|
276
|
+
const slotted = Object.fromEntries([...el.querySelectorAll("[slot]")].map(el => {
|
|
277
|
+
el.parentElement.removeChild(el);
|
|
278
|
+
const slot = el.getAttribute("slot");
|
|
279
|
+
el.removeAttribute("slot");
|
|
280
|
+
return [slot, el];
|
|
281
|
+
}));
|
|
282
|
+
slotted.default = new DocumentFragment();
|
|
283
|
+
slotted.default.append(...el.childNodes);
|
|
284
|
+
return slotted;
|
|
285
|
+
}
|
|
286
|
+
static labelAndInputGroup(id, name, isFloating, slotted) {
|
|
287
|
+
if (isFloating) {
|
|
288
|
+
/**
|
|
289
|
+
* <div class="input-group has-validation">
|
|
290
|
+
* <span data-tpl-if="slotted.before" class="input-group-text">{{{{ slotted.before }}}}</span>
|
|
291
|
+
* <div class="form-floating">
|
|
292
|
+
* {{{{ slotted.input }}}}
|
|
293
|
+
* <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
|
|
294
|
+
* </div>
|
|
295
|
+
* <span data-tpl-if="slotted.after" class="input-group-text">{{{{ slotted.after }}}}</span>
|
|
296
|
+
* <ful-field-error data-tpl-field="name"></ful-field-error>
|
|
297
|
+
* </div>
|
|
298
|
+
*/
|
|
299
|
+
const label = document.createElement("label");
|
|
300
|
+
label.setAttribute("for", id);
|
|
301
|
+
label.classList.add('form-label');
|
|
302
|
+
label.append(slotted.default);
|
|
303
|
+
|
|
304
|
+
const ff = document.createElement('div');
|
|
305
|
+
ff.classList.add("form-floating");
|
|
306
|
+
ff.append(slotted.input, label);
|
|
307
|
+
|
|
308
|
+
const ffe = document.createElement('ful-field-error');
|
|
309
|
+
ffe.setAttribute("field", name);
|
|
310
|
+
|
|
311
|
+
const ig = document.createElement("div");
|
|
312
|
+
ig.classList.add('input-group', 'has-validtion');
|
|
313
|
+
|
|
314
|
+
if (slotted.before) {
|
|
315
|
+
ig.append(slotted.before);
|
|
316
|
+
} else if (slotted.ibefore) {
|
|
317
|
+
const igt = document.createElement('div');
|
|
318
|
+
igt.classList.add('input-group-text');
|
|
319
|
+
igt.append(slotted.ibefore);
|
|
320
|
+
ig.append(igt);
|
|
321
|
+
}
|
|
322
|
+
ig.append(ff);
|
|
323
|
+
if (slotted.after) {
|
|
324
|
+
ig.append(slotted.after);
|
|
325
|
+
} else if (slotted.iafter) {
|
|
326
|
+
const igt = document.createElement('div');
|
|
327
|
+
igt.classList.add('input-group-text');
|
|
328
|
+
igt.append(slotted.iafter);
|
|
329
|
+
ig.append(igt);
|
|
330
|
+
}
|
|
331
|
+
ig.append(ffe);
|
|
332
|
+
return ig;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
<label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
|
|
336
|
+
<div class="input-group has-validation">
|
|
337
|
+
<span data-tpl-if="slotted.before" class="input-group-text">{{{{ slotted.before }}}}</span>
|
|
338
|
+
{{{{ slotted.input }}}}
|
|
339
|
+
<span data-tpl-if="slotted.after" class="input-group-text">{{{{ slotted.after }}}}</span>
|
|
340
|
+
<ful-field-error data-tpl-field="name"></ful-field-error>
|
|
341
|
+
</div>
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
const label = document.createElement("label");
|
|
345
|
+
label.setAttribute("for", name);
|
|
346
|
+
label.classList.add('form-label');
|
|
347
|
+
label.append(slotted.default);
|
|
348
|
+
|
|
349
|
+
const ffe = document.createElement('ful-field-error');
|
|
350
|
+
ffe.setAttribute("field", name);
|
|
351
|
+
|
|
352
|
+
const ig = document.createElement("div");
|
|
353
|
+
ig.classList.add('input-group', 'has-validation');
|
|
354
|
+
|
|
355
|
+
if (slotted.before) {
|
|
356
|
+
ig.append(slotted.before);
|
|
357
|
+
} else if (slotted.ibefore) {
|
|
358
|
+
const igt = document.createElement('div');
|
|
359
|
+
igt.classList.add('input-group-text');
|
|
360
|
+
igt.append(slotted.ibefore);
|
|
361
|
+
ig.append(igt);
|
|
362
|
+
}
|
|
363
|
+
ig.append(slotted.input);
|
|
364
|
+
if (slotted.after) {
|
|
365
|
+
ig.append(slotted.after);
|
|
366
|
+
} else if (slotted.iafter) {
|
|
367
|
+
const igt = document.createElement('div');
|
|
368
|
+
igt.classList.add('input-group-text');
|
|
369
|
+
igt.append(slotted.iafter);
|
|
370
|
+
ig.append(igt);
|
|
371
|
+
}
|
|
372
|
+
ig.append(ffe);
|
|
373
|
+
|
|
374
|
+
const fragment = new DocumentFragment();
|
|
375
|
+
fragment.append(label, ig);
|
|
376
|
+
return fragment;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class FieldError extends HTMLElement {
|
|
383
|
+
constructor() {
|
|
384
|
+
super();
|
|
385
|
+
}
|
|
386
|
+
connectedCallback() {
|
|
387
|
+
this.classList.add('invalid-feedback');
|
|
388
|
+
}
|
|
389
|
+
static configure() {
|
|
390
|
+
customElements.define('ful-field-error', FieldError);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
class Errors extends HTMLElement {
|
|
395
|
+
constructor() {
|
|
396
|
+
super();
|
|
397
|
+
}
|
|
398
|
+
connectedCallback() {
|
|
399
|
+
this.classList.add('alert', 'alert-danger', 'd-none');
|
|
400
|
+
}
|
|
401
|
+
static configure() {
|
|
402
|
+
customElements.define('ful-errors', Errors);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
class Spinner extends HTMLElement {
|
|
408
|
+
constructor() {
|
|
409
|
+
super();
|
|
410
|
+
}
|
|
411
|
+
connectedCallback() {
|
|
412
|
+
this.classList.add('spinner-border', 'spinner-border-sm', 'd-none');
|
|
413
|
+
this.setAttribute("aria-hidden", "true");
|
|
414
|
+
}
|
|
415
|
+
show() {
|
|
416
|
+
this.classList.remove("d-none");
|
|
417
|
+
}
|
|
418
|
+
hide() {
|
|
419
|
+
this.classList.add("d-none");
|
|
420
|
+
}
|
|
421
|
+
static configure() {
|
|
422
|
+
customElements.define('ful-spinner', Spinner);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
class Input extends HTMLElement {
|
|
429
|
+
constructor() {
|
|
430
|
+
super();
|
|
431
|
+
const id = CustomElements.uid('ful-input');
|
|
432
|
+
const name = this.getAttribute('@name');
|
|
433
|
+
const floating = this.hasAttribute('@floating');
|
|
434
|
+
const slotted = CustomElements.extractSlots(this);
|
|
435
|
+
slotted.input = slotted.input || (() => {
|
|
436
|
+
const el = document.createElement("input");
|
|
437
|
+
el.classList.add("form-control");
|
|
438
|
+
return el;
|
|
439
|
+
})();
|
|
440
|
+
CustomElements.forwardAttributes(this, slotted.input, ['@floating']);
|
|
441
|
+
const attrIfMissing = (el, k, v) => !el.hasAttribute(k) && el.setAttribute(k, v);
|
|
442
|
+
attrIfMissing(slotted.input, "name", id);
|
|
443
|
+
attrIfMissing(slotted.input, "id", id);
|
|
444
|
+
attrIfMissing(slotted.input, "type", "text");
|
|
445
|
+
attrIfMissing(slotted.input, "placeholder", " ");
|
|
446
|
+
this.innerHTML = '';
|
|
447
|
+
this.append(CustomElements.labelAndInputGroup(id, name || id, floating, slotted));
|
|
448
|
+
}
|
|
449
|
+
static configure() {
|
|
450
|
+
customElements.define('ful-input', Input);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* <script src="tom-select.complete.js"></script>
|
|
458
|
+
* <link href="tom-select.bootstrap5.css" rel="stylesheet" />
|
|
459
|
+
*/
|
|
460
|
+
class Select extends HTMLElement {
|
|
461
|
+
constructor(tsConfig) {
|
|
462
|
+
super();
|
|
463
|
+
Observable.mixin(this);
|
|
464
|
+
const id = CustomElements.uid('ful-select');
|
|
465
|
+
const name = this.getAttribute('@name');
|
|
466
|
+
const floating = this.hasAttribute('@floating');
|
|
467
|
+
const remote = this.hasAttribute('@remote');
|
|
468
|
+
const slotted = CustomElements.extractSlots(this);
|
|
469
|
+
slotted.input = slotted.input || (() => {
|
|
470
|
+
return document.createElement("select");
|
|
471
|
+
})();
|
|
472
|
+
CustomElements.forwardAttributes(this, slotted.input, ['@floating', '@remote']);
|
|
473
|
+
const attrIfMissing = (el, k, v) => !el.hasAttribute(k) && el.setAttribute(k, v);
|
|
474
|
+
attrIfMissing(slotted.input, "name", id);
|
|
475
|
+
attrIfMissing(slotted.input, "id", id);
|
|
476
|
+
attrIfMissing(slotted.input, "placeholder", " ");
|
|
477
|
+
this.innerHTML = '';
|
|
478
|
+
this.append(CustomElements.labelAndInputGroup(id, name || id, floating, slotted));
|
|
479
|
+
this.loaded = !remote;
|
|
480
|
+
this.ts = new TomSelect(slotted.input, Object.assign(remote ? {
|
|
481
|
+
preload: 'focus',
|
|
482
|
+
load: async (query, callback) => {
|
|
483
|
+
if (this.loaded) {
|
|
484
|
+
callback();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const data = await this.fire('load', query, []);
|
|
488
|
+
this.loaded = true;
|
|
489
|
+
callback(data);
|
|
490
|
+
}
|
|
491
|
+
} : {}, tsConfig));
|
|
492
|
+
slotted.input.setValue = this.setValue.bind(this);
|
|
493
|
+
slotted.input.getValue = this.getValue.bind(this);
|
|
494
|
+
}
|
|
495
|
+
async setValue(v){
|
|
496
|
+
if(!this.loaded){
|
|
497
|
+
await this.ts.load();
|
|
498
|
+
}
|
|
499
|
+
this.ts.setValue(v);
|
|
500
|
+
}
|
|
501
|
+
getValue(){
|
|
502
|
+
const v = this.ts.getValue();
|
|
503
|
+
return v === '' ? null : v;
|
|
504
|
+
}
|
|
505
|
+
static custom(tagName, configuration) {
|
|
506
|
+
customElements.define(tagName, class extends Select {
|
|
507
|
+
constructor() {
|
|
508
|
+
super(configuration);
|
|
379
509
|
}
|
|
380
510
|
});
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
511
|
+
}
|
|
512
|
+
static configure() {
|
|
513
|
+
return Select.custom('ful-select');
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
class Form extends HTMLElement {
|
|
519
|
+
constructor({ mutators, extractors, valueHoldersSelector, ignoredChildrenSelector }) {
|
|
520
|
+
super();
|
|
521
|
+
Observable.mixin(this);
|
|
522
|
+
this.mutators = mutators || {};
|
|
523
|
+
this.extractors = extractors || {};
|
|
524
|
+
this.valueHoldersSelector = valueHoldersSelector || '[name]';
|
|
525
|
+
this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none';
|
|
526
|
+
|
|
527
|
+
const form = document.createElement('form');
|
|
528
|
+
form.append(...this.childNodes);
|
|
529
|
+
this.appendChild(form);
|
|
530
|
+
|
|
531
|
+
form.addEventListener('submit', async (e) => {
|
|
532
|
+
e.preventDefault();
|
|
533
|
+
this.spinner(true);
|
|
534
|
+
try {
|
|
535
|
+
await this.fire('submit', this.getValues(), this);
|
|
536
|
+
} catch (e) {
|
|
537
|
+
if (e instanceof Failure) {
|
|
538
|
+
this.setErrors(e.problems);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
throw e;
|
|
542
|
+
} finally {
|
|
543
|
+
this.spinner(false);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
spinner(spin) {
|
|
548
|
+
this.querySelectorAll('ful-spinner').forEach(el => {
|
|
549
|
+
el[spin ? 'show' : 'hide']();
|
|
550
|
+
});
|
|
551
|
+
this.querySelectorAll('[type=submit],[type=reset]').forEach(el => {
|
|
552
|
+
el.disabled = spin;
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
setValues(values) {
|
|
556
|
+
for (let k in values) {
|
|
557
|
+
if (!values.hasOwnProperty(k)) {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
Array.from(this.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
|
|
561
|
+
Form.mutate(this.mutators, el, values[k], k, values);
|
|
393
562
|
});
|
|
394
563
|
}
|
|
395
564
|
}
|
|
565
|
+
getValues() {
|
|
566
|
+
return Array.from(this.querySelectorAll(this.valueHoldersSelector))
|
|
567
|
+
.filter((el) => {
|
|
568
|
+
if (el.dataset['fulBindInclude'] === 'never') {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
return el.dataset['fulBindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
|
|
572
|
+
})
|
|
573
|
+
.reduce((result, el) => {
|
|
574
|
+
return Form.providePath(result, el.getAttribute('name'), Form.extract(this.extractors, el));
|
|
575
|
+
}, {});
|
|
576
|
+
}
|
|
577
|
+
setErrors(errors, scroll) {
|
|
578
|
+
this.clearErrors();
|
|
579
|
+
errors
|
|
580
|
+
.filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
|
|
581
|
+
.forEach((e) => {
|
|
582
|
+
const name = e.context.replace("[", ".").replace("].", ".");
|
|
583
|
+
this.querySelectorAll(`[name='${CSS.escape(name)}']`)
|
|
584
|
+
.forEach(input => {
|
|
585
|
+
input.classList.add('is-invalid');
|
|
586
|
+
if (input.parentElement.classList.contains("form-floating")) {
|
|
587
|
+
input.parentElement.classList.add('is-invalid');
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
this.querySelectorAll(`ful-field-error[field='${CSS.escape(name)}']`)
|
|
591
|
+
.forEach(el => el.innerText = e.reason);
|
|
592
|
+
});
|
|
593
|
+
this.querySelectorAll("ful-errors")
|
|
594
|
+
.forEach(el => {
|
|
595
|
+
const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
|
|
596
|
+
el.innerHTML = globalErrors.map(e => e.reason).join("\n");
|
|
597
|
+
if (globalErrors.length !== 0) {
|
|
598
|
+
el.classList.remove('d-none');
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
if (!scroll) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const ys = Array.from(this.querySelectorAll('ful-field-error:not(.d-none)'))
|
|
606
|
+
.map(el => el.getBoundingClientRect().y + window.scrollY);
|
|
607
|
+
const miny = Math.min(...ys);
|
|
608
|
+
if (miny !== Infinity) {
|
|
609
|
+
window.scroll(window.scrollX, miny > 100 ? miny - 100 : 0);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
clearErrors() {
|
|
613
|
+
this.querySelectorAll('[name].is-invalid, .form-floating.is-invalid')
|
|
614
|
+
.forEach(el => el.classList.remove('is-invalid'));
|
|
615
|
+
this.querySelectorAll("ful-errors")
|
|
616
|
+
.forEach(el => {
|
|
617
|
+
el.innerHTML = '';
|
|
618
|
+
el.classList.add('d-none');
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
static extract(extractors, el) {
|
|
622
|
+
const maybeExtractor = extractors[el.dataset['fulBindExtractor']] || extractors[el.dataset['fulBindProvide']];
|
|
623
|
+
if (maybeExtractor) {
|
|
624
|
+
return maybeExtractor(el);
|
|
625
|
+
}
|
|
626
|
+
if (el.getAttribute('type') === 'radio') {
|
|
627
|
+
if (!el.checked) {
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
return el.dataset['fulBindType'] === 'boolean' ? el.value === 'true' : el.value;
|
|
631
|
+
}
|
|
632
|
+
if (el.getAttribute('type') === 'checkbox') {
|
|
633
|
+
return el.checked;
|
|
634
|
+
}
|
|
635
|
+
if (el.dataset['fulBindType'] === 'boolean') {
|
|
636
|
+
return !el.value ? null : el.value === 'true';
|
|
637
|
+
}
|
|
638
|
+
if (el.getValue) {
|
|
639
|
+
return el.getValue();
|
|
640
|
+
}
|
|
641
|
+
return el.value || null;
|
|
642
|
+
}
|
|
643
|
+
static mutate(mutators, el, raw, key, values) {
|
|
644
|
+
const maybeMutator = mutators[el.dataset['fulBindMutator']] || mutators[el.dataset['fulBindProvide']];
|
|
645
|
+
if (maybeMutator) {
|
|
646
|
+
maybeMutator(el, raw, key, values);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (el.getAttribute('type') === 'radio') {
|
|
650
|
+
el.checked = el.getAttribute('value') === raw;
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (el.getAttribute('type') === 'checkbox') {
|
|
654
|
+
el.checked = raw;
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (el.setValue) {
|
|
658
|
+
el.setValue(raw);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
el.value = raw;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
static providePath(result, path, value) {
|
|
665
|
+
const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
|
|
666
|
+
let current = result;
|
|
667
|
+
let previous = null;
|
|
668
|
+
for (let i = 0; ; ++i) {
|
|
669
|
+
const ckey = keys[i];
|
|
670
|
+
const pkey = keys[i - 1];
|
|
671
|
+
if (Number.isInteger(ckey) && !Array.isArray(current)) {
|
|
672
|
+
if (previous !== null) {
|
|
673
|
+
previous[pkey] = current = [];
|
|
674
|
+
} else {
|
|
675
|
+
result = current = [];
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
if (i === keys.length - 1) {
|
|
679
|
+
//when value is undefined we only want to define the property if it's not defined
|
|
680
|
+
current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
if (current[ckey] === undefined) {
|
|
684
|
+
current[ckey] = {};
|
|
685
|
+
}
|
|
686
|
+
previous = current;
|
|
687
|
+
current = current[ckey];
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
static custom(tagName, configuration) {
|
|
691
|
+
customElements.define(tagName, class extends Form {
|
|
692
|
+
constructor() {
|
|
693
|
+
super(configuration);
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
static configure(configuration) {
|
|
698
|
+
FieldError.configure();
|
|
699
|
+
Errors.configure();
|
|
700
|
+
Spinner.configure();
|
|
701
|
+
Input.configure();
|
|
702
|
+
Select.configure();
|
|
703
|
+
Form.custom('ful-form', configuration || {});
|
|
704
|
+
}
|
|
396
705
|
}
|
|
397
706
|
|
|
398
707
|
class Storage {
|
|
@@ -457,22 +766,22 @@ var ful = (function (exports) {
|
|
|
457
766
|
|
|
458
767
|
class AuthorizationCodeFlow {
|
|
459
768
|
static forKeycloak(clientId, realmBaseUrl, redirectUri){
|
|
460
|
-
const authUri = new URL("protocol/openid-connect/auth", realmBaseUrl);
|
|
461
|
-
const tokenUri = new URL("protocol/openid-connect/token", realmBaseUrl);
|
|
462
|
-
const logoutUri = new URL("protocol/openid-connect/logout", realmBaseUrl);
|
|
463
769
|
const scope = "openid profile";
|
|
464
|
-
return new AuthorizationCodeFlow(clientId, scope,
|
|
465
|
-
|
|
466
|
-
|
|
770
|
+
return new AuthorizationCodeFlow(clientId, scope, {
|
|
771
|
+
auth: new URL("protocol/openid-connect/auth", realmBaseUrl),
|
|
772
|
+
token: new URL("protocol/openid-connect/token", realmBaseUrl),
|
|
773
|
+
logout: new URL("protocol/openid-connect/logout", realmBaseUrl),
|
|
774
|
+
registration: new URL("protocol/openid-connect/registrations", realmBaseUrl),
|
|
775
|
+
redirect: redirectUri
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
constructor(clientId, scope, {auth, token, registration, logout, redirect}) {
|
|
779
|
+
this.storage = new SessionStorage(clientId);
|
|
467
780
|
this.clientId = clientId;
|
|
468
781
|
this.scope = scope;
|
|
469
|
-
this.
|
|
470
|
-
this.tokenUri = tokenUri;
|
|
471
|
-
this.logoutUri = logoutUri;
|
|
472
|
-
this.redirectUri = redirectUri;
|
|
473
|
-
this.storage = new SessionStorage(clientId);
|
|
782
|
+
this.uri = {auth, token, registration, logout, redirect};
|
|
474
783
|
}
|
|
475
|
-
async
|
|
784
|
+
async action(uri, additionalParams){
|
|
476
785
|
const pkceVerifier = Base64.encode(crypto.getRandomValues(new Uint8Array(32)).buffer);
|
|
477
786
|
const pkceChallenge = Base64.encode(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(pkceVerifier)));
|
|
478
787
|
const state = this.clientId + Base64.encode(crypto.getRandomValues(new Uint8Array(16)).buffer);
|
|
@@ -480,23 +789,34 @@ var ful = (function (exports) {
|
|
|
480
789
|
state: state,
|
|
481
790
|
verifier: pkceVerifier
|
|
482
791
|
});
|
|
483
|
-
const url = new URL(
|
|
792
|
+
const url = new URL(uri);
|
|
484
793
|
url.searchParams.set("client_id", this.clientId);
|
|
485
|
-
url.searchParams.set("redirect_uri", this.
|
|
794
|
+
url.searchParams.set("redirect_uri", this.uri.redirect);
|
|
486
795
|
url.searchParams.set("response_type", 'code');
|
|
487
796
|
url.searchParams.set("scope", this.scope);
|
|
488
797
|
url.searchParams.set("state", state);
|
|
489
798
|
url.searchParams.set("code_challenge", pkceChallenge);
|
|
490
799
|
url.searchParams.set("code_challenge_method", 'S256');
|
|
800
|
+
Object.entries(additionalParams || {}).forEach(kv => {
|
|
801
|
+
url.searchParams.set(kv[0], kv[1]);
|
|
802
|
+
});
|
|
491
803
|
window.location = url;
|
|
492
804
|
}
|
|
805
|
+
async registration(additionalParams){
|
|
806
|
+
await this.action(this.uri.registration, additionalParams);
|
|
807
|
+
}
|
|
808
|
+
async applicationInitiatedAction(kcAction){
|
|
809
|
+
await this.action(this.uri.auth, {
|
|
810
|
+
kc_action: kcAction
|
|
811
|
+
});
|
|
812
|
+
}
|
|
493
813
|
async _tokenExchange(code, state) {
|
|
494
|
-
window.history.replaceState('', "", this.
|
|
814
|
+
window.history.replaceState('', "", this.uri.redirect);
|
|
495
815
|
const stateAndVerifier = this.storage.pop(AuthorizationCodeFlow.PKCE_AND_STATE_KEY);
|
|
496
816
|
if (stateAndVerifier.state !== state) {
|
|
497
817
|
throw new Error("State mismatch");
|
|
498
818
|
}
|
|
499
|
-
const response = await fetch(this.
|
|
819
|
+
const response = await fetch(this.uri.token, {
|
|
500
820
|
method: "POST",
|
|
501
821
|
headers: {
|
|
502
822
|
"Content-Type": 'application/x-www-form-urlencoded'
|
|
@@ -507,7 +827,7 @@ var ful = (function (exports) {
|
|
|
507
827
|
["grant_type", "authorization_code"],
|
|
508
828
|
["code_verifier", stateAndVerifier.verifier],
|
|
509
829
|
["state", stateAndVerifier.state],
|
|
510
|
-
["redirect_uri", this.
|
|
830
|
+
["redirect_uri", this.uri.redirect]
|
|
511
831
|
])
|
|
512
832
|
});
|
|
513
833
|
if (!response.ok) {
|
|
@@ -515,7 +835,7 @@ var ful = (function (exports) {
|
|
|
515
835
|
throw new Error("Error:" + response.status + ": " + text);
|
|
516
836
|
}
|
|
517
837
|
const token = await response.json();
|
|
518
|
-
return new AuthorizationCodeFlowSession(this.clientId, token, this.
|
|
838
|
+
return new AuthorizationCodeFlowSession(this.clientId, token, this.uri);
|
|
519
839
|
}
|
|
520
840
|
async ensureLoggedIn() {
|
|
521
841
|
const url = new URL(window.location.href);
|
|
@@ -526,7 +846,7 @@ var ful = (function (exports) {
|
|
|
526
846
|
return await this._tokenExchange(code, state);
|
|
527
847
|
}
|
|
528
848
|
//if not authorized
|
|
529
|
-
await this.
|
|
849
|
+
await this.action(this.uri.auth, {});
|
|
530
850
|
return null;
|
|
531
851
|
}
|
|
532
852
|
}
|
|
@@ -535,27 +855,26 @@ var ful = (function (exports) {
|
|
|
535
855
|
class AuthorizationCodeFlowSession {
|
|
536
856
|
static parseToken(token) {
|
|
537
857
|
const [rawHeader, rawPayload, signature] = token.split(".");
|
|
858
|
+
const ut8decoder = new TextDecoder("utf-8");
|
|
538
859
|
return {
|
|
539
|
-
header: JSON.parse(
|
|
540
|
-
payload: JSON.parse(
|
|
860
|
+
header: JSON.parse(ut8decoder.decode(Base64.decode(rawHeader, Base64.STANDARD))),
|
|
861
|
+
payload: JSON.parse(ut8decoder.decode(Base64.decode(rawPayload, Base64.STANDARD))),
|
|
541
862
|
signature: signature
|
|
542
863
|
};
|
|
543
864
|
}
|
|
544
|
-
constructor(clientId,
|
|
865
|
+
constructor(clientId, t, {token, logout, redirect}) {
|
|
545
866
|
this.clientId = clientId;
|
|
546
|
-
this.token =
|
|
547
|
-
this.
|
|
548
|
-
this.
|
|
549
|
-
this.
|
|
550
|
-
this.accessToken = AuthorizationCodeFlowSession.parseToken(token.access_token);
|
|
551
|
-
this.refreshToken = AuthorizationCodeFlowSession.parseToken(token.refresh_token);
|
|
867
|
+
this.token = t;
|
|
868
|
+
this.accessToken = AuthorizationCodeFlowSession.parseToken(t.access_token);
|
|
869
|
+
this.refreshToken = AuthorizationCodeFlowSession.parseToken(t.refresh_token);
|
|
870
|
+
this.uri = { token, logout, redirect };
|
|
552
871
|
this.refreshCallback = null;
|
|
553
872
|
}
|
|
554
873
|
onRefresh(callback) {
|
|
555
874
|
this.refreshCallback = callback;
|
|
556
875
|
}
|
|
557
876
|
async refresh() {
|
|
558
|
-
const response = await fetch(this.
|
|
877
|
+
const response = await fetch(this.uri.token, {
|
|
559
878
|
method: "POST",
|
|
560
879
|
headers: {
|
|
561
880
|
"Content-Type": 'application/x-www-form-urlencoded'
|
|
@@ -567,7 +886,7 @@ var ful = (function (exports) {
|
|
|
567
886
|
])
|
|
568
887
|
});
|
|
569
888
|
if (!response.ok) {
|
|
570
|
-
throw new Error("Error:" + response.
|
|
889
|
+
throw new Error("Error:" + response.status + ": " + response.text());
|
|
571
890
|
}
|
|
572
891
|
const token = await response.json();
|
|
573
892
|
this.token = token;
|
|
@@ -591,8 +910,8 @@ var ful = (function (exports) {
|
|
|
591
910
|
await this.refresh();
|
|
592
911
|
}
|
|
593
912
|
logout() {
|
|
594
|
-
const url = new URL(this.
|
|
595
|
-
url.searchParams.set("post_logout_redirect_uri", this.
|
|
913
|
+
const url = new URL(this.uri.logout);
|
|
914
|
+
url.searchParams.set("post_logout_redirect_uri", this.uri.redirect);
|
|
596
915
|
url.searchParams.set("id_token_hint", this.token.id_token);
|
|
597
916
|
window.location = url;
|
|
598
917
|
}
|
|
@@ -612,13 +931,12 @@ var ful = (function (exports) {
|
|
|
612
931
|
this.gracePeriodBefore = gracePeriodBefore || 2000;
|
|
613
932
|
this.gracePeriodAfter = gracePeriodAfter || 30000;
|
|
614
933
|
}
|
|
615
|
-
async
|
|
934
|
+
async intercept(request, chain) {
|
|
616
935
|
await this.session.refreshIf(this.gracePeriodBefore);
|
|
617
936
|
const headers = new Headers(request.options.headers);
|
|
618
937
|
headers.set("Authorization", this.session.bearerToken());
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
async after(request, response) {
|
|
938
|
+
request.options.headers = headers;
|
|
939
|
+
const response = await chain.proceed(request);
|
|
622
940
|
await this.session.refreshIf(this.gracePeriodAfter);
|
|
623
941
|
return response;
|
|
624
942
|
}
|
|
@@ -706,10 +1024,10 @@ var ful = (function (exports) {
|
|
|
706
1024
|
}
|
|
707
1025
|
};
|
|
708
1026
|
|
|
709
|
-
class Wizard {
|
|
710
|
-
constructor(
|
|
711
|
-
|
|
712
|
-
this.progress = [...
|
|
1027
|
+
class Wizard extends HTMLElement {
|
|
1028
|
+
constructor() {
|
|
1029
|
+
super();
|
|
1030
|
+
this.progress = [...this.children].filter(e => e.matches("header,ol,ul"));
|
|
713
1031
|
|
|
714
1032
|
this.progress.forEach(p => {
|
|
715
1033
|
const children = [...p.children];
|
|
@@ -718,8 +1036,8 @@ var ful = (function (exports) {
|
|
|
718
1036
|
children[0].classList.add('active');
|
|
719
1037
|
}
|
|
720
1038
|
});
|
|
721
|
-
if (this.
|
|
722
|
-
const firstSection = this.
|
|
1039
|
+
if (this.querySelector('section.current') === null) {
|
|
1040
|
+
const firstSection = this.querySelector('section:first-of-type');
|
|
723
1041
|
if (firstSection !== null) {
|
|
724
1042
|
firstSection.classList.add('current');
|
|
725
1043
|
}
|
|
@@ -732,11 +1050,11 @@ var ful = (function (exports) {
|
|
|
732
1050
|
current?.classList.remove('active');
|
|
733
1051
|
current?.nextElementSibling?.classList.add('active');
|
|
734
1052
|
});
|
|
735
|
-
const currentSection = this.
|
|
1053
|
+
const currentSection = this.querySelector('section.current');
|
|
736
1054
|
currentSection.classList.remove("current");
|
|
737
1055
|
currentSection.nextElementSibling.classList.add('current');
|
|
738
1056
|
|
|
739
|
-
this.
|
|
1057
|
+
this.dispatchEvent(new CustomEvent('wizard:activate', {
|
|
740
1058
|
bubbles: true,
|
|
741
1059
|
cancelable: true
|
|
742
1060
|
}));
|
|
@@ -749,10 +1067,10 @@ var ful = (function (exports) {
|
|
|
749
1067
|
current?.classList.remove('active');
|
|
750
1068
|
current?.previousElementSibling?.classList.add('active');
|
|
751
1069
|
});
|
|
752
|
-
const currentSection = this.
|
|
1070
|
+
const currentSection = this.querySelector('section.current');
|
|
753
1071
|
currentSection.classList.remove("current");
|
|
754
1072
|
currentSection.previousElementSibling.classList.add('current');
|
|
755
|
-
this.
|
|
1073
|
+
this.dispatchEvent(new CustomEvent('wizard:activate', {
|
|
756
1074
|
bubbles: true,
|
|
757
1075
|
cancelable: true
|
|
758
1076
|
}));
|
|
@@ -764,15 +1082,25 @@ var ful = (function (exports) {
|
|
|
764
1082
|
current?.classList.remove('active');
|
|
765
1083
|
p.children[+n]?.classList.add('active');
|
|
766
1084
|
});
|
|
767
|
-
const currentSection = this.
|
|
1085
|
+
const currentSection = this.querySelector('section.current');
|
|
768
1086
|
currentSection?.classList.remove("current");
|
|
769
|
-
const nthSection = this.
|
|
1087
|
+
const nthSection = this.querySelector(`section:nth-child(${+n})`);
|
|
770
1088
|
nthSection.classList.add('current');
|
|
771
|
-
this.
|
|
1089
|
+
this.dispatchEvent(new CustomEvent('wizard:activate', {
|
|
772
1090
|
bubbles: true,
|
|
773
1091
|
cancelable: true
|
|
774
1092
|
}));
|
|
775
1093
|
}
|
|
1094
|
+
static custom(tagName, configuration) {
|
|
1095
|
+
customElements.define(tagName, class extends Wizard {
|
|
1096
|
+
constructor() {
|
|
1097
|
+
super(configuration);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
static configure() {
|
|
1102
|
+
return Wizard.custom('ful-wizard');
|
|
1103
|
+
}
|
|
776
1104
|
}
|
|
777
1105
|
|
|
778
1106
|
class App {
|
|
@@ -806,13 +1134,19 @@ var ful = (function (exports) {
|
|
|
806
1134
|
exports.AuthorizationCodeFlowInterceptor = AuthorizationCodeFlowInterceptor;
|
|
807
1135
|
exports.AuthorizationCodeFlowSession = AuthorizationCodeFlowSession;
|
|
808
1136
|
exports.Base64 = Base64;
|
|
809
|
-
exports.
|
|
1137
|
+
exports.CustomElements = CustomElements;
|
|
1138
|
+
exports.Errors = Errors;
|
|
810
1139
|
exports.Failure = Failure;
|
|
1140
|
+
exports.FieldError = FieldError;
|
|
811
1141
|
exports.Form = Form;
|
|
812
1142
|
exports.Hex = Hex;
|
|
813
1143
|
exports.HttpClient = HttpClient;
|
|
1144
|
+
exports.Input = Input;
|
|
814
1145
|
exports.LocalStorage = LocalStorage;
|
|
1146
|
+
exports.Observable = Observable;
|
|
1147
|
+
exports.Select = Select;
|
|
815
1148
|
exports.SessionStorage = SessionStorage;
|
|
1149
|
+
exports.Spinner = Spinner;
|
|
816
1150
|
exports.VersionedStorage = VersionedStorage;
|
|
817
1151
|
exports.Wizard = Wizard;
|
|
818
1152
|
exports.timing = timing;
|