From e30c7241705f68c2b8b314ddc00d97038e737ea2 Mon Sep 17 00:00:00 2001 From: Kroese Date: Sun, 21 Jan 2024 18:50:57 +0100 Subject: [PATCH] feat: Dynamic content (#362) --- readme.md | 2 +- src/config.sh | 1 + src/reset.sh | 8 +-- web/css/style.css | 154 ++++++++++++++++++++++++++++++++++++++++++++ web/img/favicon.svg | 1 + web/index.html | 22 +++++-- web/js/script.js | 120 ++++++++++++++++++++++++++++++++++ web/style.css | 59 ----------------- 8 files changed, 296 insertions(+), 71 deletions(-) create mode 100644 web/css/style.css create mode 100644 web/img/favicon.svg create mode 100644 web/js/script.js delete mode 100644 web/style.css diff --git a/readme.md b/readme.md index 5d60483..9941c9b 100644 --- a/readme.md +++ b/readme.md @@ -57,7 +57,7 @@ docker run -it --rm -e "BOOT=http://example.com/image.iso" -p 8006:8006 --device - Set the `BOOT` environment variable to the URL of an ISO image you want to install. - - Start the container and connect to port 8006 in your web browser. + - Start the container and connect to [port 8006](http://localhost:8006) in your web browser. - You will see the screen and can control it via the keyboard and mouse. diff --git a/src/config.sh b/src/config.sh index e105b8b..b4de4b1 100644 --- a/src/config.sh +++ b/src/config.sh @@ -16,6 +16,7 @@ ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OP ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ') if [[ "${DISPLAY,,}" == "web" ]]; then + rm -f /dev/shm/msg.html rm -f /dev/shm/index.html else if [[ "${DISPLAY,,}" == "vnc" ]]; then diff --git a/src/reset.sh b/src/reset.sh index 83a8974..51ddbad 100644 --- a/src/reset.sh +++ b/src/reset.sh @@ -27,6 +27,7 @@ echo # Helper variables STORAGE="/storage" +INFO="/dev/shm/msg.html" PAGE="/dev/shm/index.html" TEMPLATE="/var/www/index.html" FOOTER1="$APP for Docker v$(${body/.../}

" fi - local timeout="4999" - [ -n "${2:-}" ] && timeout="$2" - local script="" - [[ "$timeout" == "0" ]] && script="" + [ -n "${2:-}" ] && script="$2" || script="" local HTML HTML=$(<"$TEMPLATE") @@ -81,6 +80,7 @@ html() HTML="${HTML/\[5\]/$FOOTER2}" echo "$HTML" > "$PAGE" + echo "$body$script" > "$INFO" return 0 } diff --git a/web/css/style.css b/web/css/style.css new file mode 100644 index 0000000..d17bfa2 --- /dev/null +++ b/web/css/style.css @@ -0,0 +1,154 @@ +body { + color: white; + background-color: #125bdb; + font-family: Verdana, Arial, sans-serif; +} + +#content { + text-align: center; + padding: 20px; + margin-top: 50px; +} + +footer { + width: 98%; + position: fixed; + bottom: 0px; + height: 40px; + text-align: center; + color: #0c8aeb; +} + +#empty { + height: 40px; + /* Same height as footer */ +} + +a, +a:hover, +a:active, +a:visited { + color: white; +} + +footer a:link, +footer a:visited, +footer a:active { color: #0c8aeb; } +footer a:hover { color: #73e6ff; } + +.loading:after { + content: " ."; + animation: dots 1s steps(5, end) infinite; +} + +@keyframes dots { + + 0%, + 20% { + color: rgba(0, 0, 0, 0); + text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0); + } + + 40% { + color: white; + text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0); + } + + 60% { + text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0); + } + + 80%, + 100% { + text-shadow: 0.25em 0 0 white, 0.5em 0 0 white; + } +} + +.spinner_LWk7 { + animation: spinner_GWy6 1.2s linear infinite, spinner_BNNO 1.2s linear infinite +} + +.spinner_yOMU { + animation: spinner_GWy6 1.2s linear infinite, spinner_pVqn 1.2s linear infinite; + animation-delay: .15s +} + +.spinner_KS4S { + animation: spinner_GWy6 1.2s linear infinite, spinner_6uKB 1.2s linear infinite; + animation-delay: .3s +} + +.spinner_zVee { + animation: spinner_GWy6 1.2s linear infinite, spinner_Qw4x 1.2s linear infinite; + animation-delay: .45s +} + +@keyframes spinner_GWy6 { + + 0%, + 50% { + width: 9px; + height: 9px + } + + 10% { + width: 11px; + height: 11px + } +} + +@keyframes spinner_BNNO { + + 0%, + 50% { + x: 1.5px; + y: 1.5px + } + + 10% { + x: .5px; + y: .5px + } +} + +@keyframes spinner_pVqn { + + 0%, + 50% { + x: 13.5px; + y: 1.5px + } + + 10% { + x: 12.5px; + y: .5px + } +} + +@keyframes spinner_6uKB { + + 0%, + 50% { + x: 13.5px; + y: 13.5px + } + + 10% { + x: 12.5px; + y: 12.5px + } +} + +@keyframes spinner_Qw4x { + + 0%, + 50% { + x: 1.5px; + y: 13.5px + } + + 10% { + x: .5px; + y: 12.5px + } +} diff --git a/web/img/favicon.svg b/web/img/favicon.svg new file mode 100644 index 0000000..47d83d3 --- /dev/null +++ b/web/img/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/index.html b/web/index.html index 10a9faa..0f10e13 100644 --- a/web/index.html +++ b/web/index.html @@ -5,22 +5,30 @@ [1] - + + [2] -
-
-

[3]

+
+
+ + + + + + +

[3]

-
+
- +
+ diff --git a/web/js/script.js b/web/js/script.js new file mode 100644 index 0000000..eee30c7 --- /dev/null +++ b/web/js/script.js @@ -0,0 +1,120 @@ +var request; +var interval = 1000; + +function getInfo() { + + var url = "/msg.html"; + + try { + + if (window.XMLHttpRequest) { + request = new XMLHttpRequest(); + } else { + throw "XMLHttpRequest not available!"; + } + + request.onreadystatechange = processInfo; + request.open("GET", url, true); + request.send(); + + } catch (e) { + var err = "Error: " + e.message; + console.log(err); + setError(err); + } +} + +function processInfo() { + try { + + if (request.readyState != 4) { + return true; + } + + var msg = request.responseText; + if (msg == null || msg.length == 0) { + setError("Lost connection"); + schedule(); + return false; + } + + var notFound = (request.status == 404); + + if (request.status == 200) { + if (msg.toLowerCase().indexOf("") !== -1) { + notFound = true; + } else { + setInfo(msg); + schedule(); + return true; + } + } + + if (notFound) { + setInfo("Connecting to VNC", true); + reload(); + return true; + } + + setError("Error: Received statuscode " + request.status); + schedule(); + return false; + + } catch (e) { + var err = "Error: " + e.message; + console.log(err); + setError(err); + return false; + } +} + +function setInfo(msg, loading, error) { + try { + + if (msg == null || msg.length == 0) { + return false; + } + + var el = document.getElementById("spinner"); + + error = !!error; + if (!error) { + el.style.visibility = 'visible'; + } else { + el.style.visibility = 'hidden'; + } + + loading = !!loading; + if (loading) { + msg = "

" + msg + "

"; + } + + el = document.getElementById("info"); + + if (el.innerHTML != msg) { + el.innerHTML = msg; + } + + return true; + + } catch (e) { + console.log("Error: " + e.message); + return false; + } +} + +function setError(text) { + return setInfo(text, false, true); +} + +function schedule() { + setTimeout(getInfo, interval); +} + +function reload() { + setTimeout(() => { + document.location.reload(); + }, 3000); +} + +schedule(); diff --git a/web/style.css b/web/style.css deleted file mode 100644 index def18b7..0000000 --- a/web/style.css +++ /dev/null @@ -1,59 +0,0 @@ -body { - color: white; - background-color: #125bdb; - font-family: Verdana, Arial, sans-serif; -} - -#content-wrap { - text-align: center; - padding: 20px; - margin-top: 100px; -} - -#footer { - width: 98%; - position: fixed; - bottom: 0px; - height: 40px; - text-align: center; -} - -#empty-space { - height: 40px; - /* Same height as footer */ -} - -a, -a:hover, -a:active, -a:visited { - color: white; -} - -.loading:after { - content: " ."; - animation: dots 1s steps(5, end) infinite; -} - -@keyframes dots { - - 0%, - 20% { - color: rgba(0, 0, 0, 0); - text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0); - } - - 40% { - color: white; - text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0); - } - - 60% { - text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0); - } - - 80%, - 100% { - text-shadow: 0.25em 0 0 white, 0.5em 0 0 white; - } -}