Skip to content

Commit a0fe97b

Browse files
Release 20250708
1 parent 0607d7b commit a0fe97b

File tree

6 files changed

+345
-17
lines changed

6 files changed

+345
-17
lines changed

hlp/Help-cs.zip

4.08 KB
Binary file not shown.

hlp/Help-en.zip

3.71 KB
Binary file not shown.

hvdata/appmain.js

Lines changed: 284 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,301 @@
1-
let arcData = null;
1+
const PAR_NAME_DOC = 'd'; // Help file path
22

3-
async function main() {
4-
arcData = await loadZipFromUrl('hvdata/data.zip');
5-
const srcT = await searchArchiveForFile('appmainRun.js', arcData);
6-
appendJavaScript('appRun', srcT, document.body);
3+
const id_JSAppRun = 'appRun';
4+
const FILENAME_ZIP_ON_USER_INPUT = '!.zip';
5+
6+
function _T(id) {
7+
return id;
8+
}
9+
10+
function appendField(target, id, defaultV = '', type = 'text') {
11+
target.innerHTML +=
12+
`<div class="form-row">
13+
<label for="${id}">${_T(id)}</label>
14+
<input type="${type}" id="${id}" value="${defaultV}" />
15+
</div>`;
16+
}
17+
18+
function formCorsHelpFilesUpload()
19+
{
20+
const formO = document.getElementById('formIn');
21+
const fieldHvData = 'data.zip';
22+
const fieldHelpLang = 'Help-(language).zip';
23+
//const fieldHelpBase = 'Help-.zip';
24+
const typeFile = 'file';
25+
26+
appendField(formO, fieldHvData, '', typeFile);
27+
appendField(formO, fieldHelpLang, '', typeFile);
28+
//appendField(formO, fieldHelpBase, '', typeFile);
29+
30+
const formM = document.getElementById('form');
31+
formM.addEventListener("submit", function(e) {
32+
e.preventDefault();
33+
34+
const hvData = document.getElementById(fieldHvData);
35+
const helpLang = document.getElementById(fieldHelpLang);
36+
37+
if (!hvData?.files?.length || !helpLang?.files?.length)
38+
return;
39+
40+
const fileHvData = hvData.files[0];
41+
const fileHelpLang = helpLang.files[0];
42+
43+
// var fileHelpBase = document.getElementById(fieldHelpBase);
44+
45+
// if (fileHelpBase?.files?.length)
46+
// fileHelpBase = fileHelpBase[0];
47+
// else
48+
// fileHelpBase = null;
49+
50+
document.getElementById(id_JSAppRun)?.remove();
51+
st = _Storage.add(STO_HELP, FILENAME_ZIP_ON_USER_INPUT, fileHelpLang).then(obsah => {
52+
main(fileHvData);
53+
const url = new URL(window.location.href);
54+
url.searchParams.set(PAR_NAME_DOC, FILENAME_ZIP_ON_USER_INPUT);
55+
window.history.pushState({}, "", url);
56+
});
57+
58+
})
59+
}
60+
61+
const STO_DATA = 'STO_DATA';
62+
const STO_HELP = 'STO_HELP';
63+
const STOF_TEXT = 'text';
64+
const STOF_B64 = 'base64';
65+
66+
const DATA_FILE_PATH_BASE = 'hvdata/data';
67+
68+
const STORAGE_ENGINES = {
69+
'.zip': async (path) => newStorageZip(path),
70+
'/': async (path) => newStorageDir(path),
71+
};
72+
73+
var _Storage = (() => {
74+
var storagesC = new Map();
75+
76+
async function add(key, path, data = null) {
77+
for (const keyC in STORAGE_ENGINES) {
78+
if (path.endsWith(keyC)) {
79+
const eng = await STORAGE_ENGINES[keyC](data || path);
80+
storagesC.set(key, eng);
81+
return true;
82+
}
83+
}
84+
return null;
85+
}
86+
87+
async function search(key, filePath, format = STOF_TEXT) {
88+
if (!storagesC.has(key))
89+
return null;
90+
91+
return await storagesC.get(key).search(filePath, format);
92+
}
93+
94+
async function getSubdirs(key, parentPath) {
95+
if (!storagesC.has(key))
96+
return [];
97+
98+
return await storagesC.get(key).getSubdirs(parentPath);
99+
}
100+
101+
async function searchImage(key, filePath) {
102+
if (!storagesC.has(key))
103+
return null;
104+
105+
return await storagesC.get(key).searchImage(filePath);
106+
}
107+
108+
return {
109+
add,
110+
search,
111+
getSubdirs,
112+
searchImage
113+
};
114+
})();
115+
116+
async function newStorageZip(path) {
117+
var storageO = await init(path);
118+
119+
async function init(path) {
120+
return await ZIPHelpers.loadZipFromUrl(path);
121+
}
122+
123+
async function search(filePath, format = STOF_TEXT) {
124+
return await ZIPHelpers.searchArchiveForFile(filePath, storageO, format);
125+
}
126+
127+
async function getSubdirs(parentPath) {
128+
const subdirs = new Set();
129+
130+
storageO?.forEach((relativePath, file) => {
131+
if (relativePath.startsWith(parentPath) && relativePath !== parentPath)
132+
{
133+
const subPath = relativePath.slice(parentPath.length);
134+
const parts = subPath.split("/");
135+
136+
if (parts.length > 1) {
137+
subdirs.add(parts[0]);
138+
} else if (file.dir) {
139+
subdirs.add(parts[0]);
140+
}
141+
}
142+
});
143+
144+
return [...subdirs];
145+
}
146+
147+
async function searchImage(filePath) {
148+
const content = await search(filePath, STOF_B64);
149+
if (!content) return null;
150+
var mimeType = 'image/' + filePath.split('.').pop().toLowerCase();
151+
return `data:${mimeType};base64,${content}`;
152+
}
153+
154+
return {
155+
search,
156+
getSubdirs,
157+
searchImage
158+
};
159+
}
160+
161+
async function newStorageDir(path) {
162+
var storageO = await init(path);
163+
164+
async function init(path) {
165+
return path.replace(/\/$/, '');
166+
}
167+
async function search(filePath, format = STOF_TEXT) {
168+
var fpath = `${storageO}/${filePath}`;
169+
const doubleSlash = '//';
170+
const doubleSlashIndexLast = fpath.lastIndexOf(doubleSlash);
171+
const doubleSlashIndex = fpath.indexOf(doubleSlash);
172+
173+
if (doubleSlashIndexLast != doubleSlashIndex && doubleSlashIndex >= 0 && doubleSlashIndexLast >= 0) {
174+
var replacement = '/_base/';
175+
if (fpath.startsWith('http') || fpath.startsWith('ftp'))
176+
replacement = '/';
177+
178+
fpath = fpath.slice(0, doubleSlashIndexLast) + replacement + fpath.slice(doubleSlashIndexLast + 2);
179+
}
180+
181+
const response = await fetchDataOrEmpty(fpath);
182+
183+
switch (format) {
184+
case STOF_B64:
185+
const zip = new JSZip();
186+
const fname = "a.txt";
187+
zip.file(fname, response, { compression: "STORE" });
188+
const base64zip = await zip.generateAsync({ type: STOF_B64 });
189+
await zip.loadAsync(base64zip, { base64: true });
190+
const file = zip.file(fname);
191+
const b64Data = await file.async(STOF_B64);
192+
return b64Data;
193+
case STOF_TEXT:
194+
default:
195+
return toText(response);
196+
}
197+
}
198+
199+
function toText(ab) {
200+
const decoder = new TextDecoder("utf-8");
201+
const text = decoder.decode(ab);
202+
return text;
203+
}
204+
205+
async function getSubdirs(parentPath) {
206+
const list = search(`${storageO}/${parentPath}/__dir.lst`, format = STOF_TEXT);
207+
const text = toText(list);
208+
text = text.trim().replace(/\r\n/g, "\n").split('\n');
209+
210+
const subdirs = new Set();
211+
text?.forEach((line, index) => {
212+
subdirs.add(line);
213+
});
214+
215+
return [...subdirs];
216+
}
217+
218+
async function fetchDataOrEmpty(url) {
219+
try {
220+
const response = await fetchData(url);
221+
return response;
222+
} catch (error) {
223+
return new ArrayBuffer(0);
224+
}
225+
}
226+
227+
async function searchImage(filePath) {
228+
const fpath = `${storageO}/${filePath}`;
229+
const response = await fetchDataOrEmpty(fpath);
230+
if (response.byteLength == 0)
231+
return null;
232+
return fpath;
233+
}
234+
235+
return {
236+
search,
237+
getSubdirs,
238+
searchImage
239+
};
240+
}
241+
242+
async function main(baseDataStream = null) {
243+
var st = null;
244+
if (!baseDataStream) {
245+
try {
246+
st = await _Storage.add(STO_DATA, `${DATA_FILE_PATH_BASE}.zip`);
247+
} catch (error) {
248+
st = await _Storage.add(STO_DATA, `${DATA_FILE_PATH_BASE}/`);
249+
}
250+
} else {
251+
st = await _Storage.add(STO_DATA, `${DATA_FILE_PATH_BASE}.zip`, baseDataStream);
252+
}
253+
const srcT = await _Storage.search(STO_DATA, 'appmainRun.js');
254+
appendJavaScript(id_JSAppRun, srcT, document.body);
7255
runApp();
8256
}
9257

10-
async function loadZipFromUrl(url) {
258+
async function fetchData(url) {
11259
try {
12260
const response = await fetch(url);
13261
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
14262
const arrayBuffer = await response.arrayBuffer();
15-
16-
const arch = await JSZip.loadAsync(arrayBuffer);
17-
return arch;
263+
return arrayBuffer;
18264
} catch (error) {
19265
throw error;
20266
}
21267
}
22268

23-
async function searchArchiveForFile(fileName, arch) {
24-
try {
25-
const fileContent = await arch.file(fileName)?.async('text');
26-
return fileContent ?? "";
27-
} catch (error) {
28-
return "";
269+
const ZIPHelpers = (() => {
270+
async function loadZipFromUrl(url) {
271+
try {
272+
var arrayBuffer = null;
273+
if (typeof url === "string") {
274+
arrayBuffer = await fetchData(url);
275+
} else {
276+
arrayBuffer = await url.arrayBuffer();
277+
}
278+
const arch = await JSZip.loadAsync(arrayBuffer);
279+
return arch;
280+
} catch (error) {
281+
throw error;
282+
}
29283
}
30-
}
284+
285+
async function searchArchiveForFile(fileName, arch, format = STOF_TEXT) {
286+
try {
287+
const fileContent = await arch.file(fileName)?.async(format);
288+
return fileContent ?? "";
289+
} catch (error) {
290+
return "";
291+
}
292+
}
293+
294+
return {
295+
loadZipFromUrl,
296+
searchArchiveForFile
297+
};
298+
})();
31299

32300
function appendCSS(id, content) {
33301
//if (document.getElementById(id)) return;

hvdata/data.zip

237 KB
Binary file not shown.

index.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@
1414
<meta property="og:description" content="Help Viewer help file.">
1515
<meta property="og:type" content="website">
1616
<meta property="og:image" content="favicon.png">
17+
<meta name="robots" content="index,follow">
18+
<style id="i-formsupport-css">
19+
.form-row {
20+
display: flex;
21+
gap: 1em;
22+
align-items: baseline;
23+
margin: 0.5em;
24+
}
25+
26+
.form-row label {
27+
width: 120px;
28+
margin-right: 0.5em;
29+
text-align: left;
30+
}
31+
32+
.form-row input {
33+
flex: 1;
34+
}
35+
</style>
36+
<!-- %NREL% -->
1737
</head>
1838
<script>
1939
if ('serviceWorker' in navigator) {
@@ -22,4 +42,34 @@
2242
}
2343
</script>
2444
<body class="inStandard"></body>
45+
<noscript>
46+
<div id="js-error"> ⚠ JavaScript is turned off!</div>
47+
</noscript>
48+
<div id="cors-error">
49+
<p> ⚠ Do you see this message only? Then viewer is not reading help file content.<br>This may be caused by your browser blocking local file access (file://) due to CORS policy restrictions. </p>
50+
<p>You can select these files:</p>
51+
<form id="form">
52+
<div id="formIn">
53+
</div>
54+
<button type="submit">Odeslat</button>
55+
</form>
56+
<script>
57+
formCorsHelpFilesUpload();
58+
</script>
59+
<p>Alternatively you need to run your browser in mode with bypass CORS policy:</p>
60+
<ul>
61+
<li><p>Chrome:</p>
62+
<blockquote>
63+
<p>Run in CLI:
64+
chrome.exe --disable-site-isolation-trials --disable-web-security --user-data-dir="C:\temp"</p>
65+
</blockquote>
66+
</li>
67+
<li><p>Edge:</p>
68+
<blockquote>
69+
<p>Run in CLI:
70+
msedge --disable-web-security --user-data-dir="C:\temp"</p>
71+
</blockquote>
72+
</li>
73+
</ul>
74+
</div>
2575
</html>

0 commit comments

Comments
 (0)