@saltcorn/mobile-app 1.1.0-beta.8 → 1.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/.babelrc +3 -0
- package/build_scripts/modify_android_manifest.js +47 -0
- package/build_scripts/modify_gradle_cfg.js +21 -0
- package/package.json +21 -12
- package/src/.eslintrc +21 -0
- package/src/helpers/api.js +43 -0
- package/src/helpers/auth.js +191 -0
- package/src/helpers/common.js +189 -0
- package/{www/js/utils/table_utils.js → src/helpers/db_schema.js} +18 -40
- package/src/helpers/file_system.js +102 -0
- package/{www/js/utils/global_utils.js → src/helpers/navigation.js} +189 -332
- package/src/helpers/offline_mode.js +645 -0
- package/src/index.js +20 -0
- package/src/init.js +424 -0
- package/src/routing/index.js +98 -0
- package/{www/js → src/routing}/mocks/request.js +5 -5
- package/{www/js → src/routing}/mocks/response.js +1 -1
- package/{www/js → src/routing}/routes/api.js +10 -15
- package/{www/js → src/routing}/routes/auth.js +12 -6
- package/{www/js → src/routing}/routes/delete.js +9 -6
- package/{www/js → src/routing}/routes/edit.js +9 -6
- package/src/routing/routes/error.js +6 -0
- package/{www/js → src/routing}/routes/fields.js +7 -2
- package/{www/js → src/routing}/routes/page.js +15 -10
- package/{www/js → src/routing}/routes/sync.js +17 -8
- package/{www/js → src/routing}/routes/view.js +20 -15
- package/{www/js/routes/common.js → src/routing/utils.js} +18 -13
- package/unsecure-default-key.jks +0 -0
- package/webpack.config.js +31 -0
- package/www/data/encoded_site_logo.js +1 -0
- package/www/index.html +23 -493
- package/www/js/{utils/iframe_view_utils.js → iframe_view_utils.js} +193 -274
- package/config.xml +0 -27
- package/res/icon/android/icon.png +0 -0
- package/res/screen/android/splash-icon.png +0 -0
- package/res/screen/ios/Default@2x~universal~anyany.png +0 -0
- package/www/js/routes/error.js +0 -5
- package/www/js/routes/init.js +0 -76
- package/www/js/utils/file_helpers.js +0 -108
- package/www/js/utils/offline_mode_helper.js +0 -625
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
/*global
|
|
1
|
+
/*global saltcorn*/
|
|
2
|
+
import i18next from "i18next";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import { router } from "../routing/index";
|
|
5
|
+
import { startOfflineMode } from "./offline_mode";
|
|
6
|
+
import { showAlerts } from "./common";
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
export let routingHistory = [];
|
|
9
|
+
|
|
10
|
+
export function currentLocation() {
|
|
6
11
|
if (routingHistory.length == 0) return undefined;
|
|
7
12
|
let index = routingHistory.length - 1;
|
|
8
13
|
while (index > 0 && routingHistory[index].route.startsWith("post/")) {
|
|
@@ -11,7 +16,13 @@ function currentLocation() {
|
|
|
11
16
|
return routingHistory[index].route;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
function
|
|
19
|
+
export function currentUrl() {
|
|
20
|
+
const query = currentQuery();
|
|
21
|
+
const safeQuery = query ? (query.startsWith("?") ? query : `?${query}`) : "";
|
|
22
|
+
return `${currentLocation()}${safeQuery}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function currentQuery(skipPosts = false) {
|
|
15
26
|
if (routingHistory.length == 0) return undefined;
|
|
16
27
|
let index = routingHistory.length - 1;
|
|
17
28
|
if (skipPosts)
|
|
@@ -21,7 +32,7 @@ function currentQuery(skipPosts = false) {
|
|
|
21
32
|
return routingHistory[index].query;
|
|
22
33
|
}
|
|
23
34
|
|
|
24
|
-
function addQueryParam(key, value) {
|
|
35
|
+
export function addQueryParam(key, value) {
|
|
25
36
|
let query = currentQuery();
|
|
26
37
|
if (!query) {
|
|
27
38
|
routingHistory[routingHistory.length - 1].query = `${key}=${value}`;
|
|
@@ -32,251 +43,71 @@ function addQueryParam(key, value) {
|
|
|
32
43
|
}
|
|
33
44
|
}
|
|
34
45
|
|
|
35
|
-
function
|
|
36
|
-
routingHistory.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function popRoute() {
|
|
44
|
-
routingHistory.pop();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function apiCall({ method, path, params, body, responseType, timeout }) {
|
|
48
|
-
const config =
|
|
49
|
-
typeof saltcorn !== "undefined"
|
|
50
|
-
? saltcorn.data.state.getState().mobileConfig
|
|
51
|
-
: splashConfig;
|
|
52
|
-
const serverPath = config.server_path;
|
|
53
|
-
const url = `${serverPath}${path}`;
|
|
54
|
-
const headers = {
|
|
55
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
56
|
-
"X-Saltcorn-Client": "mobile-app",
|
|
57
|
-
};
|
|
58
|
-
if (config.tenantAppName) headers["X-Saltcorn-App"] = config.tenantAppName;
|
|
59
|
-
const token = config.jwt;
|
|
60
|
-
if (token) headers.Authorization = `jwt ${token}`;
|
|
61
|
-
try {
|
|
62
|
-
const result = await axios({
|
|
63
|
-
url: url,
|
|
64
|
-
method,
|
|
65
|
-
params,
|
|
66
|
-
headers,
|
|
67
|
-
responseType: responseType ? responseType : "json",
|
|
68
|
-
data: body,
|
|
69
|
-
timeout: timeout ? timeout : 0,
|
|
70
|
-
});
|
|
71
|
-
return result;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
error.message = `Unable to call ${method} ${url}:\n${error.message}`;
|
|
74
|
-
throw error;
|
|
46
|
+
export function setAnchor(anchor) {
|
|
47
|
+
if (routingHistory.length === 0)
|
|
48
|
+
throw new Error("No current route to set anchor");
|
|
49
|
+
else {
|
|
50
|
+
const current = routingHistory[routingHistory.length - 1];
|
|
51
|
+
current.anchor = anchor;
|
|
75
52
|
}
|
|
76
53
|
}
|
|
77
54
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function showAlerts(alerts, toast = true) {
|
|
86
|
-
if (typeof saltcorn === "undefined") {
|
|
87
|
-
console.log("Not yet initalized.");
|
|
88
|
-
console.log(alerts);
|
|
89
|
-
} else {
|
|
90
|
-
const iframe = document.getElementById("content-iframe");
|
|
91
|
-
let area = iframe.contentWindow.document.getElementById(
|
|
92
|
-
toast ? "toasts-area" : "top-alert"
|
|
93
|
-
);
|
|
94
|
-
if (!area) {
|
|
95
|
-
const areaHtml = `<div class="container">
|
|
96
|
-
<div
|
|
97
|
-
id="toasts-area"
|
|
98
|
-
class="toast-container position-fixed bottom-0 start-50 p-0"
|
|
99
|
-
style="z-index: 9999;"
|
|
100
|
-
aria-live="polite"
|
|
101
|
-
aria-atomic="true">
|
|
102
|
-
</div>
|
|
103
|
-
</div>`;
|
|
104
|
-
iframe.contentWindow.document
|
|
105
|
-
.getElementById("page-inner-content")
|
|
106
|
-
.insertAdjacentHTML("beforeend", areaHtml);
|
|
107
|
-
area = iframe.contentWindow.document.getElementById(
|
|
108
|
-
toast ? "toasts-area" : "top-alert"
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const successIds = [];
|
|
113
|
-
area.innerHTML = "";
|
|
114
|
-
for (const { type, msg } of alerts) {
|
|
115
|
-
if (toast) {
|
|
116
|
-
const rndid = `tab${Math.floor(Math.random() * 16777215).toString(16)}`;
|
|
117
|
-
area.innerHTML += saltcorn.markup.toast(type, msg, rndid);
|
|
118
|
-
if (type === "success") successIds.push(rndid);
|
|
119
|
-
} else area.innerHTML += saltcorn.markup.alert(type, msg);
|
|
120
|
-
}
|
|
121
|
-
if (successIds.length > 0) {
|
|
122
|
-
setTimeout(() => {
|
|
123
|
-
for (const id of successIds) {
|
|
124
|
-
const toastEl = iframe.contentWindow.document.getElementById(id);
|
|
125
|
-
if (toastEl) $(toastEl).removeClass("show");
|
|
126
|
-
}
|
|
127
|
-
}, 5000);
|
|
128
|
-
}
|
|
55
|
+
export function getAnchor() {
|
|
56
|
+
if (routingHistory.length === 0) return undefined;
|
|
57
|
+
else {
|
|
58
|
+
const current = routingHistory[routingHistory.length - 1];
|
|
59
|
+
return current.anchor;
|
|
129
60
|
}
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function clearTopAlerts() {
|
|
134
|
-
const iframe = document.getElementById("content-iframe");
|
|
135
|
-
const area = iframe.contentWindow.document.getElementById("alerts-area");
|
|
136
|
-
if (area) area.innerHTML = "";
|
|
137
|
-
const topAlert = iframe.contentWindow.document.getElementById("top-alert");
|
|
138
|
-
if (topAlert) topAlert.innerHTML = "";
|
|
139
61
|
}
|
|
140
62
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
try {
|
|
144
|
-
const response = await apiCall({
|
|
145
|
-
method: "GET",
|
|
146
|
-
path: `/files/download/${fileId}`,
|
|
147
|
-
responseType: "blob",
|
|
148
|
-
});
|
|
149
|
-
return new Promise((resolve, reject) => {
|
|
150
|
-
const reader = new FileReader();
|
|
151
|
-
reader.onloadend = () => {
|
|
152
|
-
return resolve(reader.result);
|
|
153
|
-
};
|
|
154
|
-
reader.onerror = (error) => {
|
|
155
|
-
return reject(error);
|
|
156
|
-
};
|
|
157
|
-
reader.readAsText(response.data);
|
|
158
|
-
});
|
|
159
|
-
} catch (error) {
|
|
160
|
-
if (
|
|
161
|
-
!showAlerts([
|
|
162
|
-
{
|
|
163
|
-
type: "error",
|
|
164
|
-
msg: error.message ? error.message : "An error occured.",
|
|
165
|
-
},
|
|
166
|
-
])
|
|
167
|
-
);
|
|
168
|
-
throw error;
|
|
169
|
-
}
|
|
63
|
+
export function addRoute(routeEntry) {
|
|
64
|
+
routingHistory.push(routeEntry);
|
|
170
65
|
}
|
|
171
66
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const response = await apiCall({
|
|
175
|
-
method: "GET",
|
|
176
|
-
path: `/files/download/${fileId}`,
|
|
177
|
-
responseType: "blob",
|
|
178
|
-
});
|
|
179
|
-
return new Promise((resolve, reject) => {
|
|
180
|
-
const reader = new FileReader();
|
|
181
|
-
reader.onloadend = () => {
|
|
182
|
-
return resolve(reader.result);
|
|
183
|
-
};
|
|
184
|
-
reader.onerror = (error) => {
|
|
185
|
-
return reject(error);
|
|
186
|
-
};
|
|
187
|
-
reader.readAsDataURL(response.data);
|
|
188
|
-
});
|
|
189
|
-
} catch (error) {
|
|
190
|
-
showAlerts([
|
|
191
|
-
{
|
|
192
|
-
type: "error",
|
|
193
|
-
msg: error.message ? error.message : "An error occured.",
|
|
194
|
-
},
|
|
195
|
-
]);
|
|
196
|
-
}
|
|
67
|
+
export function clearHistory() {
|
|
68
|
+
routingHistory = [];
|
|
197
69
|
}
|
|
198
70
|
|
|
199
|
-
function
|
|
200
|
-
|
|
201
|
-
let query = undefined;
|
|
202
|
-
const queryStart = url.indexOf("?");
|
|
203
|
-
if (queryStart > 0) {
|
|
204
|
-
path = url.substring(0, queryStart);
|
|
205
|
-
query = url.substring(queryStart);
|
|
206
|
-
}
|
|
207
|
-
return { path, query };
|
|
71
|
+
export function popRoute() {
|
|
72
|
+
routingHistory.pop();
|
|
208
73
|
}
|
|
209
74
|
|
|
210
|
-
async function
|
|
75
|
+
export async function goBack(steps = 1, exitOnFirstPage = false) {
|
|
76
|
+
const { inLoadState } = saltcorn.data.state.getState().mobileConfig;
|
|
77
|
+
if (inLoadState) return;
|
|
211
78
|
const iframe = document.getElementById("content-iframe");
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
resolve();
|
|
227
|
-
};
|
|
228
|
-
scriptEl.src = "js/utils/iframe_view_utils.js";
|
|
229
|
-
} catch (e) {
|
|
230
|
-
reject(e);
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
iframe.onerror = () => {
|
|
234
|
-
reject();
|
|
235
|
-
};
|
|
236
|
-
});
|
|
237
|
-
} else iframe.setAttribute("is-html-file", false);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function addScriptToIframeHead(iframeDoc, script) {
|
|
241
|
-
return new Promise((resolve, reject) => {
|
|
242
|
-
const srcAttr = script.attributes.getNamedItem("src").value;
|
|
243
|
-
const existingScripts = iframeDoc.head.getElementsByTagName("script");
|
|
244
|
-
for (const existing of existingScripts) {
|
|
245
|
-
const existingSrc = existing.attributes.getNamedItem("src");
|
|
246
|
-
if (existingSrc && existingSrc.value === srcAttr) return resolve(); // already there
|
|
79
|
+
if (
|
|
80
|
+
routingHistory.length === 0 ||
|
|
81
|
+
(exitOnFirstPage && routingHistory.length === 1)
|
|
82
|
+
) {
|
|
83
|
+
navigator.app.exitApp();
|
|
84
|
+
} else if (routingHistory.length <= steps) {
|
|
85
|
+
try {
|
|
86
|
+
if (iframe?.contentWindow?.showLoadSpinner)
|
|
87
|
+
iframe.contentWindow.showLoadSpinner();
|
|
88
|
+
routingHistory = [];
|
|
89
|
+
await handleRoute("/");
|
|
90
|
+
} finally {
|
|
91
|
+
if (iframe?.contentWindow?.removeLoadSpinner)
|
|
92
|
+
iframe.contentWindow.removeLoadSpinner();
|
|
247
93
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const innerContentDiv = iframeDocument.getElementById("page-inner-content");
|
|
263
|
-
innerContentDiv.innerHTML = content;
|
|
264
|
-
const scripts = innerContentDiv.getElementsByTagName("script");
|
|
265
|
-
for (const script of scripts) {
|
|
266
|
-
if (script.attributes.getNamedItem("src")) {
|
|
267
|
-
await addScriptToIframeHead(iframe.contentWindow.document, script);
|
|
268
|
-
} else {
|
|
269
|
-
iframe.contentWindow.eval(script.innerHTML);
|
|
94
|
+
} else {
|
|
95
|
+
try {
|
|
96
|
+
if (iframe?.contentWindow?.showLoadSpinner)
|
|
97
|
+
iframe.contentWindow.showLoadSpinner();
|
|
98
|
+
routingHistory = routingHistory.slice(0, routingHistory.length - steps);
|
|
99
|
+
// don't repeat a post
|
|
100
|
+
if (routingHistory[routingHistory.length - 1].route.startsWith("post/")) {
|
|
101
|
+
routingHistory.pop();
|
|
102
|
+
}
|
|
103
|
+
const newCurrent = routingHistory.pop();
|
|
104
|
+
await handleRoute(newCurrent.route, newCurrent.query);
|
|
105
|
+
} finally {
|
|
106
|
+
if (iframe?.contentWindow?.removeLoadSpinner)
|
|
107
|
+
iframe.contentWindow.removeLoadSpinner();
|
|
270
108
|
}
|
|
271
109
|
}
|
|
272
|
-
const scmodal = iframe.contentWindow.$("#scmodal");
|
|
273
|
-
if (scmodal) {
|
|
274
|
-
scmodal.modal("hide");
|
|
275
|
-
}
|
|
276
|
-
iframe.contentWindow.scrollTo(0, 0);
|
|
277
|
-
iframe.contentWindow.initialize_page();
|
|
278
110
|
}
|
|
279
|
-
|
|
280
111
|
function clearContentDiv() {
|
|
281
112
|
const iframe = document.getElementById("content-iframe");
|
|
282
113
|
if (iframe) {
|
|
@@ -286,53 +117,12 @@ function clearContentDiv() {
|
|
|
286
117
|
}
|
|
287
118
|
}
|
|
288
119
|
|
|
289
|
-
async function gotoEntryView() {
|
|
290
|
-
const mobileConfig = saltcorn.data.state.getState().mobileConfig;
|
|
291
|
-
try {
|
|
292
|
-
if (mobileConfig.inErrorState) window.location.reload(true);
|
|
293
|
-
else if (
|
|
294
|
-
mobileConfig.networkState === "none" &&
|
|
295
|
-
mobileConfig.allowOfflineMode &&
|
|
296
|
-
!mobileConfig.isOfflineMode
|
|
297
|
-
) {
|
|
298
|
-
await offlineHelper.startOfflineMode();
|
|
299
|
-
clearHistory();
|
|
300
|
-
}
|
|
301
|
-
const page = await router.resolve({
|
|
302
|
-
pathname: mobileConfig.entry_point,
|
|
303
|
-
alerts: [],
|
|
304
|
-
});
|
|
305
|
-
addRoute({ route: mobileConfig.entry_point, query: undefined });
|
|
306
|
-
await replaceIframeInnerContent(page.content);
|
|
307
|
-
} catch (error) {
|
|
308
|
-
showAlerts([
|
|
309
|
-
{
|
|
310
|
-
type: "error",
|
|
311
|
-
msg: error.message ? error.message : "An error occured.",
|
|
312
|
-
},
|
|
313
|
-
]);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function handleOpenModal() {
|
|
318
|
-
const result = { moddalWasOpen: false, noSubmitReload: false };
|
|
319
|
-
const iframe = document.getElementById("content-iframe");
|
|
320
|
-
if (!iframe) return result;
|
|
321
|
-
const openModal = iframe.contentWindow.$("#scmodal.modal.show");
|
|
322
|
-
if (openModal.length === 0) return result;
|
|
323
|
-
result.moddalWasOpen = true;
|
|
324
|
-
iframe.contentWindow.bootstrap.Modal.getInstance(openModal[0]).hide();
|
|
325
|
-
result.noSubmitReload = openModal[0].classList.contains("no-submit-reload");
|
|
326
|
-
return result;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
120
|
function isModalOpen() {
|
|
330
121
|
const iframe = document.getElementById("content-iframe");
|
|
331
122
|
if (!iframe) return false;
|
|
332
123
|
const openModal = iframe.contentWindow.$("#scmodal.modal.show");
|
|
333
124
|
return openModal.length > 0;
|
|
334
125
|
}
|
|
335
|
-
|
|
336
126
|
function replaceModalContent(content) {
|
|
337
127
|
const iframe = document.getElementById("content-iframe");
|
|
338
128
|
if (!iframe) throw new Error("No iframe found");
|
|
@@ -343,12 +133,7 @@ function replaceModalContent(content) {
|
|
|
343
133
|
modalBody.html(content);
|
|
344
134
|
}
|
|
345
135
|
|
|
346
|
-
function
|
|
347
|
-
const iframe = document.getElementById("content-iframe");
|
|
348
|
-
return iframe.getAttribute("is-html-file") === "true";
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
async function handleRoute(route, query, files, data) {
|
|
136
|
+
export async function handleRoute(route, query, files, data) {
|
|
352
137
|
const mobileConfig = saltcorn.data.state.getState().mobileConfig;
|
|
353
138
|
let routeAdded = false;
|
|
354
139
|
const isModal = isModalOpen();
|
|
@@ -359,7 +144,7 @@ async function handleRoute(route, query, files, data) {
|
|
|
359
144
|
mobileConfig.allowOfflineMode &&
|
|
360
145
|
!mobileConfig.isOfflineMode
|
|
361
146
|
) {
|
|
362
|
-
await
|
|
147
|
+
await startOfflineMode();
|
|
363
148
|
clearHistory();
|
|
364
149
|
await gotoEntryView();
|
|
365
150
|
} else {
|
|
@@ -395,7 +180,8 @@ async function handleRoute(route, query, files, data) {
|
|
|
395
180
|
);
|
|
396
181
|
}
|
|
397
182
|
} else if (page.content) {
|
|
398
|
-
if (isModal
|
|
183
|
+
if (isModal && route?.startsWith("post"))
|
|
184
|
+
replaceModalContent(page.content);
|
|
399
185
|
else if (!page.replaceIframe)
|
|
400
186
|
await replaceIframeInnerContent(page.content);
|
|
401
187
|
else await replaceIframe(page.content, page.isFile);
|
|
@@ -425,66 +211,137 @@ async function handleRoute(route, query, files, data) {
|
|
|
425
211
|
}
|
|
426
212
|
}
|
|
427
213
|
|
|
214
|
+
function handleOpenModal() {
|
|
215
|
+
const result = { moddalWasOpen: false, noSubmitReload: false };
|
|
216
|
+
const iframe = document.getElementById("content-iframe");
|
|
217
|
+
if (!iframe) return result;
|
|
218
|
+
const openModal = iframe.contentWindow.$("#scmodal.modal.show");
|
|
219
|
+
if (openModal.length === 0) return result;
|
|
220
|
+
result.moddalWasOpen = true;
|
|
221
|
+
iframe.contentWindow.bootstrap.Modal.getInstance(openModal[0]).hide();
|
|
222
|
+
result.noSubmitReload = openModal[0].classList.contains("no-submit-reload");
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function isHtmlFile() {
|
|
227
|
+
const iframe = document.getElementById("content-iframe");
|
|
228
|
+
return iframe.getAttribute("is-html-file") === "true";
|
|
229
|
+
}
|
|
230
|
+
|
|
428
231
|
async function reload() {
|
|
429
232
|
const currentRoute = currentLocation();
|
|
430
233
|
if (!currentRoute) await gotoEntryView();
|
|
431
234
|
await handleRoute(currentRoute, currentQuery(true));
|
|
432
235
|
}
|
|
433
236
|
|
|
434
|
-
async function
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (iframe?.contentWindow?.showLoadSpinner)
|
|
446
|
-
iframe.contentWindow.showLoadSpinner();
|
|
447
|
-
routingHistory = [];
|
|
448
|
-
await handleRoute("/");
|
|
449
|
-
} finally {
|
|
450
|
-
if (iframe?.contentWindow?.removeLoadSpinner)
|
|
451
|
-
iframe.contentWindow.removeLoadSpinner();
|
|
452
|
-
}
|
|
453
|
-
} else {
|
|
454
|
-
try {
|
|
455
|
-
if (iframe?.contentWindow?.showLoadSpinner)
|
|
456
|
-
iframe.contentWindow.showLoadSpinner();
|
|
457
|
-
routingHistory = routingHistory.slice(0, routingHistory.length - steps);
|
|
458
|
-
// don't repeat a post
|
|
459
|
-
if (routingHistory[routingHistory.length - 1].route.startsWith("post/")) {
|
|
460
|
-
routingHistory.pop();
|
|
461
|
-
}
|
|
462
|
-
const newCurrent = routingHistory.pop();
|
|
463
|
-
await handleRoute(newCurrent.route, newCurrent.query);
|
|
464
|
-
} finally {
|
|
465
|
-
if (iframe?.contentWindow?.removeLoadSpinner)
|
|
466
|
-
iframe.contentWindow.removeLoadSpinner();
|
|
237
|
+
export async function gotoEntryView() {
|
|
238
|
+
const mobileConfig = saltcorn.data.state.getState().mobileConfig;
|
|
239
|
+
try {
|
|
240
|
+
if (mobileConfig.inErrorState) window.location.reload(true);
|
|
241
|
+
else if (
|
|
242
|
+
mobileConfig.networkState === "none" &&
|
|
243
|
+
mobileConfig.allowOfflineMode &&
|
|
244
|
+
!mobileConfig.isOfflineMode
|
|
245
|
+
) {
|
|
246
|
+
await startOfflineMode();
|
|
247
|
+
clearHistory();
|
|
467
248
|
}
|
|
249
|
+
const page = await router.resolve({
|
|
250
|
+
pathname: mobileConfig.entry_point,
|
|
251
|
+
alerts: [],
|
|
252
|
+
});
|
|
253
|
+
addRoute({ route: mobileConfig.entry_point, query: undefined });
|
|
254
|
+
await replaceIframeInnerContent(page.content);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
showAlerts([
|
|
257
|
+
{
|
|
258
|
+
type: "error",
|
|
259
|
+
msg: error.message ? error.message : "An error occured.",
|
|
260
|
+
},
|
|
261
|
+
]);
|
|
468
262
|
}
|
|
469
263
|
}
|
|
470
264
|
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
265
|
+
export async function replaceIframe(content, isFile = false) {
|
|
266
|
+
const iframe = document.getElementById("content-iframe");
|
|
267
|
+
iframe.srcdoc = content;
|
|
268
|
+
if (isFile) {
|
|
269
|
+
iframe.setAttribute("is-html-file", true);
|
|
270
|
+
await new Promise((resolve, reject) => {
|
|
271
|
+
iframe.onload = () => {
|
|
272
|
+
try {
|
|
273
|
+
const _iframe = document.getElementById("content-iframe");
|
|
274
|
+
const iframeDoc = _iframe.contentWindow.document;
|
|
275
|
+
const baseEl = iframeDoc.createElement("base");
|
|
276
|
+
iframeDoc.head.appendChild(baseEl);
|
|
277
|
+
baseEl.href = "http://localhost";
|
|
278
|
+
const scriptEl = iframeDoc.createElement("script");
|
|
279
|
+
iframeDoc.body.appendChild(scriptEl);
|
|
280
|
+
scriptEl.onload = () => {
|
|
281
|
+
resolve();
|
|
282
|
+
};
|
|
283
|
+
scriptEl.src = "js/iframe_view_utils.js";
|
|
284
|
+
} catch (e) {
|
|
285
|
+
reject(e);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
iframe.onerror = () => {
|
|
289
|
+
reject();
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
} else iframe.setAttribute("is-html-file", false);
|
|
479
293
|
}
|
|
480
294
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
295
|
+
export function addScriptToIframeHead(iframeDoc, script) {
|
|
296
|
+
return new Promise((resolve /*reject*/) => {
|
|
297
|
+
const srcAttr = script.attributes.getNamedItem("src").value;
|
|
298
|
+
const existingScripts = iframeDoc.head.getElementsByTagName("script");
|
|
299
|
+
for (const existing of existingScripts) {
|
|
300
|
+
const existingSrc = existing.attributes.getNamedItem("src");
|
|
301
|
+
if (existingSrc && existingSrc.value === srcAttr) return resolve(); // already there
|
|
302
|
+
}
|
|
303
|
+
const scriptEl = iframeDoc.createElement("script");
|
|
304
|
+
iframeDoc.head.appendChild(scriptEl);
|
|
305
|
+
scriptEl.onload = () => {
|
|
306
|
+
resolve();
|
|
307
|
+
};
|
|
308
|
+
scriptEl.src = srcAttr;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function replaceIframeInnerContent(content) {
|
|
313
|
+
const iframe = document.getElementById("content-iframe");
|
|
314
|
+
const iframeDocument = iframe.contentWindow.document;
|
|
315
|
+
if (iframe.contentWindow.$) {
|
|
316
|
+
const scmodal = iframe.contentWindow.$("#scmodal");
|
|
317
|
+
if (scmodal.length > 0) {
|
|
318
|
+
scmodal.modal("hide");
|
|
319
|
+
scmodal.remove();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const modal = iframeDocument.getElementById("scmodal");
|
|
323
|
+
if (modal) modal.remove();
|
|
324
|
+
const innerContentDiv = iframeDocument.getElementById("page-inner-content");
|
|
325
|
+
innerContentDiv.innerHTML = content;
|
|
326
|
+
const scripts = innerContentDiv.getElementsByTagName("script");
|
|
327
|
+
for (const script of scripts) {
|
|
328
|
+
if (script.attributes.getNamedItem("src")) {
|
|
329
|
+
await addScriptToIframeHead(iframe.contentWindow.document, script);
|
|
330
|
+
} else {
|
|
331
|
+
iframe.contentWindow.eval(script.innerHTML);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
iframe.contentWindow.scrollTo(0, 0);
|
|
335
|
+
iframe.contentWindow.initialize_page();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function splitPathQuery(url) {
|
|
339
|
+
let path = url;
|
|
340
|
+
let query = undefined;
|
|
341
|
+
const queryStart = url.indexOf("?");
|
|
342
|
+
if (queryStart > 0) {
|
|
343
|
+
path = url.substring(0, queryStart);
|
|
344
|
+
query = url.substring(queryStart);
|
|
345
|
+
}
|
|
346
|
+
return { path, query };
|
|
490
347
|
}
|