Update FramesBrowser; Add gcounter and some fixes to global JS

This commit is contained in:
octospacc 2024-09-14 02:23:39 +02:00
parent 0c7cb2c7ac
commit 6372cfe2fe
4 changed files with 544 additions and 363 deletions

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
SourceApps="SpiderADB TiktOctt TiVuOcto WuppiMini" SourceApps="$(ls ./source/)"
HubSdkApps="${SourceApps} MatrixStickerHelper" HubSdkApps="${SourceApps} MatrixStickerHelper"
HtmlHeadInject='<script src="../../shared/OctoHub-Global.js"></script>' HtmlHeadInject='<script src="../../shared/OctoHub-Global.js"></script>'

View File

@ -1,7 +1,7 @@
window.addEventListener('load', (function(){ window.addEventListener('load', (function(){
if (['', 'hub.octt.eu.org'].includes(location.host)) { if (['', 'hub.octt.eu.org'].includes(location.host)) {
if ('serviceWorker' in navigator) { if (('serviceWorker' in navigator) && (location.protocol.slice(0, 4) === 'http')) {
navigator.serviceWorker.register('/ServiceWorker.js'); navigator.serviceWorker.register('/ServiceWorker.js');
} }
} else { } else {
@ -30,4 +30,12 @@ if (['', 'hub.octt.eu.org'].includes(location.host)) {
document.body.appendChild(noticeElem); document.body.appendChild(noticeElem);
} }
if (["www.octt.eu.org", "hub.octt.eu.org"].includes(location.hostname)) {
fetch('https://private-analytics-not-for-public-use.octt.eu.org/octospacchub/count?p=' + location.href /* + '&rnd=' + Date.now() */)
.catch(function(err){
console.error(err);
fetch('https://octospacchub.goatcounter.com/count?p=' + location.href);
});
}
})); }));

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- TODO: <!-- TODO:
* deprecate old pseudo-framework and use SpaccDotWeb
* options menu/zone? * options menu/zone?
* js/css injection? * js/css injection?
* open file via drag&drop * open file via drag&drop
@ -28,6 +29,7 @@
--BaseMargin: 8px; --BaseMargin: 8px;
--BtnHeight: calc(1rem + var(--BaseMargin)); --BtnHeight: calc(1rem + var(--BaseMargin));
--BtnActionHeight: calc(2rem + var(--BaseMargin)); --BtnActionHeight: calc(2rem + var(--BaseMargin));
--BtnActionWidth: calc(2.5rem + var(--BaseMargin));
--ColorBg: #f0f0f0; --ColorBg: #f0f0f0;
--ColorFg: #0f0f0f; --ColorFg: #0f0f0f;
} }
@ -42,21 +44,36 @@
button { button {
height: var(--BtnHeight); height: var(--BtnHeight);
} }
iframe { iframe, #IframeMain, #IframeMainBox {
border: none; border: none;
width: 100%;
height: 100%;
}
#IframeMainBox {
overflow: hidden;
resize: both;
}
#IframeMainBox[style] {
border: 4px dashed gray;
}
#IframeMainContainer {
width: 100vw; width: 100vw;
position: relative; position: relative;
overflow: auto;
} }
#BoxControls { #BoxControls {
overflow: auto; overflow: auto;
} }
#BoxControls table, #BoxControls table td, #BoxControls table td > * { #BoxControls table, #BoxControls table td, #BoxControls table td > * {
height: var(--BtnActionHeight); height: var(--BtnActionHeight);
min-width: var(--BtnActionHeight); min-width: var(--BtnActionWidth);
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
border-spacing: 0; border-spacing: 0;
} }
#BoxControls table td > button {
word-spacing: 100vw;
}
#BtnFullscreen { #BtnFullscreen {
position: absolute; position: absolute;
top: 0; top: 0;
@ -94,6 +111,7 @@
} }
</style> </style>
<script src="../../shared/OctoHub-Global.js"></script> <script src="../../shared/OctoHub-Global.js"></script>
<script> window.FramesBrowser = { Lib: {} }; </script>
</head> </head>
<body> <body>
<button id="BtnFullscreen" onclick="ToggleFullscreen()">🎞️ Menu</button> <button id="BtnFullscreen" onclick="ToggleFullscreen()">🎞️ Menu</button>
@ -104,12 +122,15 @@
<button id="BtnFile" onclick="this.nextElementSibling.click()">📄 File</button> <button id="BtnFile" onclick="this.nextElementSibling.click()">📄 File</button>
<input type="file" hidden="hidden" style="display: none;" onchange="LoadFile(this.files[0])"/> <input type="file" hidden="hidden" style="display: none;" onchange="LoadFile(this.files[0])"/>
</td> </td>
<td><button style="min-width: calc(4em + 4px);" onclick="ZoomFrame()">🔍️ Zoom</button></td> <td><button onclick="ZoomFrame()">🔍️ Zoom</button></td>
<td><button onclick="ResizeFrame()">↘️ Size</button></td>
<td><button onclick="ToggleFullscreen()">🎞️ Hide</button></td> <td><button onclick="ToggleFullscreen()">🎞️ Hide</button></td>
<td><button style="min-width: calc(4em + 4px);" onclick="ListFrames()">🪟 Tabs</button></td> <td><button onclick="ListFrames()">🪟 Tabs</button></td>
<td style="width: 100%;"><input id="InputUri" type="text" style="min-width: 100%;" placeholder="🔗️ Enter URI..." onkeydown="InputHandleKey(event)"/></td> <td style="width: 100%;"><input id="InputUri" type="text" style="min-width: 100%;" placeholder="🔗️ Enter URI..." onkeydown="InputHandleKey(event)"/></td>
<td><button id="BtnLoad" onclick="LoadFrame()">↩️ Load</button></td> <td><button id="BtnLoad" onclick="LoadFrame()">↩️ Load</button></td>
<td><button id="BtnExcise" onclick="ExciseFrame()">↗️ Excise</button></td> <td><button id="BtnExcise" onclick="ExciseFrame()">↗️ Excise</button></td>
<td><button onclick="FrameDispatch('Screenshot')">🖼️ Shot</button></td>
<td><button onclick="FrameDispatch('Print')">🖨️ Print</button></td>
<tr></table></div> <tr></table></div>
<div id="BoxHandy"></div> <div id="BoxHandy"></div>
<noscript><div class="NoScript padded"><p> <noscript><div class="NoScript padded"><p>
@ -147,6 +168,11 @@
<li>Clicking "<code>📐️ Tools</code>" will open or close <a href="https://eruda.liriliri.io">the Eruda console</a> as desired. (Note that Eruda is currently only injected in the root window and not inside any iFrame.)</li> <li>Clicking "<code>📐️ Tools</code>" will open or close <a href="https://eruda.liriliri.io">the Eruda console</a> as desired. (Note that Eruda is currently only injected in the root window and not inside any iFrame.)</li>
</ul> </ul>
<h2>Changelog</h2> <h2>Changelog</h2>
<h3>2024-09-13</h3><ul>
<li>Better normalized the action buttons' width.</li>
<li>Allow resizing frames (and then resetting the size), by hand or by inputting numbers.</li>
<li>Add "<code>🖨️ Print</code>" and (experimental, thanks to <a href="https://html2canvas.hertzen.com">html2canvas</a>) "<code>🖼️ Screenshot</code>" features for any current frame. (Note: this requires code injection and will not work 100%.)</li>
</ul>
<h3>2024-03-19</h3><ul> <h3>2024-03-19</h3><ul>
<li>Remove info button from toolbar for now.</li> <li>Remove info button from toolbar for now.</li>
<li>Update info page also adding Usage and Help section with tips.</li> <li>Update info page also adding Usage and Help section with tips.</li>
@ -189,18 +215,62 @@
}) })
</script> </script>
</div></div> </div></div>
<script src="./html2canvas.min.wrappedLib.js"></script>
<script> <script>
var AppData, SesAppData, SesAppDataBak; var AppData, SesAppData, SesAppDataBak;
function SaveAppData(){ function SaveAppData(){
localStorage.setItem('org.eu.octt.FramesBrowser.v1', JSON.stringify({ ...AppData, ...(SesAppData.optionsFromUrl ? SesAppDataBak : SesAppData) })); localStorage.setItem('org.eu.octt.FramesBrowser.v1', JSON.stringify({ ...AppData, ...(SesAppData.optionsFromUrl ? SesAppDataBak : SesAppData) }));
}; };
var FrameZoomLevels = [50, 200]; var FrameZoomLevels = [50, 200];
var SampleHtmlContent = `<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>${MainAppContent.innerHTML}</body></html>`; var SampleHtmlContent = `<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>${MainAppContent.innerHTML}</body></html>`;
MainAppContent.innerHTML = '<iframe id="IframeMain"></iframe>'; MainAppContent.innerHTML = `<div id="IframeMainContainer">
document.body.style.overflow = 'hidden'; <div id="IframeMainBox">
<iframe id="IframeMain"></iframe>
</div>
</div>`;
document.body.style.overflow = 'hidden';
function $new(tag, props){ var FrameHtmlInjectable = ('<scr' + 'ipt> window.FramesBrowser = ' + JSON.stringify(window.FramesBrowser) + ' ; (' + (function(){
for (var name in window.FramesBrowser.Lib) {
var content = window.FramesBrowser.Lib[name];
if ((typeof content) === 'string') {
content = eval(content);
if ((typeof content) === 'boolean') {
window.FramesBrowser.Lib[name] = window[name];
}
}
}
window.FramesBrowser.Print = (function(){
window.print();
});
window.FramesBrowser.Screenshot = (function(){
// TODO must somehow load images in canvas with cors proxy or it becomes tainted and undownloadable
// for now just show on screen and let user download via browser actions
html2canvas(document.body, { allowTaint: true }).then(function(canvas){
try {
Object.assign(document.createElement('a'), {
download: `FramesBrowser-Screenshot-${(new Date).toJSON()}`,
href: canvas.toDataURL('image/png'),
}).click();
} catch(err) {
console.error(err);
var modalEl = document.createElement('dialog');
document.body.appendChild(modalEl);
modalEl.appendChild(canvas);
modalEl.showModal();
}
});
});
window.addEventListener('message', (function(messageEv){
var action = (messageEv.data && messageEv.data.FramesBrowser);
if (action) {
FramesBrowser[action]();
}
}));
}) + ')(); </scr' + 'ipt>');
function $new (tag, props) {
var el = document.createElement(tag); var el = document.createElement(tag);
if (props) { if (props) {
Object.keys(props).forEach(function(key){ Object.keys(props).forEach(function(key){
@ -208,9 +278,9 @@
}); });
}; };
return el; return el;
}; }
function $request(url, opts){ function $request (url, opts) {
if (!opts.method) { if (!opts.method) {
opts.method = 'GET'; opts.method = 'GET';
}; };
@ -222,20 +292,20 @@
}; };
req.open(opts.method, url, true); req.open(opts.method, url, true);
req.send(); req.send();
}; }
function InputHandleKey(ev){ function InputHandleKey (ev) {
// Enter // Enter
if (ev.keyCode == 13) { if (ev.keyCode == 13) {
LoadFrame(); LoadFrame();
}; };
}; }
function GetTabUrlFromTabIndex (index) { function GetTabUrlFromTabIndex (index) {
return (index === -1 ? null : AppData.urls[AppData.tabs[index].urlIndex]); return (index === -1 ? null : AppData.urls[AppData.tabs[index].urlIndex]);
}; }
function ShowFrame(index){ function ShowFrame (index) {
var isFrameRoot = (index === -1); var isFrameRoot = (index === -1);
var url = (GetTabUrlFromTabIndex(index) || ''); var url = (GetTabUrlFromTabIndex(index) || '');
ListFramesClose(); ListFramesClose();
@ -246,11 +316,24 @@
BtnFile.disabled = isFrameRoot; BtnFile.disabled = isFrameRoot;
BtnLoad.disabled = isFrameRoot; BtnLoad.disabled = isFrameRoot;
BtnExcise.disabled = isFrameRoot; BtnExcise.disabled = isFrameRoot;
document.querySelector('iframe').src = (isFrameRoot ? `data:text/html;utf8,${encodeURIComponent(SampleHtmlContent)}` : url); if (isFrameRoot) {
}; url = createDataUrl('text/html', SampleHtmlContent, 'utf8');
}
if (isDataUrl(url)) {
var [mime, body, encoding] = extractDataUrl(url);
if (mime.toLowerCase() !== 'text/html') {
return url;
}
url = createDataUrl(mime, patchFrameHtml(body), (encoding || 'utf8'));
}
document.querySelector('iframe').src = url;
}
function SaveUrl(){ function SaveUrl (url) {
var url = document.querySelector('input[type="text"]').value; if (url) {
document.querySelector('input[type="text"]').value = url;
}
url = document.querySelector('input[type="text"]').value;
var urlIndex = AppData.urls.indexOf(url); var urlIndex = AppData.urls.indexOf(url);
if (urlIndex === -1) { if (urlIndex === -1) {
// it's a new url, store it // it's a new url, store it
@ -261,9 +344,9 @@
PruneUnusedUrls(); PruneUnusedUrls();
SaveAppData(); SaveAppData();
return url; return url;
}; }
function PruneUnusedUrls () { function PruneUnusedUrls () {
var urlsNew = []; var urlsNew = [];
for (var urlIndex in AppData.urls) { for (var urlIndex in AppData.urls) {
if (AppData.tabs.filter(function(tab){ if (AppData.tabs.filter(function(tab){
@ -278,17 +361,17 @@
} }
AppData.urls = urlsNew; AppData.urls = urlsNew;
SaveAppData(); SaveAppData();
}; }
function AddFrame(){ function AddFrame () {
AppData.tabs = AppData.tabs.concat([{}]); AppData.tabs = AppData.tabs.concat([{}]);
ListFrames(); ListFrames();
ShowFrame(AppData.tabs.length - 1); ShowFrame(AppData.tabs.length - 1);
SaveAppData(); SaveAppData();
RefreshFramesCounter(); RefreshFramesCounter();
}; }
function CloseFrame(index){ function CloseFrame (index) {
AppData.tabs.splice(index, 1); AppData.tabs.splice(index, 1);
PruneUnusedUrls(); PruneUnusedUrls();
if (AppData.currentTabIndex === index) { if (AppData.currentTabIndex === index) {
@ -299,9 +382,9 @@
ListFrames(); ListFrames(); ListFrames(); ListFrames();
SaveAppData(); SaveAppData();
RefreshFramesCounter(); RefreshFramesCounter();
}; }
function RefreshFramesCounter(){ function RefreshFramesCounter () {
var count = AppData.tabs.length; var count = AppData.tabs.length;
var countHtml = ''; var countHtml = '';
if (count > 99) { if (count > 99) {
@ -310,13 +393,13 @@
countHtml = `(${count})`; countHtml = `(${count})`;
}; };
document.querySelector('button[onclick="ListFrames()"]').textContent = `🪟${countHtml} Tabs`; document.querySelector('button[onclick="ListFrames()"]').textContent = `🪟${countHtml} Tabs`;
}; }
function GetNeededIframeHeight (hScale=100) { function GetNeededIframeHeight (hScale=100) {
return (SesAppData.fullscreen ? `${hScale}vh` : `calc(${hScale}vh - (var(--BtnActionHeight) * ${hScale / 100}))`); return (SesAppData.fullscreen ? `${hScale}vh` : `calc(${hScale}vh - (var(--BtnActionHeight) * ${hScale / 100}))`);
} }
function ApplyFullscreen () { function ApplyFullscreen () {
if (SesAppData.fullscreen) { if (SesAppData.fullscreen) {
BoxControls.style.display = 'none'; BoxControls.style.display = 'none';
BtnFullscreen.style.display = ''; BtnFullscreen.style.display = '';
@ -325,57 +408,98 @@
BtnFullscreen.style.display = 'none'; BtnFullscreen.style.display = 'none';
} }
ApplyFrameZoom(); ApplyFrameZoom();
} }
function ToggleFullscreen () { function ToggleFullscreen () {
SesAppData.fullscreen = !SesAppData.fullscreen; SesAppData.fullscreen = !SesAppData.fullscreen;
ApplyFullscreen(); ApplyFullscreen();
SaveAppData(); SaveAppData();
} }
function LoadFrame(){ function LoadFrame () {
var url = document.querySelector('input[type="text"]').value; var url = document.querySelector('input[type="text"]').value;
if (!url.toLowerCase().startsWith('javascript:')) { if (!url.toLowerCase().startsWith('javascript:')) {
document.querySelector('iframe').src = SaveUrl(); document.querySelector('iframe').src = SaveUrl();
} }
}; }
function ExciseFrame(){ function isDataUrl (url) {
var uri = SaveUrl(); return url.toLowerCase().startsWith('data:');
if (uri.toLowerCase().startsWith('data:')) { }
opendatauri(uri);
function extractDataUrl (url) {
var [mime, encoding] = url.split(',')[0].split('data:')[1].split(';');
var body = url.split(',').slice(1).join(',');
switch ((encoding || '').toLowerCase()) { default:
break; case 'utf8':
body = decodeURIComponent(body);
break; case 'base64':
body = atob(body);
}
return [mime, body, encoding];
}
function createDataUrl (mime, body, encoding) {
switch (encoding) { default:
break; case 'utf8':
body = encodeURIComponent(body);
break; case 'base64':
body = btoa(body);
}
return `data:${mime};${encoding},${body}`;
}
function patchFrameHtml (html) {
var replaced = false;
for (var ref of ["</head>", "</body>", "</html>"]) {
if (html.includes(ref)) {
html = html.replace(ref, (FrameHtmlInjectable + ref));
replaced = true;
break;
}
}
if (!replaced) {
html = (html + FrameHtmlInjectable);
}
return html;
}
function ExciseFrame () {
var url = SaveUrl();
if (isDataUrl(url)) {
opendatauri(url);
} else { } else {
open(uri, '_blank'); open(url, '_blank');
};
}; };
}
function LoadFile(file){ function LoadFile (file) {
var reader = new FileReader(); var reader = new FileReader();
reader.onload = function(){ reader.onload = function(){
document.querySelector('input[type="text"]').value = reader.result; document.querySelector('input[type="text"]').value = reader.result;
LoadFrame(); LoadFrame();
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
}; }
function ApplyFrameZoom () { function ApplyFrameZoom () {
var level = FrameZoomLevels[SesAppData.frameZoomIndex]; var level = FrameZoomLevels[SesAppData.frameZoomIndex];
var levelopp = FrameZoomLevels[FrameZoomLevels.length - 1 - SesAppData.frameZoomIndex]; var levelopp = FrameZoomLevels[FrameZoomLevels.length - 1 - SesAppData.frameZoomIndex];
['bottom', 'top', 'left', 'right', 'scale', 'width'].forEach(function(prop){ ['bottom', 'top', 'left', 'right', 'scale', 'width'].forEach(function(prop){
IframeMain.style[prop] = ''; IframeMainContainer.style[prop] = '';
}); });
if (level < 100) { if (level < 100) {
IframeMain.style.bottom = `calc(${level}vh - 16px)`; IframeMainContainer.style.bottom = `calc(${level}vh - 16px)`;
IframeMain.style.right = `${level}vw`; IframeMainContainer.style.right = `${level}vw`;
} else { } else {
IframeMain.style.top = `calc(${levelopp/2}vh - 8px)`; IframeMainContainer.style.top = `calc(${levelopp/2}vh - 8px)`;
IframeMain.style.left = `${levelopp/2}vw`; IframeMainContainer.style.left = `${levelopp/2}vw`;
} }
if (SesAppData.frameZoomIndex !== -1) { if (SesAppData.frameZoomIndex !== -1) {
IframeMain.style.scale = (level / 100); IframeMainContainer.style.scale = (level / 100);
IframeMain.style.width = `${levelopp}vw`; IframeMainContainer.style.width = `${levelopp}vw`;
} }
IframeMain.style.height = GetNeededIframeHeight(levelopp); IframeMainContainer.style.height = GetNeededIframeHeight(levelopp);
var zoomButton = document.querySelector('button[onclick="ZoomFrame()"]'); var zoomButton = document.querySelector('button[onclick="ZoomFrame()"]');
if (level < 100) { if (level < 100) {
zoomButton.textContent = '🔍️(-) Zoom'; zoomButton.textContent = '🔍️(-) Zoom';
@ -384,9 +508,9 @@
} else { } else {
zoomButton.textContent = '🔍️ Zoom'; zoomButton.textContent = '🔍️ Zoom';
}; };
} }
function ZoomFrame(){ function ZoomFrame () {
if (SesAppData.frameZoomIndex === FrameZoomLevels.length - 1) { if (SesAppData.frameZoomIndex === FrameZoomLevels.length - 1) {
SesAppData.frameZoomIndex = -1; SesAppData.frameZoomIndex = -1;
} else { } else {
@ -394,9 +518,60 @@
}; };
ApplyFrameZoom(); ApplyFrameZoom();
SaveAppData(); SaveAppData();
}; }
function ListFrames(){ function ResizeFrame () {
var prevStyle = IframeMainBox.style.length;
IframeMainBox.removeAttribute('style');
if (!prevStyle) {
var props = (prompt("Format: width,height") || '').trim();
if (!props) {
return;
}
props = (props
.replaceAll(' ', ',').replaceAll('\t', ',')
.replaceAll(':', ',').replaceAll(';', ',')
.replaceAll(',,', ',').replaceAll(',,', ','));
if ((!props.includes(',')) && props.includes('x')) {
props = props.replace('x', ',');
}
props = props.split(',');
if (!(props[0] || props[1])) {
return;
}
for (var i=0; i<props.length; i++) {
if (!props[i]) {
props[i] = '100%';
} else if (!isNaN(props[i])) {
props[i] += 'px';
}
}
IframeMainBox.style = `width: ${props[0]}; height: ${props[1]};`;
}
}
function FrameDispatch (action) {
var url = ((AppData.currentTabIndex !== -1) && SaveUrl());
if (url && (!isDataUrl(url))) {
IframeMain.src = createDataUrl('text/plain', 'Reloading, please wait...');
// TODO use dynamic proxy
fetch(`https://corsproxy.io/?${encodeURIComponent(url)}`).then(function(response){
response.text().then(function(html){
AddFrame();
SaveUrl(createDataUrl('text/html', html, 'utf8'));
ShowFrame(AppData.currentTabIndex);
});
}).catch(function(error){
console.error(error);
IframeMain.src = createDataUrl('text/plain', error);
});
return;
}
IframeMain.contentWindow.postMessage({ FramesBrowser: action }, '*');
return true;
}
function ListFrames () {
if (!ListFramesClose()){ if (!ListFramesClose()){
var Box = NewBoxPopup('BoxFramesList'); var Box = NewBoxPopup('BoxFramesList');
var BtnAdd = $new('button', { className: 'BtnAction', innerHTML: ' Add', onclick: AddFrame }); var BtnAdd = $new('button', { className: 'BtnAction', innerHTML: ' Add', onclick: AddFrame });
@ -417,18 +592,18 @@
li.append(close); li.append(close);
}; };
}; };
}; }
function ListFramesClose(){ function ListFramesClose () {
var exist = document.querySelector('#BoxFramesList'); var exist = document.querySelector('#BoxFramesList');
if (exist) { if (exist) {
BoxFramesList.remove(); BoxFramesList.remove();
}; };
return exist; return exist;
}; }
var isDevToolsOpen = false; var isDevToolsOpen = false;
function ToggleDevTools(){ function ToggleDevTools () {
if (typeof(eruda) === 'undefined') { if (typeof(eruda) === 'undefined') {
$request('https://cdn.jsdelivr.net/npm/eruda', { callback: function(text){ $request('https://cdn.jsdelivr.net/npm/eruda', { callback: function(text){
eval(text); eval(text);
@ -441,42 +616,35 @@
eruda[isDevToolsOpen ? 'hide' : 'show'](); eruda[isDevToolsOpen ? 'hide' : 'show']();
isDevToolsOpen = !isDevToolsOpen; isDevToolsOpen = !isDevToolsOpen;
} }
}; }
function NewBoxPopup(id){ function NewBoxPopup (id) {
var Container = $new('div', { id: id, className: 'BoxPopup Container' }); var Container = $new('div', { id: id, className: 'BoxPopup Container' });
var Content = $new('div', { className: 'BoxPopup Content' }); var Content = $new('div', { className: 'BoxPopup Content' });
Container.appendChild(Content); Container.appendChild(Content);
var BtnClose = $new('button', { className: 'BtnAction', innerHTML: '❌ Close', onclick: function(){this.parentElement.parentElement.remove()} }); var BtnClose = $new('button', { className: 'BtnAction', innerHTML: '❌ Close', onclick: function(){this.parentElement.parentElement.remove()} });
Content.appendChild(BtnClose); Content.appendChild(BtnClose);
BoxHandy.appendChild(Container); BoxHandy.appendChild(Container);
return { Container: Container, Content: Content }; return { Container, Content };
}; }
function opendatauri (data) { function opendatauri (url) {
var head = data.split(',')[0].split('data:')[1]; var [mime, body, encoding] = extractDataUrl(url);
var [mime, encoding] = head.split(';'); var bytes = new Array(body.length);
data = data.split(',').slice(1).join(','); for (var i = 0; i < body.length; i++) {
if (encoding.toLowerCase() === 'base64') { bytes[i] = body.charCodeAt(i);
data = atob(data);
} else if (encoding.toLowerCase() === 'utf8') {
data = decodeURIComponent(data);
};
var bytes = new Array(data.length);
for (var i = 0; i < data.length; i++) {
bytes[i] = data.charCodeAt(i);
}; };
window.open(URL.createObjectURL( window.open(URL.createObjectURL(
new Blob([new Uint8Array(bytes)], { type: `${mime};${encoding ? encoding : 'utf8'}` }) new Blob([new Uint8Array(bytes)], { type: `${mime};${encoding || 'utf8'}` })
), '_blank'); ), '_blank');
}; }
// https://stackoverflow.com/questions/45053624/convert-hex-to-binary-in-javascript/45054052#45054052 // https://stackoverflow.com/questions/45053624/convert-hex-to-binary-in-javascript/45054052#45054052
function hex2bin (hex) { function hex2bin (hex) {
return (parseInt(hex, 16).toString(2)).padStart((hex.length * 4), '0'); return (parseInt(hex, 16).toString(2)).padStart((hex.length * 4), '0');
} }
function AlertMigrateAppData(){ function AlertMigrateAppData () {
var stored = Object.keys(localStorage); var stored = Object.keys(localStorage);
if (stored.includes('FramesBrowser.CurrentFrames') || stored.includes('FramesBrowser.FrameIndexes') || stored.includes('FramesBrowser.url')) { if (stored.includes('FramesBrowser.CurrentFrames') || stored.includes('FramesBrowser.FrameIndexes') || stored.includes('FramesBrowser.url')) {
var overlay = document.createElement('div'); var overlay = document.createElement('div');
@ -494,11 +662,15 @@
}; };
document.body.appendChild(overlay); document.body.appendChild(overlay);
} }
}; }
window.onhashchange = function(){ location.reload() }; (new ResizeObserver(function(){
document.querySelector('button[onclick="ResizeFrame()"]').textContent = (IframeMainBox.style.length ? '↘️(~) Size' : '↘️ Size');
})).observe(IframeMainBox);
window.onload = function(){ window.onhashchange = (function(){ location.reload() });
window.onload = (function(){
Array.from(document.querySelectorAll('noscript, .NoScript')).forEach(function(el){ el.remove() }); Array.from(document.querySelectorAll('noscript, .NoScript')).forEach(function(el){ el.remove() });
AppData = (JSON.parse(localStorage.getItem('org.eu.octt.FramesBrowser.v1')) || {}); AppData = (JSON.parse(localStorage.getItem('org.eu.octt.FramesBrowser.v1')) || {});
SesAppData = { SesAppData = {
@ -562,7 +734,7 @@
} }
} }
} }
}; });
</script> </script>
</body> </body>
</html> </html>