def connecting():
emit("update", database_read_views(), json=True, namespace="/", broadcast=True)
+@app.route("/data", methods=["get"])
+def data():
+ return database_read_views()
+
# info screen pages
@app.route("/info/<string:screen>", methods=["get"])
from flask import render_template, request
from flask_socketio import emit
+from glob import glob
import time
+import os
from argo.database.read import read_indexes as database_read_indexes
from argo.database.read import read_views as database_read_views
from argo import STAR
def admin_main():
+ from argo import app
+ assets = glob(os.path.join(app.root_path, "static", "media", "assets", "*"))
+ assets = [path.split("argo")[-1] for path in assets]
+
return render_template(
"admin.html",
indexes=database_read_indexes(),
+ assets=assets,
star=STAR
)
})
}
}
+
+export function setupTimerButtons() {
+ // setup all timer control buttons
+ const el: HTMLCollectionOf<Element> =
+ document.getElementsByClassName("timer");
+
+ for (let e of el) {
+ e.addEventListener("click", (event) => {
+ if (event.target instanceof HTMLElement) {
+ let delay = document.getElementById(`${event.target.getAttribute('name')}+delay`);
+
+ if (delay instanceof HTMLInputElement) {
+ delay.value = eval(`${delay.value}${event.target.getAttribute('value')}`);
+ }
+ }
+ })
+ }
+}
+
+export function setupClipboard() {
+ // copy previous links href to the clipboard
+ const el: HTMLCollectionOf<Element> =
+ document.getElementsByClassName("clipboard");
+
+ for (let e of el) {
+ const button = document.createElement("button");
+ button.innerHTML = "Copy Link";
+
+ button.addEventListener("click", (event) => {
+ const button = event.target;
+ if (button instanceof HTMLElement) {
+ const link = button.previousElementSibling;
+ if (link instanceof HTMLAnchorElement) {
+ navigator.clipboard.writeText(link["href"]);
+ button.innerHTML = "Copied!";
+ setTimeout(function () {
+ button.innerHTML = "Copy Link";
+ }, 1000)
+ }
+ }
+ })
+
+ if (e.parentNode instanceof HTMLElement) {
+ e.parentNode.insertBefore(button, e.nextSibling);
+ }
+ }
+}
--- /dev/null
+export default function renderImage(): void {
+ const el: HTMLCollectionOf<Element> =
+ document.getElementsByClassName("image");
+
+ for (let e of el) {
+ if (e instanceof HTMLImageElement) {
+ if (typeof e.dataset.raw === "string") {
+ e.src = `${e.dataset.raw}?${new Date().getTime()}` // cachebreaker!
+ }
+ }
+ }
+}
--- /dev/null
+const marqueeInterval: number = 20; //ms
+
+export default function setupMarquee(): void {
+ const el: HTMLCollectionOf<Element> =
+ document.getElementsByClassName("marquee");
+
+ for (let e of el) {
+ if (e instanceof HTMLElement) {
+ e.dataset.current = "0";
+ setInterval(function() {
+ const box = e.parentNode;
+ if (box instanceof HTMLElement) {
+ let inner = e.getBoundingClientRect().right;
+ let outer = box.getBoundingClientRect().left;
+ let width = box.getBoundingClientRect().width;
+
+ if (inner - outer < 0) {
+ randomPopulate(e);
+
+ e.style = `transform: translateX(${width}px);`
+ e.dataset.current = width as unknown as string;
+ }
+
+ let position = e.dataset.current as unknown as number - 1;
+ e.style = `transform: translateX(${position}px);`
+ e.dataset.current = position as unknown as string;
+ }
+ }, marqueeInterval);
+ }
+ }
+}
+
+function randomPopulate(e: HTMLElement) {
+ if (typeof e.dataset.raw === "string") {
+ let data = eval(e.dataset.raw);
+ if (data instanceof Array) {
+ if (data.length == 0) { return }
+ else {
+ let index = randomInt(0, data.length);
+
+ while (index as unknown as string == e.dataset.index) {
+ index = randomInt(0, data.length);
+ }
+
+ e.innerHTML = data[index];
+ e.dataset.index = index as unknown as string;
+ }
+ } else if (typeof data == "string") { // UNTESTED
+ e.innerHTML = data;
+ }
+ }
+}
+
+function randomInt(min: number, max: number): number {
+ return Math.floor(Math.random() * (max - min) + min)
+}
-import { timeSince, timeUntil, timeFormatBroadcast } from "./utils.js";
+import {
+ timeSince,
+ timeUntil,
+ timeFormatBroadcast,
+ timeFormatStandard,
+ timeFormatState
+} from "./utils.js";
const timeUpdate: number = 1000; // ms
setInterval(
function () {
const el: HTMLCollectionOf<Element> =
- document.getElementsByClassName("until");
+ document.getElementsByClassName("until");
for (let e of el) {
if (e instanceof HTMLElement) {
if (typeof e.dataset.raw === "string") {
- let [hours, minutes, seconds] = timeUntil(eval(e.dataset.raw))
- e.innerHTML = timeFormatBroadcast(hours, minutes, seconds);
+ let [hours, minutes, seconds, totalSeconds] = timeUntil(eval(e.dataset.raw))
+
+ let length = e.dataset.raw.split("+")[1] as unknown as number;
+ let percent = (totalSeconds / length) * 100
+ e.style = `--percent: ${percent}%;`
+
+ // populate
+ if (e.classList.contains("until-broadcast")) { // broadcast timer
+ e.innerHTML = timeFormatBroadcast(hours, minutes, seconds);
+ } else if (e.classList.contains("until-timeout")) { // state changes when run down
+ e.dataset.timeout = timeFormatState(hours, minutes, seconds);
+ } else if (e.classList.contains("until-sound")) { // play sounds when run down
+ let timeout: string = timeFormatState(hours, minutes, seconds);
+ if (timeout == "true" && e.dataset.last == "false") {
+ if (e instanceof HTMLMediaElement) {
+ e.play();
+ }
+ }
+ e.dataset.last = timeout;
+ } else { // standard timer (MM:SS)
+ e.innerHTML = timeFormatStandard(hours, minutes, seconds);
+ }
}
}
}
import { setupSounds, playSounds } from "./sound.js";
import { setupStyle, renderStyle } from "./style.js";
import renderShowUntil from "./show_until.js";
+import setupMarquee from "./marquee.js";
import renderState from "./state.js";
-import renderPrice from "./price.js"
+import renderPrice from "./price.js";
+import renderImage from "./image.js";
import renderText from "./text.js";
export default function setupUpdate(): void {
setupTimeSince();
setupTimeUntil();
+ setupMarquee();
setupSounds();
setupStyle();
renderState();
renderPrice();
renderStyle();
+ renderImage();
renderText();
playSounds();
let minutes: number = Math.floor(time / 60);
let seconds: number = Math.floor(time % 60);
- return [hours, minutes, seconds]
+ return [hours, minutes, seconds, time]
}
export function timeSince(end: number): number[] {
return [hours, minutes, seconds]
}
+export function timeFormatStandard(hours: number, minutes: number, seconds: number): string {
+ var hoursString = hours.toString().padStart(2, "0");
+ var minutesString = minutes.toString().padStart(2, "0");
+ var secondsString = seconds.toString().padStart(2, "0");
+
+ return `${minutesString}:${secondsString}`
+}
+
export function timeFormatBroadcast(hours: number, minutes: number, seconds: number): string {
var hoursString = hours.toString().padStart(2, "0");
var minutesString = minutes.toString().padStart(2, "0");
return `${hoursString}h ${minutesString}' ${secondsString}"`
}
+
+export function timeFormatState(hours: number, minutes: number, seconds: number): string {
+ if (hours + minutes + seconds == 0) { return "true" }
+ else { return "false" }
+}
input[type="submit"]:first-of-type {
outline: 15px solid white;
position: sticky; top: 0px;
+ z-index: 2;
}
/* THE OPTIONS GRID */
z-index: 1;
}
.currentValue { color: red; }
-.currentValue[type="radio"] { background-color: red; }
i {
font-style: normal;
-1px 0px 0 black;
}
+.clipboard ~ button {
+ margin-left: 0.5em;
+}
+
+/* timer button text colors */
+.timer[value^="+"] { color: green; }
+.timer[value^="-"] { color: red; }
+
+#assetGrid {
+ display: grid; grid-template-columns: 30% 70%;
+ column-gap: 10px; row-gap: 10px;
+ margin-bottom: 10px;
+}
+#assetGrid > img { border: 1px solid black; }
+
/* PRINT MODE
need to work on this a little bit, currently just hides things that
shouldnt be printed and thats all. Should be able to potentially display
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="1920"
+ height="480"
+ viewBox="0 0 1920 480"
+ version="1.1"
+ id="svg1"
+ inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
+ sodipodi:docname="strap1.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="px"
+ inkscape:zoom="0.22239583"
+ inkscape:cx="960.00001"
+ inkscape:cy="541.82671"
+ inkscape:window-width="1366"
+ inkscape:window-height="710"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1" />
+ <defs
+ id="defs1" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3"
+ transform="translate(0,-51.224152)"
+ style="fill:#000000;fill-opacity:0.99000001">
+ <text
+ xml:space="preserve"
+ style="font-size:192px;line-height:1.35;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';text-align:end;letter-spacing:0.06px;writing-mode:lr-tb;direction:ltr;text-anchor:end;fill:#000000;fill-opacity:0.99000001;stroke-width:6.4;stroke-linejoin:bevel"
+ x="1257.3059"
+ y="236.73596"
+ id="text1"><tspan
+ sodipodi:role="line"
+ x="1257.3658"
+ y="236.73596"
+ id="tspan2"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:192px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond Bold';fill:#000000;fill-opacity:0.99000001">XMDV</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:192px;line-height:1.35;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond';text-align:end;letter-spacing:0.06px;writing-mode:lr-tb;direction:ltr;text-anchor:end;fill:#000000;fill-opacity:0.99000001;stroke-width:6.4;stroke-linejoin:bevel"
+ x="1445.7065"
+ y="416.56024"
+ id="text3"><tspan
+ sodipodi:role="line"
+ id="tspan3"
+ x="1445.7665"
+ y="416.56024"
+ style="font-style:italic;font-variant:normal;font-weight:600;font-stretch:normal;font-size:192px;font-family:'EB Garamond';-inkscape-font-specification:'EB Garamond Semi-Bold Italic';fill:#000000;fill-opacity:0.99000001">Teleshopping</tspan></text>
+ </g>
+ </g>
+</svg>
font-weight: 600;
}
.title { text-transform: capitalize; }
+.style { display: none; }
--- /dev/null
+.styleBottomBox {
+ position: fixed;
+ bottom: var(--safeVertical); left: var(--safeHorizontal);
+ width: calc(100vw - (var(--safeHorizontal) * 2)) !important;
+}
--- /dev/null
+.styleBottomGraphic {
+ margin-left: calc(100% - var(--bottomGraphicWidth)) !important;
+ margin-bottom: 0 !important;
+ box-sizing: border-box;
+ width: var(--bottomGraphicWidth) !important;
+}
+
+.state-false .styleBottomGraphic {
+ padding: 0; margin: 0;
+}
+.styleBottomGraphic {
+ opacity: 0; max-height: 0;
+ transform: translateY(20px);
+ /* should do some tweeking for the below */
+ transition:
+ opacity var(--baseAnimationLength),
+ max-height var(--baseAnimationLength) cubic-bezier(1,0,0,1),
+ padding var(--baseAnimationLength),
+ margin var(--baseAnimationLength),
+ transform var(--baseAnimationLength);
+ overflow: hidden;
+}
+.state-true .styleBottomGraphic {
+ opacity: 1; max-height: 50vh;
+ transform: translateY(0px);
+}
+
+.styleBottomGraphic { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
text-shadow: var(--textAShadow);
background: var(--backgroundA);
- box-shadow: inset 0 0 var(--innerGlowSize) var(--innerGlowColor);
+ box-shadow: inset 0 0 var(--innerGlowASize) var(--innerGlowAColor);
border: var(--borderASize) solid var(--borderAColor);
border-radius: var(--borderARadius);
text-shadow: var(--textBShadow);
background: var(--backgroundB);
- box-shadow: inset 0 0 var(--innerGlowSize) var(--innerGlowColor);
+ box-shadow: inset 0 0 var(--innerGlowBSize) var(--innerGlowBColor);
border: var(--borderBSize) solid var(--borderBColor);
border-radius: var(--borderBRadius);
text-shadow: var(--textCShadow);
background: var(--backgroundC);
- box-shadow: inset 0 0 var(--innerGlowSize) var(--innerGlowColor);
+ box-shadow: inset 0 0 var(--innerGlowCSize) var(--innerGlowCColor);
border: var(--borderCSize) solid var(--borderCColor);
border-radius: var(--borderCRadius);
--- /dev/null
+.styleCrawlerMarquee {
+ font-size: 1.5em;
+ display: inline-block;
+}
+.styleCrawlerInnerBox {
+ overflow: clip;
+}
--- /dev/null
+body.state-blank .styleCrawlerOuterBox {
+ padding: 0; margin: 0;
+}
+.styleCrawlerOuterBox {
+ opacity: 0; max-height: 0;
+ transform: translateX(-20px);
+ /* should do some tweeking for the below */
+ transition:
+ opacity var(--baseAnimationLength),
+ max-height var(--baseAnimationLength) cubic-bezier(1,0,0,1),
+ padding var(--baseAnimationLength),
+ margin var(--baseAnimationLength),
+ transform var(--baseAnimationLength);
+ overflow: hidden;
+}
+body:not(.state-blank) .styleCrawlerOuterBox {
+ opacity: 1; max-height: 50vh;
+ transform: translateX(0px);
+}
+
+/* BEGIN GRID LAYOUT */
+
+.styleCrawlerOuterBox { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
+.styleCrawlerOuterBox:has(*[data-timeout="false"]) { column-gap: var(--boxHPad); }
+.styleCrawlerOuterBox > *:first-child { grid-column-start: 1; grid-column-end: 3; }
+
+/* END GRID LAYOUT */
--- /dev/null
+.styleCrawlerTimerBox {
+ font-size: 1.5em;
+ background:
+ linear-gradient(
+ color-mix(in srgb, var(--borderBColor), transparent 100%),
+ color-mix(in srgb, var(--borderBColor), transparent 100%) var(--percent),
+ color-mix(in srgb, var(--borderBColor), transparent 0%) calc(var(--percent) + 1px),
+ color-mix(in srgb, var(--borderBColor), transparent 0%)
+ ),
+ var(--backgroundC)
+ !important;
+}
+
+.styleCrawlerTimerBox[data-timeout="true"] {
+ padding: 0; margin: 0;
+}
+.styleCrawlerTimerBox {
+ opacity: 0; max-width: 0;
+ transform: translateX(20px);
+ /* should do some tweeking for the below */
+ transition:
+ opacity var(--baseAnimationLength),
+ max-width var(--baseAnimationLength) cubic-bezier(1,0,0,1),
+ padding var(--baseAnimationLength),
+ margin var(--baseAnimationLength),
+ transform var(--baseAnimationLength);
+ overflow: hidden;
+}
+.styleCrawlerTimerBox[data-timeout="false"] {
+ opacity: 1; max-width: 50vh;
+ transform: translateX(0px);
+}
--- /dev/null
+.state-false .styleCrawlerTop {
+ padding: 0; margin: 0;
+}
+.styleCrawlerTop {
+ opacity: 0; max-height: 0;
+ transform: translateY(20px);
+ /* should do some tweeking for the below */
+ transition:
+ opacity var(--baseAnimationLength),
+ max-height var(--baseAnimationLength) cubic-bezier(1,0,0,1),
+ padding var(--baseAnimationLength),
+ margin var(--baseAnimationLength),
+ transform var(--baseAnimationLength);
+ overflow: hidden;
+}
+.state-true .styleCrawlerTop {
+ opacity: 1; max-height: 50vh;
+ transform: translateY(0px);
+}
+
+.styleCrawlerTop { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
-.styleDiscountBox > em {
- display: block;
+.styleDiscountBox em {
font-size: 0.7em;
line-height: 1.4em;
}
opacity: 1; max-height: 50vh;
transform: translateX(0px);
}
+
+/* text styling in item box */
+.styleItemId {}
+.styleItemTitle {
+ font-size: 1.1em;
+ line-height: 1.3;
+}
+.styleItemDescription {
+ display: block;
+ font-size: 0.8em;
+ line-height: 1.3;
+ font-weight: 400;
+}
margin var(--baseAnimationLength),
transform var(--baseAnimationLength);
overflow: hidden;
+
+ color: white;
+
+ width: fit-content;
+ text-align: center;
+ font-size: 1.5em;
+ font-weight: 800;
}
body.state-product .styleLogoBox,
body.state-crawler .styleLogoBox {
- opacity: 1; max-height: 50vh;
+ opacity: .4; max-height: 50vh;
transform: translateX(0px);
}
--- /dev/null
+/*
+body:not(
+ .state-discount,
+ .state-move
+) .styleMonthlyBox {
+ padding: 0; margin: 0;
+}
+*/
+.styleMonthlyBox {
+ /*
+ opacity: 0; max-height: 0;
+ transform: translateX(-20px);
+ /* should do some tweeking for the below *//*
+ transition:
+ opacity var(--baseAnimationLength),
+ max-height var(--baseAnimationLength) cubic-bezier(1,0,0,1),
+ padding var(--baseAnimationLength),
+ margin var(--baseAnimationLength),
+ transform var(--baseAnimationLength);
+ overflow: hidden;
+ */
+
+ font-size: calc(0.75 * var(--baseFontSize));
+ line-height: 1;
+}
+/*
+body.state-discount .styleMonthlyBox,
+body.state-move .styleMonthlyBox {
+ opacity: 1; max-height: 50vh;
+ transform: translateX(0px);
+ padding: 5px;
+}
+*/
margin var(--baseAnimationLength),
transform var(--baseAnimationLength);
overflow: hidden;
+
+ font-size: 0.8em;
+ line-height: 1;
+ font-weight: 400;
}
body.state-price .stylePricingBox,
body.state-discount .stylePricingBox,
opacity: 1; max-height: 50vh;
transform: translateX(0px);
}
+
+body.state-discount .stylePricingBox .styleStrikeDependant,
+body.state-move .stylePricingBox .styleStrikeDependant {
+ text-decoration: line-through var(--textBColor) solid 3px;
+}
+
+.styleStartPrice { font-size: 1.5em; font-weight: 600; }
+.stylePhoneNumber { font-size: 1.5em; }
display: flex;
flex-direction: column;
- margin: 50px;
+ /* standard HD safe margins */
+ margin: var(--safeVertical) 0 0 var(--safeHorizontal);
}
.styleSideBox > * {
margin-bottom: 5px;
.styleSideTimerBox {
display: inline;
width: fit-content !important;
+
+ text-shadow:
+ 1px 1px 3px white, -1px 1px 3px white, 1px -1px 3px white, -1px -1px 3px white,
+ 0 1px 3px white, 0 -1px 3px white, 1px 0 3px white, -1px 0 3px white
+ !important;
+ background:
+ linear-gradient(
+ -0.25turn,
+ color-mix(in srgb, var(--borderBColor), transparent 100%),
+ color-mix(in srgb, var(--borderBColor), transparent 100%) var(--percent),
+ color-mix(in srgb, var(--borderBColor), transparent 0%) calc(var(--percent) + 1px),
+ color-mix(in srgb, var(--borderBColor), transparent 0%)
+ ),
+ var(--backgroundA)
+ !important;
+}
+
+.styleSideTimerBox[data-timeout="true"] {
+ padding: 0; margin: 0;
+}
+.styleSideTimerBox {
+ opacity: 0; max-height: 0;
+ transform: translateX(-20px);
+ /* should do some tweeking for the below */
+ transition:
+ opacity var(--baseAnimationLength),
+ max-height var(--baseAnimationLength) cubic-bezier(1,0,0,1),
+ padding var(--baseAnimationLength),
+ margin var(--baseAnimationLength),
+ transform var(--baseAnimationLength);
+ overflow: hidden;
+}
+.styleSideTimerBox[data-timeout="false"] {
+ opacity: 1; max-height: 50vh;
+ transform: translateX(0px);
}
--- /dev/null
+@keyframes spin {
+ from { transform: rotateY(-90deg); }
+ to { transform: rotateY(90deg); }
+}
+
+.styleSpinBox {
+ animation: 6s linear 0s spin infinite;
+}
<script type="module" defer>
-import { setupTrigger } from "/script/admin.js";
-setupTrigger();
+import { setupClipboard } from "/script/admin.js";
+setupClipboard();
</script>
</head>
<li><a href="/info/c">Info page C</a> <em>(Gallery screen, timing and state)</em></li>
</ul>
- <img
- src="/static/media/velo.jpg"
- attributionsrc="https://commons.wikimedia.org/w/index.php?curid=4075468"
- style="width: min(100%, 700px);" />
+ <h2>Assets</h2>
+
+ <div id="assetGrid">
+ {% for path in assets %}
+ <img style="width: 100%;" src="{{ path }}" />
+ <span><a href="{{ path }}" class="clipboard">Link</a></span>
+ {% endfor %}
+ </div>
+
+ {#
+ <img
+ src="/static/media/velo.jpg"
+ attributionsrc="https://commons.wikimedia.org/w/index.php?curid=4075468"
+ style="width: min(100%, 700px);" />
+ #}
<br>
<code><a href="https://delta-aa.ozva.co.uk/admin">Delta Aa</a> - <a href="https://delta-ab.ozva.co.uk/admin">Delta Ab</a> - <a href="https://git.ozva.co.uk/?p=delta-velorum;a=summary">Nomenclature</a></code>
--- /dev/null
+<span id="{{ name }}" style="grid-column: 2 / span 3;">
+ <noscript style="color: red;">Timer will not function without JS<br></noscript>
+ <button type="button" name="{{ name }}" value="{{ label }}" class="trigger">
+ Start {{ label }}
+ </button>
+ <input type="number" id="{{ name }}+delay" value="{{ value.split('+')[1] }}"/><span> seconds</span>
+ <button type="button" name="{{ name }}" value="-60" class="timer">-60s</button>
+ <button type="button" name="{{ name }}" value="-30" class="timer">-30s</button>
+ <button type="button" name="{{ name }}" value="-10" class="timer">-10s</button>
+ <button type="button" name="{{ name }}" value="+10" class="timer">+10s</button>
+ <button type="button" name="{{ name }}" value="+30" class="timer">+30s</button>
+ <button type="button" name="{{ name }}" value="+60" class="timer">+60s</button>
+</span>
--- /dev/null
+<label>{{ label }}</label>
+<span>
+ {% if value == "true" %}
+ <input id="{{ name }}" type="radio" name="{{ name }}" value="true" checked="checked" />
+ <label>on ↔ off</label>
+ <input id="{{ name }}" type="radio" name="{{ name }}" value="false" />
+ {% else %}
+ <input id="{{ name }}" type="radio" name="{{ name }}" value="true" />
+ <label>on ↔ off</label>
+ <input id="{{ name }}" type="radio" name="{{ name }}" value="false" checked="checked" />
+ {% endif %}
+</span>
+<span class="currentValue">{{ value }}</span>
{# ELEMENT STYLING IMPORTS #}
<link rel="stylesheet" href="/static/style/side_box.css">
+ <link rel="stylesheet" href="/static/style/bottom_box.css">
<link rel="stylesheet" href="/static/style/qty_box.css">
<link rel="stylesheet" href="/static/style/side_timer_box.css">
<link rel="stylesheet" href="/static/style/center_shout.css">
<link rel="stylesheet" href="/static/style/discount_box.css">
<link rel="stylesheet" href="/static/style/banner_box.css">
+ <link rel="stylesheet" href="/static/style/monthly_box.css">
<link rel="stylesheet" href="/static/style/pricing_box.css">
<link rel="stylesheet" href="/static/style/item_box.css">
<link rel="stylesheet" href="/static/style/logo_box.css">
+ <link rel="stylesheet" href="/static/style/spin_box.css">
+ <link rel="stylesheet" href="/static/style/crawler_outer_box.css">
+ <link rel="stylesheet" href="/static/style/crawler_timer_box.css">
+ <link rel="stylesheet" href="/static/style/crawler_marquee.css">
+ <link rel="stylesheet" href="/static/style/crawler_top.css">
+ <link rel="stylesheet" href="/static/style/bottom_graphic.css">
{# HELPER IMPORTS #}
<link rel="stylesheet" href="/static/style/box.css">
<link rel="stylesheet" href="/static/style/show_until.css">
- <link rel="stylesheet" href="/static/style/background.css">
<script type="importmap">
{
<span class="update style~~text--sideBoxWidth style"></span>
<span class="update style~~text--baseFontSize style"></span>
+ <span class="update style~~text--safeHorizontal style"></span>
+ <span class="update style~~text--safeVertical style"></span>
+
+ <span class="update style~~text--bottomGraphicWidth style"></span>
+
{# DYNAMIC STYLING ELEMENTS #}
<span class="update style__colorscheme~~text--borderASize style"></span>
<span class="update style__colorscheme__backgroundB~~text--backgroundB style"></span>
<span class="update style__colorscheme__backgroundC~~text--backgroundC style"></span>
- <span class="update style__colorscheme~~color--innerGlowColor style"></span>
- <span class="update style__colorscheme~~text--innerGlowSize style"></span>
+ <span class="update style__colorscheme~~color--innerGlowAColor style"></span>
+ <span class="update style__colorscheme~~text--innerGlowASize style"></span>
+ <span class="update style__colorscheme~~color--innerGlowBColor style"></span>
+ <span class="update style__colorscheme~~text--innerGlowBSize style"></span>
+ <span class="update style__colorscheme~~color--innerGlowCColor style"></span>
+ <span class="update style__colorscheme~~text--innerGlowCSize style"></span>
{# SFX ELEMENTS #}
<audio src="/static/media/sfx/banner_shout.wav" class="update data~~trigger--bannerShoutTrigger sound"></audio>
<audio src="/static/media/sfx/state_change.wav" class="update data__state~~text--state sound"></audio>
<audio src="/static/media/sfx/shout.wav" class="update data~~trigger--centerShoutTrigger sound"></audio>
+ {# TIMER SFX ELEMENTS #}
+ <audio src="/static/media/sfx/clock.wav" class="update data__timer~~timer--timerSide1 until until-sound"></audio>
+ <audio src="/static/media/sfx/clock.wav" class="update data__timer~~timer--timerSide2 until until-sound"></audio>
+ <audio src="/static/media/sfx/clock.wav" class="update data__timer~~timer--timerCrawler until until-sound"></audio>
+
+ {#
+
+ ------------------------------------------------------------------------
+ PAGE BEGINS
+
+ #}
+
{# CENTER SHOUT #}
<div class="styleCenterShout update data~~trigger--centerShoutTrigger showUntil">
<div>
these are switched on and off using the state class from the
body tag.
#}
+
<div class="styleSideBox">
{# LOGO BOX #}
- <div class="styleLogoBox">
- LOGO BOX
+ <div class="styleLogoBox styleSpinBox .styleOutline">
+ XMDV
</div>
{# BANNER BOX #}
<div class="styleBannerBox boxC">
- BANNER BOX
+ <span class="update data__text~~text--bannerText title"></span>
</div>
{# ITEM BOX #}
<div class="styleItemBox boxB">
+ Item Code <span class="update data__product~~text--id title styleItemId"></span>
<div class="boxA">
- ITEM BOX
+ <span class="update data__product~~text--title title styleItemTitle"></span><br>
+ <span class="update data__product~~text--description text styleItemDescription"></span>
</div>
- </div>
- {# PRICING BOX #}
- <div class="stylePricingBox boxA">
- PRICING BOX
+ {# PRICING BOX #}
+ <div class="stylePricingBox boxA">
+ <span class="styleStrikeDependant">ONLY <span class="update data__product__originalPriceString~~text--originalPriceString text styleStartPrice "></span></span><br>
+ <span class="stylePhoneNumber">#### ### ####</span> Call Us Now!
+ </div>
</div>
{# DISOUNT BOX #}
<div class="styleDiscountBox boxB">
- <em>Now only...</em>
+ <b><span class="update data__product~~number--discount discount"></span></b>
+ OFF! CHEAP! <em>SAVE
+ <b><span class="update data__product__savePriceString~~text--savePriceString text"></span></b></em>
<div class="boxA">
- <b><span class="update data__product~~number--discount discount"></span></b>
- OFF!
+ <em>NOW...</em>
+ <span class="update data__product__currentPriceString~~text--currentPriceString text"></span>
+ <div class="boxC styleMonthlyBox">
+ ...Or <b><span class="update data__product~~float--monthlyPayments text"></span></b> Monthly Payments of <b><span class="update data__product__multibuyPriceString~~text--multibuyPriceString text"></span></b>
+ </div>
</div>
</div>
</div>
{# TIMER BOX #}
- <div class="styleSideTimerBox boxA">
- TIMER 1!
+ <div class="styleSideTimerBox boxA update data__timer~~timer--timerSide1 until until-timeout">
+ <span class="update data__timer~~timer--timerSide1 until"></span>
+ </div>
+ <div class="styleSideTimerBox boxA update data__timer~~timer--timerSide2 until until-timeout">
+ <span class="update data__timer~~timer--timerSide2 until"></span>
</div>
- <div class="styleSideTimerBox boxA">
- TIMER 2!
+ </div>
+
+ {# CRAWLER
+
+ GRAPHIC BOX
+
+ OUTER CRAWLER
+ includes:
+ - CRAWLER TOP BANNER (STATIC) (show on OuterBox state change)
+ - CRAWLER MARQUEE
+ - CRAWLER TIMER (show only when running)
+
+ (crawler show during: all except blank)
+ #}
+
+ <div class="styleBottomBox">
+
+ <div class="update data~~toggle--showBottomGraphic state">
+ <img class="styleBottomGraphic boxA update data~~text--bottomGraphic image" />
+ </div>
+
+ <div class="styleCrawlerOuterBox boxA update data__text~~toggle--showCrawlerTop state">
+ <span class="styleCrawlerTop">
+ <span class="update data__text~~text--crawlerTop title"></span>
+ <span style="color: var(--borderBColor); margin-right: 0.2em;"><em>XMDV Teleshopping</em></span>
+ </span>
+ <div class="styleCrawlerInnerBox boxC">
+ <span class="styleCrawlerMarquee update data__text__crawlerMarqueeList~~text--crawlerMarqueeList marquee"></span>
+ </div>
+ <div class="styleCrawlerTimerBox boxC update data__timer~~timer--timerCrawler until until-timeout">
+ <span class="update data__timer~~timer--timerCrawler until"></span>
+ </div>
</div>
</div>
</body>
<span>Next state: <span class="update data__state~~text--nextState title"></span></span>
<span style="grid-column: 1 / span 3; font-size: 2em; font-size: 2em;">
- <span class="update data~~trigger--startTrigger since"></span> <span style="background-color: red;">T-<span class="update data__endTime~~text--endTime until"></span></span>
+ <span class="update data~~trigger--startTrigger since"></span> <span style="background-color: red;">T-<span class="update data__endTime~~text--endTime until until-broadcast"></span></span>
</span>
</div>
</body>
<script type="module" defer>
-import { setupTrigger } from "/script/admin.js";
+import { setupTrigger, setupTimerButtons } from "/script/admin.js";
setupTrigger();
+setupTimerButtons();
</script>
</head>
<nav>
<p><a href="/admin">.. back</a></p>
<p><b>{{ table_name }}</b></p>
+ <p><a href="/admin/{{ table_name }}#current">current</a></p>
<ol>
{% for row in table %}
<li style="border: 1px solid gray; margin-top: -1px;"><ul>
</nav>
<main>
<h1><i>δ Velorum {{ star }}</i> Table: {{ table_name }}</h1>
- <form class="optionsGrid" method="post">
+ <form class="optionsGrid" method="post" action="/admin/{{ table_name }}#current">
<input type="submit" value="Submit table" style="grid-column: 1 / span 4;">
<span class="optionsGridSpacer" style="grid-column: 1 / span 4;"></span>
{% for row in table %}
display: flex;
justify-content: center;
align-items: center;
- ">
+ "
{% if loop.index0 == indexes["int--" ~ table_name] %}
- <input type="radio" name="{{ "indexes~~0~~int--" ~ table_name }}" value="{{ loop.index0 }}" checked="checked" class="currentValue">
+ id="current">
+ <input type="radio" name="{{ "indexes~~0~~int--" ~ table_name }}" value="{{ loop.index0 }}" checked="checked">
{% else %}
+ >
<input type="radio" name="{{ "indexes~~0~~int--" ~ table_name }}" value="{{ loop.index0 }}" >
{% endif %}
</span>
{% set value = row[feild] %}
{% set name = table_name ~ "~~" ~ group ~ "~~" ~ feild %}
- {% if type == "textarea" %}
- {% include "admin/textarea.html" %}
- {% elif type == "text" %}
- {% include "admin/text.html" %}
- {% elif type == "number" %}
- {% include "admin/number.html" %}
- {% elif type == "float" %}
- {% include "admin/float.html" %}
- {% elif type == "trigger" %}
- {% include "admin/trigger.html" %}
- {% elif type == "color" %}
- {% include "admin/color.html" %}
- {% endif %}
+ {% include "admin/" ~ type ~ ".html" %}
{% endfor %}
<span class="optionsGridSpacer" style="grid-column: 1 / span 4;"></span>
+prometheus-flask-exporter
flask_socketio
flask_httpauth
flask_cors