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
}
################################################################################
npm update
npm install
cd ./node_modules/SpaccDotWeb
npm install
npm run build:lib
cd ../..
rm -vrf ./public || true
cp -vr ./static ./public
cp -vr ./shared ./public/shared
@ -21,9 +30,9 @@ do
mkdir -p "./public/${App}"
cd "./source/${App}"
if [ -f ./Requirements.sh ]
then sh ./Requirements.sh
else
[ -f ./package.json ] && (npm update; npm install)
then sh ./Requirements.sh
elif [ -f ./package.json ]
then (npm update; npm install)
fi
copyfiles="$(sh ./Build.sh)"
cp -vr $copyfiles "../../public/${App}/"
@ -42,13 +51,17 @@ node ../WriteRedirectPages.js
for App in ${HubSdkApps}
do
file="./${App}/index.html"
name="$( getMetaAttr "${file}" og:title)"
description="$(getMetaAttr "${file}" og:description)"
url="$( getMetaAttr "${file}" OctoSpaccHubSdk:Url)"
cat << [OctoSpaccHubSdk-WebManifest-EOF] > "./${App}/WebManifest.json"
htmlfile="./${App}/index.html"
jsonfile="./${App}/WebManifest.json"
if [ -f "${jsonfile}" ]
then continue
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}"),")
"start_url": "${url}",
"scope": "${url}",
@ -57,5 +70,5 @@ do
[OctoSpaccHubSdk-WebManifest-EOF]
htmltitle='<title>'"${name}"'</title>'
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

1
WriteRedirectPages.js Normal file → Executable file
View File

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

39
package-lock.json generated
View File

@ -7,7 +7,8 @@
"devDependencies": {
"@babel/cli": "^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": {
@ -2324,6 +2325,27 @@
"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": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -2367,6 +2389,12 @@
"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": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -2529,6 +2557,15 @@
"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": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@ -2,6 +2,7 @@
"devDependencies": {
"@babel/cli": "^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:
* deprecate old pseudo-framework and use SpaccDotWeb
* options menu/zone?
* js/css injection?
* tools for js/css injection
* open file via drag&drop
* windowing system
* multiple frames loaded simoultaneously
* windowing system
* reordering tabs
* automatically add useful meta tags to injected HTML data URIs?
* 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)
* loading notices
* decide how to handle resized iframe border offset dimensions
-->
<html lang="en">
<head>
@ -19,7 +21,7 @@
<link rel="canonical" href="https://hub.octt.eu.org/FramesBrowser/"/>
<link rel="shortcut icon" type="image/x-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>
<meta name="description" content="iFrame-based HTML5 Browser for fun and development"/>
<meta property="og:title" content="🪟️ Frames Browser"/>
@ -29,7 +31,7 @@
--BaseMargin: 8px;
--BtnHeight: calc(1rem + var(--BaseMargin));
--BtnActionHeight: calc(2rem + var(--BaseMargin));
--BtnActionWidth: calc(2.5rem + var(--BaseMargin));
--BtnActionWidth: calc(3rem + var(--BaseMargin));
--ColorBg: #f0f0f0;
--ColorFg: #0f0f0f;
}
@ -39,11 +41,15 @@
body {
margin: 0px;
max-width: 100vw;
max-height: 100vh;
max-height: 100%;
position: absolute;
}
button {
height: var(--BtnHeight);
}
#MainAppContent {
height: 100%;
}
iframe, #IframeMain, #IframeMainBox {
border: none;
width: 100%;
@ -111,7 +117,10 @@
}
</style>
<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 src="./utils.js"></script>
</head>
<body>
<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><button id="BtnLoad" onclick="LoadFrame()">↩️ Load</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>
<tr></table></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>
</ul>
<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>
<li>Better normalized the action buttons' width.</li>
<li>Allow resizing frames (and then resetting the size), by hand or by inputting numbers.</li>
@ -215,7 +229,7 @@
})
</script>
</div></div>
<script src="./html2canvas.min.wrappedLib.js"></script>
<!--<script src="./html2canvas.min.wrappedLib.js"></script>-->
<script>
var AppData, SesAppData, SesAppDataBak;
function SaveAppData(){
@ -229,9 +243,15 @@ MainAppContent.innerHTML = `<div id="IframeMainContainer">
<iframe id="IframeMain"></iframe>
</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) {
var content = window.FramesBrowser.Lib[name];
if ((typeof content) === 'string') {
@ -247,19 +267,49 @@ var FrameHtmlInjectable = ('<scr' + 'ipt> window.FramesBrowser = ' + JSON.string
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
function postFinished () {
window.parent.postMessage({ FramesBrowser: "Screenshot:0" }, '*');
}
window.parent.postMessage({ FramesBrowser: "Screenshot:1" }, '*');
html2canvas(document.body, { allowTaint: true }).then(function(canvas){
try {
/*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();
SpaccDotWeb.ShowModal({
text: "Use the browser's context menu to download the image.",
extraHTML: `<div style="overflow: auto;"></div>`,
}).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){
@ -268,7 +318,7 @@ var FrameHtmlInjectable = ('<scr' + 'ipt> window.FramesBrowser = ' + JSON.string
FramesBrowser[action]();
}
}));
}) + ')(); </scr' + 'ipt>');
}) + `)(); </${'script'}>`);
function $new (tag, props) {
var el = document.createElement(tag);
@ -305,17 +355,9 @@ function GetTabUrlFromTabIndex (index) {
return (index === -1 ? null : AppData.urls[AppData.tabs[index].urlIndex]);
}
function ShowFrame (index) {
var isFrameRoot = (index === -1);
var url = (GetTabUrlFromTabIndex(index) || '');
ListFramesClose();
AppData.currentTabIndex = index;
SaveAppData();
InputUri.disabled = isFrameRoot;
InputUri.value = url;
BtnFile.disabled = isFrameRoot;
BtnLoad.disabled = isFrameRoot;
BtnExcise.disabled = isFrameRoot;
// TODO fallback to no-inject loading, or data: inject loading, in case of failure (mainly regarding the external domain)
var htmlInjectIndex = 0;
function makeRealFrameUrl (url, isFrameRoot) {
if (isFrameRoot) {
url = createDataUrl('text/html', SampleHtmlContent, 'utf8');
}
@ -324,16 +366,54 @@ function ShowFrame (index) {
if (mime.toLowerCase() !== 'text/html') {
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'));
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) {
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);
if (urlIndex === -1) {
// it's a new url, store it
@ -396,7 +476,7 @@ function RefreshFramesCounter () {
}
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 () {
@ -417,9 +497,9 @@ function ToggleFullscreen () {
}
function LoadFrame () {
var url = document.querySelector('input[type="text"]').value;
var url = InputUri.value;
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:');
}
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) {
function createDataUrl (mime, body, encoding, reverse) {
switch (encoding) { default:
break; case 'utf8':
body = encodeURIComponent(body);
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) {
@ -476,7 +551,7 @@ function ExciseFrame () {
function LoadFile (file) {
var reader = new FileReader();
reader.onload = function(){
document.querySelector('input[type="text"]').value = reader.result;
InputUri.value = reader.result;
LoadFrame();
};
reader.readAsDataURL(file);
@ -489,10 +564,10 @@ function ApplyFrameZoom () {
IframeMainContainer.style[prop] = '';
});
if (level < 100) {
IframeMainContainer.style.bottom = `calc(${level}vh - 16px)`;
IframeMainContainer.style.bottom = `calc(${level}% - 16px)`;
IframeMainContainer.style.right = `${level}vw`;
} else {
IframeMainContainer.style.top = `calc(${levelopp/2}vh - 8px)`;
IframeMainContainer.style.top = `calc(${levelopp/2}% - 8px)`;
IframeMainContainer.style.left = `${levelopp/2}vw`;
}
if (SesAppData.frameZoomIndex !== -1) {
@ -686,7 +761,7 @@ window.onload = (function(){
};
AlertMigrateAppData();
SaveAppData();
document.querySelector('input[type="text"]').value = GetTabUrlFromTabIndex(AppData.currentTabIndex);
InputUri.value = GetTabUrlFromTabIndex(AppData.currentTabIndex);
ApplyFullscreen();
ApplyFrameZoom();
ShowFrame(AppData.currentTabIndex);
@ -721,7 +796,7 @@ window.onload = (function(){
var url = ((optLow.startsWith('h=') ? 'data:text/html;utf8,' : '') + fieldData);
if (GetTabUrlFromTabIndex(AppData.currentTabIndex) !== url) {
AddFrame();
document.querySelector('input[type="text"]').value = url;
InputUri.value = url;
LoadFrame();
}
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>
</body>
</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
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/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 property="og:title" content="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/video.js/dist/video-js.min.css"/>
<style>

File diff suppressed because one or more lines are too long