Move FramesBrowser to built apps, update, add dependencies; Update build process; Add TiVuocto icon
This commit is contained in:
parent
6372cfe2fe
commit
3e5a65ff33
33
Build.sh
33
Build.sh
|
@ -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,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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/html-data-url-loader-?.html
|
|
@ -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
|
|
@ -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>
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
@ -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>
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"html2canvas": "^1.4.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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];
|
||||||
|
}
|
|
@ -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 \
|
||||||
;
|
;
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
|
@ -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
Loading…
Reference in New Issue