@olenbetong/appframe-core 2.0.4 → 2.1.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/dist/esm/af.common.js +97 -197
- package/dist/esm/af.common.legacy.js +98 -191
- package/dist/esm/af.common.legacy.min.js +1 -29
- package/dist/esm/af.common.legacy.min.js.map +4 -4
- package/dist/esm/af.common.min.js +1 -29
- package/dist/esm/af.common.min.js.map +4 -4
- package/dist/iife/af.common.js +97 -195
- package/dist/iife/af.common.legacy.js +98 -191
- package/dist/iife/af.common.legacy.min.js +1 -29
- package/dist/iife/af.common.legacy.min.js.map +4 -4
- package/dist/iife/af.common.min.js +1 -29
- package/dist/iife/af.common.min.js.map +4 -4
- package/es/SessionHandler.d.ts +2 -7
- package/es/SessionHandler.js +34 -135
- package/es/UserSession.d.ts +1 -2
- package/es/UserSession.js +1 -2
- package/es/downloadRequest.js +1 -4
- package/es/expose.d.ts +1 -1
- package/es/expose.js +4 -5
- package/es/index.d.ts +3 -2
- package/es/index.js +4 -3
- package/es/localize.d.ts +14 -0
- package/es/localize.js +25 -0
- package/es/parseFunc.d.ts +1 -1
- package/es/parseFunc.js +1 -1
- package/es/requireNamespace.d.ts +1 -1
- package/es/requireNamespace.js +1 -7
- package/package.json +2 -2
- package/src/SessionHandler.ts +38 -156
- package/src/UserSession.ts +1 -2
- package/src/downloadRequest.ts +1 -4
- package/src/expose.ts +4 -5
- package/src/index.ts +3 -2
- package/src/localize.ts +29 -0
- package/src/parseFunc.ts +1 -1
- package/src/requireNamespace.ts +1 -5
- package/es/warn.d.ts +0 -1
- package/es/warn.js +0 -4
- package/src/warn.ts +0 -4
package/es/SessionHandler.js
CHANGED
|
@@ -1,140 +1,43 @@
|
|
|
1
1
|
import mitt from "mitt";
|
|
2
|
-
import { getLocalizedString } from "./getLocalizedString.js";
|
|
3
2
|
export default class SessionHandler {
|
|
4
3
|
constructor() {
|
|
5
4
|
this.eventHandler = mitt();
|
|
6
|
-
this.showingDialog = false;
|
|
7
|
-
this.dialog = null;
|
|
8
5
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<input type="text" class="form-control" id="username" autofocus>
|
|
33
|
-
</div>
|
|
34
|
-
<div class="form-group">
|
|
35
|
-
<label for="password">${getLocalizedString("Password")}</label>
|
|
36
|
-
<input type="password" autocomplete="password" class="form-control" id="password" >
|
|
37
|
-
</div>
|
|
38
|
-
<div class="form-group">
|
|
39
|
-
<div>
|
|
40
|
-
<button type="submit" class="btn btn-default sign-in">
|
|
41
|
-
${getLocalizedString("Log in")}
|
|
42
|
-
</button>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
<div class="row">
|
|
48
|
-
<div class="" style="display:none; margin: 0px 30px;">
|
|
49
|
-
<div class="alert alert-warning" role="alert" style="margin-top:10px;"></div>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
`;
|
|
54
|
-
const message = af.userSession.login ? SessionHandler.messages.relog : SessionHandler.messages.login;
|
|
55
|
-
const messageElem = contentContainer.querySelector(".message");
|
|
56
|
-
const statusElem = contentContainer.querySelector(".alert-warning");
|
|
57
|
-
const userElem = contentContainer.querySelector("input[type=text]");
|
|
58
|
-
const signInElem = contentContainer.querySelector(".sign-in");
|
|
59
|
-
const passwordElem = contentContainer.querySelector("input[type=password]");
|
|
60
|
-
if (messageElem !== null &&
|
|
61
|
-
userElem !== null &&
|
|
62
|
-
statusElem !== null &&
|
|
63
|
-
userElem !== null &&
|
|
64
|
-
signInElem !== null &&
|
|
65
|
-
passwordElem !== null) {
|
|
66
|
-
messageElem.innerText = getLocalizedString(message);
|
|
67
|
-
userElem.value = af.userSession.login || "";
|
|
68
|
-
userElem.disabled = !!af.userSession.login;
|
|
69
|
-
userElem.readOnly = !!af.userSession.login;
|
|
70
|
-
document.body.appendChild(contentContainer);
|
|
71
|
-
const doLogin = async () => {
|
|
72
|
-
passwordElem.disabled = true;
|
|
73
|
-
const request = new Request("/login", {
|
|
74
|
-
body: JSON.stringify({
|
|
75
|
-
username: userElem.value,
|
|
76
|
-
password: passwordElem.value,
|
|
77
|
-
remember: false
|
|
78
|
-
}),
|
|
79
|
-
headers: {
|
|
80
|
-
Accept: "application/json",
|
|
81
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
82
|
-
"X-Requested-With": "XMLHttpRequest"
|
|
83
|
-
},
|
|
84
|
-
method: "POST"
|
|
85
|
-
});
|
|
86
|
-
try {
|
|
87
|
-
const response = await fetch(request);
|
|
88
|
-
const json = await response.json();
|
|
89
|
-
if (json.success) {
|
|
90
|
-
document.body.removeChild(contentContainer);
|
|
91
|
-
this.showingDialog = false;
|
|
92
|
-
this.eventHandler.emit("onAuthenticated");
|
|
93
|
-
if (statusElem.parentNode) {
|
|
94
|
-
statusElem.parentNode.style.display = "none";
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
throw new Error(json.error);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
if (err && typeof err === "object") {
|
|
103
|
-
let error = err;
|
|
104
|
-
passwordElem.disabled = false;
|
|
105
|
-
passwordElem.classList.toggle("invalid", true);
|
|
106
|
-
passwordElem.value = "";
|
|
107
|
-
passwordElem.focus();
|
|
108
|
-
statusElem.innerText = error.message ? error.message : String(error);
|
|
109
|
-
if (statusElem.parentNode) {
|
|
110
|
-
statusElem.parentNode.style.display = "initial";
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
signInElem.addEventListener("click", doLogin);
|
|
116
|
-
passwordElem.addEventListener("keyup", evt => {
|
|
117
|
-
if (evt.key === "Enter") {
|
|
118
|
-
doLogin();
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
passwordElem.addEventListener("change", evt => {
|
|
122
|
-
if (evt.key === "Enter") {
|
|
123
|
-
doLogin();
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
userElem.addEventListener("keyup", evt => {
|
|
127
|
-
if (evt.key === "Enter") {
|
|
128
|
-
doLogin();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
userElem.addEventListener("change", evt => {
|
|
132
|
-
if (evt.key === "Enter") {
|
|
133
|
-
doLogin();
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
passwordElem.focus();
|
|
6
|
+
authenticated() {
|
|
7
|
+
this.eventHandler.emit("onAuthenticated");
|
|
8
|
+
}
|
|
9
|
+
async expired(callback, type) {
|
|
10
|
+
let authPromise = new Promise((resolve) => {
|
|
11
|
+
let eventHandler = () => {
|
|
12
|
+
this.eventHandler.off("onAuthenticated", eventHandler);
|
|
13
|
+
callback?.();
|
|
14
|
+
resolve();
|
|
15
|
+
};
|
|
16
|
+
this.eventHandler.on("onAuthenticated", eventHandler);
|
|
17
|
+
});
|
|
18
|
+
let dialog = document.querySelector("dialog.session-expired-dialog");
|
|
19
|
+
if (!dialog) {
|
|
20
|
+
dialog = document.createElement("dialog");
|
|
21
|
+
dialog.className = "session-expired-dialog";
|
|
22
|
+
dialog.style.display = "flex";
|
|
23
|
+
dialog.style.padding = "0";
|
|
24
|
+
dialog.style.left = "50%";
|
|
25
|
+
dialog.style.transform = "translateX(-50%)";
|
|
26
|
+
let link = "/login?ReturnUrl=%2fapi/sessionexpired/remove";
|
|
27
|
+
if (type !== "RequireTwoFactor") {
|
|
28
|
+
await fetch("/logout", { method: "POST" });
|
|
137
29
|
}
|
|
30
|
+
else {
|
|
31
|
+
link = "/login?RequireTwoFactor=1&ReturnUrl=%2fapi/sessionexpired/remove";
|
|
32
|
+
}
|
|
33
|
+
dialog.innerHTML = `<iframe sandbox="allow-same-origin allow-scripts allow-modals allow-top-navigation-by-user-activation allow-forms" src="${link}" style="border:0;width:min(500px,100vw);height:min(700px, 100vh);"></iframe>`;
|
|
34
|
+
document.body.append(dialog);
|
|
35
|
+
dialog.showModal();
|
|
36
|
+
await authPromise;
|
|
37
|
+
dialog.remove();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
await authPromise;
|
|
138
41
|
}
|
|
139
42
|
}
|
|
140
43
|
attachEvent(eventName, handler) {
|
|
@@ -144,7 +47,3 @@ export default class SessionHandler {
|
|
|
144
47
|
return this.eventHandler.off(eventName, handler);
|
|
145
48
|
}
|
|
146
49
|
}
|
|
147
|
-
SessionHandler.messages = {
|
|
148
|
-
login: "Log in",
|
|
149
|
-
relog: "Session expired - log in"
|
|
150
|
-
};
|
package/es/UserSession.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import SessionHandler from "./SessionHandler.js";
|
|
2
|
-
export
|
|
2
|
+
export declare class UserSession extends SessionHandler {
|
|
3
3
|
amDesignator: string;
|
|
4
4
|
dayNames: string[];
|
|
5
5
|
dayNamesShort: string[];
|
|
@@ -19,6 +19,5 @@ export default class UserSession extends SessionHandler {
|
|
|
19
19
|
shortDateFormat: string;
|
|
20
20
|
culture: string;
|
|
21
21
|
uiculture: string;
|
|
22
|
-
expired: (callback: () => void) => void;
|
|
23
22
|
constructor(userData: any);
|
|
24
23
|
}
|
package/es/UserSession.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import SessionHandler from "./SessionHandler.js";
|
|
2
|
-
export
|
|
2
|
+
export class UserSession extends SessionHandler {
|
|
3
3
|
constructor(userData) {
|
|
4
4
|
super();
|
|
5
5
|
this.amDesignator = "a.m.";
|
|
@@ -17,7 +17,6 @@ export default class UserSession extends SessionHandler {
|
|
|
17
17
|
this.shortDateFormat = "dd.MM.yyyy";
|
|
18
18
|
this.culture = "en-GB";
|
|
19
19
|
this.uiculture = "en-GB";
|
|
20
|
-
this.expired = () => null;
|
|
21
20
|
this.formats = {};
|
|
22
21
|
Object.assign(this, userData);
|
|
23
22
|
}
|
package/es/downloadRequest.js
CHANGED
|
@@ -39,10 +39,7 @@ export async function downloadRequest(request, { fileName, signal, onProgress })
|
|
|
39
39
|
});
|
|
40
40
|
response = new Response(stream);
|
|
41
41
|
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
// eslint-disable-next-line no-console
|
|
44
|
-
console.warn("Failed to replace response stream to log download progress.");
|
|
45
|
-
}
|
|
42
|
+
catch (error) { }
|
|
46
43
|
const blob = await response.blob();
|
|
47
44
|
reportProgress({ bytesReceived, status: "downloaded" });
|
|
48
45
|
await downloadBlob(blob, fileName);
|
package/es/expose.d.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
4
4
|
* @param value {object | Function | Function[]} The value(s) to expose
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
6
|
+
export declare function expose(name: object | string, value: object | Function | Function[]): void;
|
package/es/expose.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { has } from "./has.js";
|
|
2
|
-
import parseFunc from "./parseFunc.js";
|
|
3
|
-
import requireNamespace from "./requireNamespace.js";
|
|
4
|
-
import warn from "./warn.js";
|
|
2
|
+
import { parseFunc } from "./parseFunc.js";
|
|
3
|
+
import { requireNamespace } from "./requireNamespace.js";
|
|
5
4
|
/**
|
|
6
5
|
* Expose a value to a potentially deeply nested global variable
|
|
7
6
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
8
7
|
* @param value {object | Function | Function[]} The value(s) to expose
|
|
9
8
|
*/
|
|
10
|
-
export
|
|
9
|
+
export function expose(name, value) {
|
|
11
10
|
if (arguments.length !== 2) {
|
|
12
11
|
throw new Error("Incorrect number of arguments passed to the expose function!");
|
|
13
12
|
}
|
|
@@ -44,7 +43,7 @@ export default function expose(name, value) {
|
|
|
44
43
|
}
|
|
45
44
|
if (has(namespace, keyName)) {
|
|
46
45
|
// If class is already defined, warn or show an alert
|
|
47
|
-
warn([name, keyName].join(".") + " is already defined!");
|
|
46
|
+
console.warn([name, keyName].join(".") + " is already defined!");
|
|
48
47
|
}
|
|
49
48
|
else {
|
|
50
49
|
namespace[keyName] = value;
|
package/es/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import "./errorHandler.js";
|
|
2
|
-
import UserSession from "./UserSession.js";
|
|
2
|
+
import { UserSession } from "./UserSession.js";
|
|
3
3
|
export { default as mitt } from "mitt";
|
|
4
4
|
export { downloadBlob } from "./downloadBlob.js";
|
|
5
5
|
export * from "./downloadRequest.js";
|
|
6
|
-
export {
|
|
6
|
+
export { expose } from "./expose.js";
|
|
7
7
|
export { getLocalizedString } from "./getLocalizedString.js";
|
|
8
|
+
export { localize } from "./localize.js";
|
|
8
9
|
export * from "./has.js";
|
|
9
10
|
export { default as localStorage } from "./localStorage.js";
|
|
10
11
|
export { default as SessionHandler } from "./SessionHandler.js";
|
package/es/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import "./errorHandler.js";
|
|
2
|
-
import UserSession from "./UserSession.js";
|
|
2
|
+
import { UserSession } from "./UserSession.js";
|
|
3
3
|
export { default as mitt } from "mitt";
|
|
4
4
|
export { downloadBlob } from "./downloadBlob.js";
|
|
5
5
|
export * from "./downloadRequest.js";
|
|
6
|
-
export {
|
|
6
|
+
export { expose } from "./expose.js";
|
|
7
7
|
export { getLocalizedString } from "./getLocalizedString.js";
|
|
8
|
+
export { localize } from "./localize.js";
|
|
8
9
|
export * from "./has.js";
|
|
9
10
|
export { default as localStorage } from "./localStorage.js";
|
|
10
11
|
export { default as SessionHandler } from "./SessionHandler.js";
|
|
11
12
|
export { UserSession };
|
|
12
13
|
export * from "./settings.js";
|
|
13
|
-
export const version = "2.0
|
|
14
|
+
export const version = "2.1.0";
|
package/es/localize.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Localization function for tagged templates. Replaces the arguments
|
|
3
|
+
* with $n, where n is the index, before asking Appframe to translate
|
|
4
|
+
* the string.
|
|
5
|
+
*
|
|
6
|
+
* For example, if you call localize`Page ${2} or ${5}`, the string
|
|
7
|
+
* "Page $0 of $1" will be sent to Appframe, and for norwegian,
|
|
8
|
+
* "Side 2 av 5" will be returned.
|
|
9
|
+
*
|
|
10
|
+
* @param stringParts
|
|
11
|
+
* @param args
|
|
12
|
+
* @returns Translated string
|
|
13
|
+
*/
|
|
14
|
+
export declare function localize(stringParts: TemplateStringsArray, ...args: any[]): string;
|
package/es/localize.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getLocalizedString } from "./getLocalizedString.js";
|
|
2
|
+
/**
|
|
3
|
+
* Localization function for tagged templates. Replaces the arguments
|
|
4
|
+
* with $n, where n is the index, before asking Appframe to translate
|
|
5
|
+
* the string.
|
|
6
|
+
*
|
|
7
|
+
* For example, if you call localize`Page ${2} or ${5}`, the string
|
|
8
|
+
* "Page $0 of $1" will be sent to Appframe, and for norwegian,
|
|
9
|
+
* "Side 2 av 5" will be returned.
|
|
10
|
+
*
|
|
11
|
+
* @param stringParts
|
|
12
|
+
* @param args
|
|
13
|
+
* @returns Translated string
|
|
14
|
+
*/
|
|
15
|
+
export function localize(stringParts, ...args) {
|
|
16
|
+
if (!Array.isArray(stringParts) || stringParts?.length || stringParts.length !== args.length + 1) {
|
|
17
|
+
throw TypeError("Method 'localize' must be used as a tag function");
|
|
18
|
+
}
|
|
19
|
+
let stringToLocalize = stringParts.map((s, i) => (i === stringParts.length - 1 ? s : `${s}\$${i}`)).join("");
|
|
20
|
+
let localized = getLocalizedString(stringToLocalize);
|
|
21
|
+
args.forEach((arg, index) => {
|
|
22
|
+
localized = localized.replace("$" + index, arg);
|
|
23
|
+
});
|
|
24
|
+
return localized;
|
|
25
|
+
}
|
package/es/parseFunc.d.ts
CHANGED
package/es/parseFunc.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const funcRegex = /^function(?:\s+([a-zA-Z$_][a-zA-Z0-9$_]*))?\s*\(((?:(?:\s*[a-zA-Z$_][a-zA-Z0-9$_]*)(?:\s*,\s*[a-zA-Z$_][a-zA-Z0-9$_]*)*)?)\s*\)\s*\{((?:.|\n|\r)*)\}$/;
|
|
2
|
-
export
|
|
2
|
+
export function parseFunc(func) {
|
|
3
3
|
var match = func.toString().match(funcRegex);
|
|
4
4
|
if (match === null) {
|
|
5
5
|
throw new TypeError("Unable to parse function!");
|
package/es/requireNamespace.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare function requireNamespace(root: any, namespace: string | Array<string>): any;
|
package/es/requireNamespace.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Namespace from "./Namespace.js";
|
|
2
|
-
export
|
|
2
|
+
export function requireNamespace(root, namespace) {
|
|
3
3
|
if (typeof root === "object") {
|
|
4
4
|
if (typeof namespace === "string") {
|
|
5
5
|
let path = namespace.split(".");
|
|
@@ -13,11 +13,5 @@ export default function requireNamespace(root, namespace) {
|
|
|
13
13
|
}
|
|
14
14
|
return current;
|
|
15
15
|
}
|
|
16
|
-
else {
|
|
17
|
-
throw new TypeError("Invalid type of parameter 'namespace'");
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
throw new TypeError("Invalid type of parameter 'root'");
|
|
22
16
|
}
|
|
23
17
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olenbetong/appframe-core",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Javascript libraries to utilize the Appframe framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"chalk": "^5.0.1"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "1074886c6a0834bdaa3ca684590c4aadc81581c5"
|
|
52
52
|
}
|
package/src/SessionHandler.ts
CHANGED
|
@@ -1,168 +1,50 @@
|
|
|
1
1
|
import mitt, { Emitter, Handler } from "mitt";
|
|
2
|
-
import { getLocalizedString } from "./getLocalizedString.js";
|
|
3
2
|
|
|
4
3
|
type SessionHandlerEvent = "onExpired" | "onAuthenticated";
|
|
5
4
|
|
|
6
5
|
export default class SessionHandler {
|
|
7
|
-
static messages = {
|
|
8
|
-
login: "Log in",
|
|
9
|
-
relog: "Session expired - log in"
|
|
10
|
-
};
|
|
11
|
-
|
|
12
6
|
private eventHandler: Emitter<{ onAuthenticated: void; onExpired: void }> = mitt();
|
|
13
|
-
private showingDialog = false;
|
|
14
|
-
private dialog = null;
|
|
15
|
-
|
|
16
|
-
expired(callback: Function) {
|
|
17
|
-
af.userSession = af.userSession || {
|
|
18
|
-
login: null
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
if (typeof callback !== "function") {
|
|
22
|
-
throw new TypeError("Parameter pCallback passed to the expired function must be a function!");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const once = () => {
|
|
26
|
-
this.detachEvent("onAuthenticated", once);
|
|
27
|
-
callback();
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
this.attachEvent("onAuthenticated", once);
|
|
31
|
-
|
|
32
|
-
if (this.showingDialog !== true) {
|
|
33
|
-
this.showingDialog = true;
|
|
34
|
-
const contentContainer = document.createElement("div");
|
|
35
|
-
contentContainer.className = "session-expired-container";
|
|
36
|
-
contentContainer.innerHTML = `
|
|
37
|
-
<div class="session-expired-log-in" style="margin-top: 20px;">
|
|
38
|
-
<div class="row">
|
|
39
|
-
<div class="sessionExpired">
|
|
40
|
-
<h4 class="message">Frakoblet - tidsavbrudd</h4>
|
|
41
|
-
<div class="form-group">
|
|
42
|
-
<label for="username">${getLocalizedString("Username")}</label>
|
|
43
|
-
<input type="text" class="form-control" id="username" autofocus>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="form-group">
|
|
46
|
-
<label for="password">${getLocalizedString("Password")}</label>
|
|
47
|
-
<input type="password" autocomplete="password" class="form-control" id="password" >
|
|
48
|
-
</div>
|
|
49
|
-
<div class="form-group">
|
|
50
|
-
<div>
|
|
51
|
-
<button type="submit" class="btn btn-default sign-in">
|
|
52
|
-
${getLocalizedString("Log in")}
|
|
53
|
-
</button>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
<div class="row">
|
|
59
|
-
<div class="" style="display:none; margin: 0px 30px;">
|
|
60
|
-
<div class="alert alert-warning" role="alert" style="margin-top:10px;"></div>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
`;
|
|
65
|
-
const message = af.userSession.login ? SessionHandler.messages.relog : SessionHandler.messages.login;
|
|
66
|
-
|
|
67
|
-
const messageElem = contentContainer.querySelector(".message") as HTMLHeadingElement;
|
|
68
|
-
const statusElem = contentContainer.querySelector(".alert-warning") as HTMLDivElement;
|
|
69
|
-
const userElem = contentContainer.querySelector("input[type=text]") as HTMLInputElement;
|
|
70
|
-
const signInElem = contentContainer.querySelector(".sign-in") as HTMLButtonElement;
|
|
71
|
-
const passwordElem = contentContainer.querySelector("input[type=password]") as HTMLInputElement;
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
messageElem !== null &&
|
|
75
|
-
userElem !== null &&
|
|
76
|
-
statusElem !== null &&
|
|
77
|
-
userElem !== null &&
|
|
78
|
-
signInElem !== null &&
|
|
79
|
-
passwordElem !== null
|
|
80
|
-
) {
|
|
81
|
-
messageElem.innerText = getLocalizedString(message);
|
|
82
|
-
|
|
83
|
-
userElem.value = af.userSession.login || "";
|
|
84
|
-
userElem.disabled = !!af.userSession.login;
|
|
85
|
-
userElem.readOnly = !!af.userSession.login;
|
|
86
|
-
|
|
87
|
-
document.body.appendChild(contentContainer);
|
|
88
|
-
|
|
89
|
-
const doLogin = async () => {
|
|
90
|
-
passwordElem.disabled = true;
|
|
91
7
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
password: passwordElem.value,
|
|
96
|
-
remember: false
|
|
97
|
-
}),
|
|
98
|
-
headers: {
|
|
99
|
-
Accept: "application/json",
|
|
100
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
101
|
-
"X-Requested-With": "XMLHttpRequest"
|
|
102
|
-
},
|
|
103
|
-
method: "POST"
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
const response = await fetch(request);
|
|
108
|
-
const json = await response.json();
|
|
109
|
-
|
|
110
|
-
if (json.success) {
|
|
111
|
-
document.body.removeChild(contentContainer);
|
|
112
|
-
this.showingDialog = false;
|
|
113
|
-
this.eventHandler.emit("onAuthenticated");
|
|
114
|
-
|
|
115
|
-
if (statusElem.parentNode) {
|
|
116
|
-
(statusElem.parentNode as HTMLElement).style.display = "none";
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
throw new Error(json.error);
|
|
120
|
-
}
|
|
121
|
-
} catch (err) {
|
|
122
|
-
if (err && typeof err === "object") {
|
|
123
|
-
let error = err as Error;
|
|
124
|
-
passwordElem.disabled = false;
|
|
125
|
-
passwordElem.classList.toggle("invalid", true);
|
|
126
|
-
passwordElem.value = "";
|
|
127
|
-
passwordElem.focus();
|
|
128
|
-
statusElem.innerText = error.message ? error.message : String(error);
|
|
129
|
-
|
|
130
|
-
if (statusElem.parentNode) {
|
|
131
|
-
(statusElem.parentNode as HTMLElement).style.display = "initial";
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
signInElem.addEventListener("click", doLogin);
|
|
138
|
-
|
|
139
|
-
passwordElem.addEventListener("keyup", evt => {
|
|
140
|
-
if (evt.key === "Enter") {
|
|
141
|
-
doLogin();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
passwordElem.addEventListener("change", evt => {
|
|
146
|
-
if ((evt as KeyboardEvent).key === "Enter") {
|
|
147
|
-
doLogin();
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
userElem.addEventListener("keyup", evt => {
|
|
152
|
-
if (evt.key === "Enter") {
|
|
153
|
-
doLogin();
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
userElem.addEventListener("change", evt => {
|
|
158
|
-
if ((evt as KeyboardEvent).key === "Enter") {
|
|
159
|
-
doLogin();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
8
|
+
authenticated() {
|
|
9
|
+
this.eventHandler.emit("onAuthenticated");
|
|
10
|
+
}
|
|
162
11
|
|
|
163
|
-
|
|
12
|
+
async expired(callback?: (() => void), type?: "RequireTwoFactor"): Promise<void> {
|
|
13
|
+
let authPromise = new Promise<void>((resolve) => {
|
|
14
|
+
let eventHandler = () => {
|
|
15
|
+
this.eventHandler.off("onAuthenticated", eventHandler);
|
|
16
|
+
callback?.();
|
|
17
|
+
resolve();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.eventHandler.on("onAuthenticated", eventHandler);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
let dialog: HTMLDialogElement | null = document.querySelector("dialog.session-expired-dialog");
|
|
24
|
+
if (!dialog) {
|
|
25
|
+
dialog = document.createElement("dialog");
|
|
26
|
+
dialog.className = "session-expired-dialog";
|
|
27
|
+
dialog.style.display = "flex";
|
|
28
|
+
dialog.style.padding = "0";
|
|
29
|
+
dialog.style.left = "50%";
|
|
30
|
+
dialog.style.transform = "translateX(-50%)";
|
|
31
|
+
|
|
32
|
+
let link = "/login?ReturnUrl=%2fapi/sessionexpired/remove";
|
|
33
|
+
if (type !== "RequireTwoFactor") {
|
|
34
|
+
await fetch("/logout", { method: "POST" });
|
|
35
|
+
} else {
|
|
36
|
+
link = "/login?RequireTwoFactor=1&ReturnUrl=%2fapi/sessionexpired/remove";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dialog.innerHTML = `<iframe sandbox="allow-same-origin allow-scripts allow-modals allow-top-navigation-by-user-activation allow-forms" src="${link}" style="border:0;width:min(500px,100vw);height:min(700px, 100vh);"></iframe>`;
|
|
40
|
+
document.body.append(dialog);
|
|
41
|
+
dialog.showModal();
|
|
42
|
+
await authPromise;
|
|
43
|
+
dialog.remove();
|
|
44
|
+
} else {
|
|
45
|
+
await authPromise;
|
|
164
46
|
}
|
|
165
|
-
|
|
47
|
+
|
|
166
48
|
}
|
|
167
49
|
|
|
168
50
|
attachEvent(eventName: SessionHandlerEvent, handler: Handler) {
|
package/src/UserSession.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import SessionHandler from "./SessionHandler.js";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export class UserSession extends SessionHandler {
|
|
4
4
|
amDesignator: string = "a.m.";
|
|
5
5
|
dayNames: string[] = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
|
|
6
6
|
dayNamesShort: string[] = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
@@ -20,7 +20,6 @@ export default class UserSession extends SessionHandler {
|
|
|
20
20
|
shortDateFormat: string = "dd.MM.yyyy";
|
|
21
21
|
culture: string = "en-GB";
|
|
22
22
|
uiculture: string = "en-GB";
|
|
23
|
-
expired: (callback: () => void) => void = () => null;
|
|
24
23
|
|
|
25
24
|
constructor(userData: any) {
|
|
26
25
|
super();
|
package/src/downloadRequest.ts
CHANGED
|
@@ -60,10 +60,7 @@ export async function downloadRequest(request: Request, { fileName, signal, onPr
|
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
response = new Response(stream);
|
|
63
|
-
} catch (error) {
|
|
64
|
-
// eslint-disable-next-line no-console
|
|
65
|
-
console.warn("Failed to replace response stream to log download progress.");
|
|
66
|
-
}
|
|
63
|
+
} catch (error) {}
|
|
67
64
|
|
|
68
65
|
const blob = await response.blob();
|
|
69
66
|
|
package/src/expose.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { has } from "./has.js";
|
|
2
|
-
import parseFunc from "./parseFunc.js";
|
|
3
|
-
import requireNamespace from "./requireNamespace.js";
|
|
4
|
-
import warn from "./warn.js";
|
|
2
|
+
import { parseFunc } from "./parseFunc.js";
|
|
3
|
+
import { requireNamespace } from "./requireNamespace.js";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Expose a value to a potentially deeply nested global variable
|
|
8
7
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
9
8
|
* @param value {object | Function | Function[]} The value(s) to expose
|
|
10
9
|
*/
|
|
11
|
-
export
|
|
10
|
+
export function expose(name: object | string, value: object | Function | Function[]): void {
|
|
12
11
|
if (arguments.length !== 2) {
|
|
13
12
|
throw new Error("Incorrect number of arguments passed to the expose function!");
|
|
14
13
|
}
|
|
@@ -50,7 +49,7 @@ export default function expose(name: object | string, value: object | Function |
|
|
|
50
49
|
|
|
51
50
|
if (has(namespace, keyName)) {
|
|
52
51
|
// If class is already defined, warn or show an alert
|
|
53
|
-
warn([name, keyName].join(".") + " is already defined!");
|
|
52
|
+
console.warn([name, keyName].join(".") + " is already defined!");
|
|
54
53
|
} else {
|
|
55
54
|
namespace[keyName] = value;
|
|
56
55
|
}
|