Move FramesBrowser to built apps, update, add dependencies; Update build process; Add TiVuocto icon

This commit is contained in:
octospacc 2024-09-14 23:17:03 +02:00
parent 6372cfe2fe
commit 3e5a65ff33
17 changed files with 330 additions and 68 deletions

View File

@ -12,6 +12,15 @@ getMetaAttr(){
grep '<meta '"$key"'="'"$name"'"' "$file" | grep '>' | cut -d '"' -f4 grep '<meta '"$key"'="'"$name"'"' "$file" | grep '>' | cut -d '"' -f4
} }
################################################################################
npm update
npm install
cd ./node_modules/SpaccDotWeb
npm install
npm run build:lib
cd ../..
rm -vrf ./public || true rm -vrf ./public || true
cp -vr ./static ./public cp -vr ./static ./public
cp -vr ./shared ./public/shared cp -vr ./shared ./public/shared
@ -21,9 +30,9 @@ do
mkdir -p "./public/${App}" mkdir -p "./public/${App}"
cd "./source/${App}" cd "./source/${App}"
if [ -f ./Requirements.sh ] if [ -f ./Requirements.sh ]
then sh ./Requirements.sh then sh ./Requirements.sh
else elif [ -f ./package.json ]
[ -f ./package.json ] && (npm update; npm install) then (npm update; npm install)
fi fi
copyfiles="$(sh ./Build.sh)" copyfiles="$(sh ./Build.sh)"
cp -vr $copyfiles "../../public/${App}/" cp -vr $copyfiles "../../public/${App}/"
@ -42,13 +51,17 @@ node ../WriteRedirectPages.js
for App in ${HubSdkApps} for App in ${HubSdkApps}
do do
file="./${App}/index.html" htmlfile="./${App}/index.html"
name="$( getMetaAttr "${file}" og:title)" jsonfile="./${App}/WebManifest.json"
description="$(getMetaAttr "${file}" og:description)" if [ -f "${jsonfile}" ]
url="$( getMetaAttr "${file}" OctoSpaccHubSdk:Url)" then continue
cat << [OctoSpaccHubSdk-WebManifest-EOF] > "./${App}/WebManifest.json" fi
name="$( getMetaAttr "${htmlfile}" og:title)"
description="$(getMetaAttr "${htmlfile}" og:description)"
url="$( getMetaAttr "${htmlfile}" OctoSpaccHubSdk:Url)"
cat << [OctoSpaccHubSdk-WebManifest-EOF] > "${jsonfile}"
{ {
$(getMetaAttr "${file}" OctoSpaccHubSdk:WebManifestExtra | sed s/\'/\"/g) $(getMetaAttr "${htmlfile}" OctoSpaccHubSdk:WebManifestExtra | sed s/\'/\"/g)
$([ -n "${description}" ] && echo "$(quoteVar description): $(quoteVar "${description}"),") $([ -n "${description}" ] && echo "$(quoteVar description): $(quoteVar "${description}"),")
"start_url": "${url}", "start_url": "${url}",
"scope": "${url}", "scope": "${url}",
@ -57,5 +70,5 @@ do
[OctoSpaccHubSdk-WebManifest-EOF] [OctoSpaccHubSdk-WebManifest-EOF]
htmltitle='<title>'"${name}"'</title>' htmltitle='<title>'"${name}"'</title>'
htmlcanonical='<link rel="canonical" href="'"${url}"'"/>' htmlcanonical='<link rel="canonical" href="'"${url}"'"/>'
sed -i 's|</head>|<link rel="manifest" href="./WebManifest.json"/>'"${htmltitle}${htmlcanonical}${htmlmanifest}${HtmlHeadInject}"'</head>|' "${file}" sed -i 's|</head>|<link rel="manifest" href="./WebManifest.json"/>'"${htmltitle}${htmlcanonical}${htmlmanifest}${HtmlHeadInject}"'</head>|' "${htmlfile}"
done done

1
WriteRedirectPages.js Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env node
const fs = require('fs'); const fs = require('fs');
for (const page of [ for (const page of [
{ path: "a/fb", target: "'../../FramesBrowser/'+location.hash" }, // Apps/FramesBrowser { path: "a/fb", target: "'../../FramesBrowser/'+location.hash" }, // Apps/FramesBrowser

39
package-lock.json generated
View File

@ -7,7 +7,8 @@
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.23.9", "@babel/cli": "^7.23.9",
"@babel/core": "^7.23.9", "@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9" "@babel/preset-env": "^7.23.9",
"SpaccDotWeb": "gitlab:SpaccInc/SpaccDotWeb"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
@ -2324,6 +2325,27 @@
"semver": "bin/semver" "semver": "bin/semver"
} }
}, },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -2367,6 +2389,12 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/parse-multipart-data": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/parse-multipart-data/-/parse-multipart-data-1.5.0.tgz",
"integrity": "sha512-ck5zaMF0ydjGfejNMnlo5YU2oJ+pT+80Jb1y4ybanT27j+zbVP/jkYmCrUGsEln0Ox/hZmuvgy8Ra7AxbXP2Mw==",
"dev": true
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -2529,6 +2557,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/SpaccDotWeb": {
"version": "indev",
"resolved": "git+ssh://git@gitlab.com/SpaccInc/SpaccDotWeb.git#9b23a57eeb50f32627cdd182070eb722118c1554",
"dev": true,
"dependencies": {
"mime-types": "^2.1.35",
"parse-multipart-data": "^1.5.0"
}
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@ -2,6 +2,7 @@
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.23.9", "@babel/cli": "^7.23.9",
"@babel/core": "^7.23.9", "@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9" "@babel/preset-env": "^7.23.9",
"SpaccDotWeb": "gitlab:SpaccInc/SpaccDotWeb"
} }
} }

1
source/FramesBrowser/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/html-data-url-loader-?.html

7
source/FramesBrowser/Build.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
for i in 0 1
do cp ./html-data-url-loader.html "./html-data-url-loader-${i}.html"
done
#echo "const fs=require('fs'); fs.writeFileSync('html2canvas.min.wrappedLib.js', 'window.FramesBrowser.Lib.html2canvas=' + JSON.stringify(fs.readFileSync('node_modules/html2canvas/dist/html2canvas.min.js', 'utf8')) + ';');" | node
echo index.html utils.js WebManifest.json icon.png html-data-url-loader-?.html \
node_modules/html2canvas/dist/html2canvas.min.js

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<script src="./utils.js"></script>
</head>
<body>
<script>(function(){
//window.addEventListener('load', (function(){
var [mime, body] = extractDataUrl(location.hash.slice(1)/*.split('#').slice(2).join('#')*/);
var dom = (new DOMParser).parseFromString(body, mime);
document.documentElement.innerHTML = dom.documentElement.innerHTML;
document.head.innerHTML = dom.head.innerHTML;
document.body.innerHTML = dom.body.innerHTML;
// hydrate scripts; TODO handle all attributes to copy
Array.from(document.querySelectorAll('script')).forEach(function(oldScriptEl){
newScriptEl = Object.assign(document.createElement('script'), {
id: oldScriptEl.id,
className: oldScriptEl.className,
innerHTML: oldScriptEl.innerHTML,
});
if (oldScriptEl.src) {
newScriptEl.src = oldScriptEl.src;
}
oldScriptEl.replaceWith(newScriptEl);
});
//}));
})();</script>
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -2,14 +2,16 @@
<!-- TODO: <!-- TODO:
* deprecate old pseudo-framework and use SpaccDotWeb * deprecate old pseudo-framework and use SpaccDotWeb
* options menu/zone? * options menu/zone?
* js/css injection? * tools for js/css injection
* open file via drag&drop * open file via drag&drop
* windowing system * multiple frames loaded simoultaneously
* windowing system
* reordering tabs * reordering tabs
* automatically add useful meta tags to injected HTML data URIs? * automatically add useful meta tags to injected HTML data URIs?
* URL hash parameters documentation and more features, also implement extended syntax mode * URL hash parameters documentation and more features, also implement extended syntax mode
* investigate bug on Firefox Android with bottom navbar covering part of frame?
* full-screen edit of HTML data URIs? (probably better to not include this and make a dedicated app) * full-screen edit of HTML data URIs? (probably better to not include this and make a dedicated app)
* loading notices
* decide how to handle resized iframe border offset dimensions
--> -->
<html lang="en"> <html lang="en">
<head> <head>
@ -19,7 +21,7 @@
<link rel="canonical" href="https://hub.octt.eu.org/FramesBrowser/"/> <link rel="canonical" href="https://hub.octt.eu.org/FramesBrowser/"/>
<link rel="shortcut icon" type="image/x-icon" href="./icon.png"/> <link rel="shortcut icon" type="image/x-icon" href="./icon.png"/>
<link rel="apple-touch-icon" href="./icon.png"/> <link rel="apple-touch-icon" href="./icon.png"/>
<link rel="manifest" href="./manifest.json"/> <link rel="manifest" href="./WebManifest.json"/>
<title>🪟️ Frames Browser</title> <title>🪟️ Frames Browser</title>
<meta name="description" content="iFrame-based HTML5 Browser for fun and development"/> <meta name="description" content="iFrame-based HTML5 Browser for fun and development"/>
<meta property="og:title" content="🪟️ Frames Browser"/> <meta property="og:title" content="🪟️ Frames Browser"/>
@ -29,7 +31,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)); --BtnActionWidth: calc(3rem + var(--BaseMargin));
--ColorBg: #f0f0f0; --ColorBg: #f0f0f0;
--ColorFg: #0f0f0f; --ColorFg: #0f0f0f;
} }
@ -39,11 +41,15 @@
body { body {
margin: 0px; margin: 0px;
max-width: 100vw; max-width: 100vw;
max-height: 100vh; max-height: 100%;
position: absolute;
} }
button { button {
height: var(--BtnHeight); height: var(--BtnHeight);
} }
#MainAppContent {
height: 100%;
}
iframe, #IframeMain, #IframeMainBox { iframe, #IframeMain, #IframeMainBox {
border: none; border: none;
width: 100%; width: 100%;
@ -111,7 +117,10 @@
} }
</style> </style>
<script src="../../shared/OctoHub-Global.js"></script> <script src="../../shared/OctoHub-Global.js"></script>
<script src="../../../SpaccDotWeb/SpaccDotWeb.Alt.js"></script><!--dev-->
<script src="../../node_modules/SpaccDotWeb/Build/SpaccDotWeb.Alt.min.js"></script><!--prod-->
<script> window.FramesBrowser = { Lib: {} }; </script> <script> window.FramesBrowser = { Lib: {} }; </script>
<script src="./utils.js"></script>
</head> </head>
<body> <body>
<button id="BtnFullscreen" onclick="ToggleFullscreen()">🎞️ Menu</button> <button id="BtnFullscreen" onclick="ToggleFullscreen()">🎞️ Menu</button>
@ -129,7 +138,7 @@
<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('Screenshot')">📷 Shot</button></td>
<td><button onclick="FrameDispatch('Print')">🖨️ Print</button></td> <td><button onclick="FrameDispatch('Print')">🖨️ Print</button></td>
<tr></table></div> <tr></table></div>
<div id="BoxHandy"></div> <div id="BoxHandy"></div>
@ -168,6 +177,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-14</h3><ul>
<li>Fix mobile browsers' UI shifting in place, causing part of the bottom of the iFrame to be hidden.</li>
<li>Handle code injection in frames efficiently, by externally loading library dependencies.</li>
<li>Always show modal with screenshot preview after taking one, show download button instead of automatically downloading.</li>
</ul>
<h3>2024-09-13</h3><ul> <h3>2024-09-13</h3><ul>
<li>Better normalized the action buttons' width.</li> <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>Allow resizing frames (and then resetting the size), by hand or by inputting numbers.</li>
@ -215,7 +229,7 @@
}) })
</script> </script>
</div></div> </div></div>
<script src="./html2canvas.min.wrappedLib.js"></script> <!--<script src="./html2canvas.min.wrappedLib.js"></script>-->
<script> <script>
var AppData, SesAppData, SesAppDataBak; var AppData, SesAppData, SesAppDataBak;
function SaveAppData(){ function SaveAppData(){
@ -229,9 +243,15 @@ MainAppContent.innerHTML = `<div id="IframeMainContainer">
<iframe id="IframeMain"></iframe> <iframe id="IframeMain"></iframe>
</div> </div>
</div>`; </div>`;
document.body.style.overflow = 'hidden'; document.body.style = 'overflow: hidden; height: 100%;';
var FrameHtmlInjectable = ('<scr' + 'ipt> window.FramesBrowser = ' + JSON.stringify(window.FramesBrowser) + ' ; (' + (function(){ var htmlInjectPrefix = ((location.protocol === 'file:') ? '.' : location.href.split('#')[0].split('/').slice(0, -1).join('/'));
var FrameHtmlInjectable = (`
<${'script'} src="../../../SpaccDotWeb/SpaccDotWeb.Alt.js"></${'script'}><!--dev-->
<${'script'} src="${htmlInjectPrefix}/../../node_modules/SpaccDotWeb/Build/SpaccDotWeb.Alt.min.js"></${'script'}><!--prod-->
<${'script'} src="${htmlInjectPrefix}/node_modules/html2canvas/dist/html2canvas.min.js"></${'script'}>
<${'script'}> window.FramesBrowser = ${JSON.stringify(window.FramesBrowser)};
(` + (function(){
for (var name in window.FramesBrowser.Lib) { for (var name in window.FramesBrowser.Lib) {
var content = window.FramesBrowser.Lib[name]; var content = window.FramesBrowser.Lib[name];
if ((typeof content) === 'string') { if ((typeof content) === 'string') {
@ -247,19 +267,49 @@ var FrameHtmlInjectable = ('<scr' + 'ipt> window.FramesBrowser = ' + JSON.string
window.FramesBrowser.Screenshot = (function(){ window.FramesBrowser.Screenshot = (function(){
// TODO must somehow load images in canvas with cors proxy or it becomes tainted and undownloadable // 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 // for now just show on screen and let user download via browser actions
function postFinished () {
window.parent.postMessage({ FramesBrowser: "Screenshot:0" }, '*');
}
window.parent.postMessage({ FramesBrowser: "Screenshot:1" }, '*');
html2canvas(document.body, { allowTaint: true }).then(function(canvas){ html2canvas(document.body, { allowTaint: true }).then(function(canvas){
try { /*try {
Object.assign(document.createElement('a'), { Object.assign(document.createElement('a'), {
download: `FramesBrowser-Screenshot-${(new Date).toJSON()}`, download: `FramesBrowser-Screenshot-${(new Date).toJSON()}`,
href: canvas.toDataURL('image/png'), href: canvas.toDataURL('image/png'),
}).click(); }).click();
} catch(err) { } catch(err) {
console.error(err); console.error(err);
var modalEl = document.createElement('dialog'); SpaccDotWeb.ShowModal({
document.body.appendChild(modalEl); text: "Use the browser's context menu to download the image.",
modalEl.appendChild(canvas); extraHTML: `<div style="overflow: auto;"></div>`,
modalEl.showModal(); }).then(function(modalEl){
modalEl.querySelector('div').appendChild(canvas);
});
}*/
var imageData;
try {
imageData = canvas.toDataURL('image/png');
} catch(err) {
console.error(err);
} }
SpaccDotWeb.ShowModal({
text: ((!imageData) && "Use the browser's context menu to download the image."),
action: (imageData && (function(){
Object.assign(document.createElement('a'), {
download: `FramesBrowser-Screenshot-${(new Date).toJSON()}`,
href: imageData,
}).click();
})),
confirmText: '📥️ Download',
extraHTML: '<div style="overflow: auto;"></div>',
buttonsPosition: 'top',
}).then(function(modalEl){
modalEl.querySelector('div').appendChild(canvas);
});
postFinished();
}).catch(function(err){
console.error(err);
postFinished();
}); });
}); });
window.addEventListener('message', (function(messageEv){ window.addEventListener('message', (function(messageEv){
@ -268,7 +318,7 @@ var FrameHtmlInjectable = ('<scr' + 'ipt> window.FramesBrowser = ' + JSON.string
FramesBrowser[action](); FramesBrowser[action]();
} }
})); }));
}) + ')(); </scr' + 'ipt>'); }) + `)(); </${'script'}>`);
function $new (tag, props) { function $new (tag, props) {
var el = document.createElement(tag); var el = document.createElement(tag);
@ -305,17 +355,9 @@ 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) { // TODO fallback to no-inject loading, or data: inject loading, in case of failure (mainly regarding the external domain)
var isFrameRoot = (index === -1); var htmlInjectIndex = 0;
var url = (GetTabUrlFromTabIndex(index) || ''); function makeRealFrameUrl (url, isFrameRoot) {
ListFramesClose();
AppData.currentTabIndex = index;
SaveAppData();
InputUri.disabled = isFrameRoot;
InputUri.value = url;
BtnFile.disabled = isFrameRoot;
BtnLoad.disabled = isFrameRoot;
BtnExcise.disabled = isFrameRoot;
if (isFrameRoot) { if (isFrameRoot) {
url = createDataUrl('text/html', SampleHtmlContent, 'utf8'); url = createDataUrl('text/html', SampleHtmlContent, 'utf8');
} }
@ -324,16 +366,54 @@ function ShowFrame (index) {
if (mime.toLowerCase() !== 'text/html') { if (mime.toLowerCase() !== 'text/html') {
return url; return url;
} }
//url = (((location.protocol === 'file:') ? '.' /* `./${Date.now()}/..` */ : 'http://data-sandbox.octt.eu.org')
// + /* `/html-data-url-loader.html#${Date.now()}#` */ `/html-data-url-loader-${htmlInjectIndex = Math.abs(1- htmlInjectIndex)}.html#`
// + createDataUrl(mime, patchFrameHtml(body), ((location.protocol === 'file:') ? (encoding || 'utf8') : 'base64'), (location.protocol !== 'file:')));
url = createDataUrl(mime, patchFrameHtml(body), (encoding || 'utf8')); url = createDataUrl(mime, patchFrameHtml(body), (encoding || 'utf8'));
if (location.protocol === 'file:') {
url = `./html-data-url-loader-${htmlInjectIndex = Math.abs(1- htmlInjectIndex)}.html#${url}`;
}
} }
document.querySelector('iframe').src = url; return url;
}
// TODO check why on Chromium mobile this here is not enough, force-reloading any page breaks the iFrame and requires page reload
function loadUrlInFrame (url) {
IframeMain.src = ''; // needed for URLs with hashes
IframeMain.src = url;
/* // (Firefox issue?) sometimes wrapper page isn't loaded properly and iframe remains blank, we fix it here
if (url === 'about:blank') {
return;
}
var loadInterval = setInterval((function(){
try {
if (IframeMain.contentWindow.location.toString() === 'about:blank') {
IframeMain.src = url;
} else {
throw '';
}
} catch(err) {
clearInterval(loadInterval);
}
}), 100);*/
}
function ShowFrame (index) {
var isFrameRoot = (index === -1);
var url = (GetTabUrlFromTabIndex(index) || '');
ListFramesClose();
AppData.currentTabIndex = index;
SaveAppData();
InputUri.disabled = BtnFile.disabled = BtnLoad.disabled = BtnExcise.disabled = isFrameRoot;
InputUri.value = url;
loadUrlInFrame(makeRealFrameUrl(url, isFrameRoot));
} }
function SaveUrl (url) { function SaveUrl (url) {
if (url) { if (url) {
document.querySelector('input[type="text"]').value = url; InputUri.value = url;
} }
url = document.querySelector('input[type="text"]').value; url = InputUri.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
@ -396,7 +476,7 @@ function RefreshFramesCounter () {
} }
function GetNeededIframeHeight (hScale=100) { function GetNeededIframeHeight (hScale=100) {
return (SesAppData.fullscreen ? `${hScale}vh` : `calc(${hScale}vh - (var(--BtnActionHeight) * ${hScale / 100}))`); return (SesAppData.fullscreen ? `${hScale}%` : `calc(${hScale}% - (var(--BtnActionHeight) * ${hScale / 100}))`);
} }
function ApplyFullscreen () { function ApplyFullscreen () {
@ -417,9 +497,9 @@ function ToggleFullscreen () {
} }
function LoadFrame () { function LoadFrame () {
var url = document.querySelector('input[type="text"]').value; var url = InputUri.value;
if (!url.toLowerCase().startsWith('javascript:')) { if (!url.toLowerCase().startsWith('javascript:')) {
document.querySelector('iframe').src = SaveUrl(); loadUrlInFrame(makeRealFrameUrl(SaveUrl(), false));
} }
} }
@ -427,26 +507,21 @@ function isDataUrl (url) {
return url.toLowerCase().startsWith('data:'); return url.toLowerCase().startsWith('data:');
} }
function extractDataUrl (url) { function createDataUrl (mime, body, encoding, reverse) {
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: switch (encoding) { default:
break; case 'utf8': break; case 'utf8':
body = encodeURIComponent(body); body = encodeURIComponent(body);
break; case 'base64': break; case 'base64':
body = btoa(body); body = btoa(unescape(encodeURIComponent(body)));
//break; case '46esab':
// body = reverseString(btoa(unescape(encodeURIComponent(body))));
} }
return `data:${mime};${encoding},${body}`; if (reverse) {
mime = reverseString(mime);
body = reverseString(body);
encoding = reverseString(encoding);
}
return `${!reverse ? 'data' : 'atad'}:${mime};${encoding},${body}`;
} }
function patchFrameHtml (html) { function patchFrameHtml (html) {
@ -476,7 +551,7 @@ function ExciseFrame () {
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; InputUri.value = reader.result;
LoadFrame(); LoadFrame();
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
@ -489,10 +564,10 @@ function ApplyFrameZoom () {
IframeMainContainer.style[prop] = ''; IframeMainContainer.style[prop] = '';
}); });
if (level < 100) { if (level < 100) {
IframeMainContainer.style.bottom = `calc(${level}vh - 16px)`; IframeMainContainer.style.bottom = `calc(${level}% - 16px)`;
IframeMainContainer.style.right = `${level}vw`; IframeMainContainer.style.right = `${level}vw`;
} else { } else {
IframeMainContainer.style.top = `calc(${levelopp/2}vh - 8px)`; IframeMainContainer.style.top = `calc(${levelopp/2}% - 8px)`;
IframeMainContainer.style.left = `${levelopp/2}vw`; IframeMainContainer.style.left = `${levelopp/2}vw`;
} }
if (SesAppData.frameZoomIndex !== -1) { if (SesAppData.frameZoomIndex !== -1) {
@ -686,7 +761,7 @@ window.onload = (function(){
}; };
AlertMigrateAppData(); AlertMigrateAppData();
SaveAppData(); SaveAppData();
document.querySelector('input[type="text"]').value = GetTabUrlFromTabIndex(AppData.currentTabIndex); InputUri.value = GetTabUrlFromTabIndex(AppData.currentTabIndex);
ApplyFullscreen(); ApplyFullscreen();
ApplyFrameZoom(); ApplyFrameZoom();
ShowFrame(AppData.currentTabIndex); ShowFrame(AppData.currentTabIndex);
@ -721,7 +796,7 @@ window.onload = (function(){
var url = ((optLow.startsWith('h=') ? 'data:text/html;utf8,' : '') + fieldData); var url = ((optLow.startsWith('h=') ? 'data:text/html;utf8,' : '') + fieldData);
if (GetTabUrlFromTabIndex(AppData.currentTabIndex) !== url) { if (GetTabUrlFromTabIndex(AppData.currentTabIndex) !== url) {
AddFrame(); AddFrame();
document.querySelector('input[type="text"]').value = url; InputUri.value = url;
LoadFrame(); LoadFrame();
} }
flags.immersiveView && !SesAppData.fullscreen && ToggleFullscreen(); flags.immersiveView && !SesAppData.fullscreen && ToggleFullscreen();
@ -735,6 +810,14 @@ window.onload = (function(){
} }
} }
}); });
window.addEventListener('message', (function(messageEv){
var data = (messageEv.data && messageEv.data.FramesBrowser);
if (data) {
data = data.split(':');
document.querySelector(`button[onclick="FrameDispatch('${data[0]}')"]`).disabled = parseInt(data[1]);
}
}));
</script> </script>
</body> </body>
</html> </html>

56
source/FramesBrowser/package-lock.json generated Normal file
View File

@ -0,0 +1,56 @@
{
"name": "tmp",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"html2canvas": "^1.4.1"
}
},
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
}
}
}

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"html2canvas": "^1.4.1"
}
}

View File

@ -0,0 +1,24 @@
function reverseString (string) {
return string.split('').reverse().join('');
}
function extractDataUrl (url) {
var head = url.split(',')[0];
var meta = head.split(':')[1].split(';');
var mime = meta[0], encoding = meta[1];
var body = url.split(',').slice(1).join(',');
if (head.split(':')[0] === 'atad') {
mime = reverseString(mime);
body = reverseString(body);
encoding = reverseString(encoding);
}
switch ((encoding || '').toLowerCase()) { default:
break; case 'utf8':
body = decodeURIComponent(body);
break; case 'base64':
body = decodeURIComponent(escape(atob(body)));
//break; case '46esab':
// body = decodeURIComponent(escape(atob(reverseString(body))));
}
return [mime, body, encoding];
}

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
echo index.html menu.svg \ echo index.html icon.jpeg menu.svg \
node_modules/muicss/dist/css/mui.min.css node_modules/muicss/dist/js/mui.min.js \ node_modules/muicss/dist/css/mui.min.css node_modules/muicss/dist/js/mui.min.js \
node_modules/video.js/dist/video-js.min.css node_modules/video.js/dist/video.min.js \ node_modules/video.js/dist/video-js.min.css node_modules/video.js/dist/video.min.js \
; ;

BIN
source/TiVuOcto/icon.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -6,7 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta property="og:title" content="TiVuOcto 📺️"/> <meta property="og:title" content="TiVuOcto 📺️"/>
<meta property="OctoSpaccHubSdk:Url" content="https://hub.octt.eu.org/TiVuOcto/"/> <meta property="OctoSpaccHubSdk:Url" content="https://hub.octt.eu.org/TiVuOcto/"/>
<meta property="OctoSpaccHubSdk:WebManifestExtra" content="'display':'standalone',"/> <meta property="OctoSpaccHubSdk:WebManifestExtra" content="'display':'standalone', 'icons':[{ 'src':'./icon.jpeg', 'type':'image/jpeg', 'sizes':'1024x1024' }],"/>
<link rel="apple-touch-icon" href="./icon.jpeg"/>
<link rel="stylesheet" href="./node_modules/muicss/dist/css/mui.min.css"/> <link rel="stylesheet" href="./node_modules/muicss/dist/css/mui.min.css"/>
<link rel="stylesheet" href="./node_modules/video.js/dist/video-js.min.css"/> <link rel="stylesheet" href="./node_modules/video.js/dist/video-js.min.css"/>
<style> <style>

File diff suppressed because one or more lines are too long