-*.ttf
-.venv/
-item[0-9].html
-content.opf
-toc.ncx
-*.toc
-*.epub
-*.mobi
-*.tex
-*.pdf
-*.aux
-*.log
-*.out
+*.wsgi
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- width="200"
- height="200"
- viewBox="0 0 52.916665 52.916666"
- version="1.1"
- id="svg5"
- inkscape:export-filename="clock.svg"
- inkscape:export-xdpi="96"
- inkscape:export-ydpi="96"
- 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="namedview7"
- pagecolor="#5b5b5b"
- bordercolor="#000000"
- borderopacity="0.25"
- inkscape:showpageshadow="2"
- inkscape:pageopacity="0"
- inkscape:pagecheckerboard="true"
- inkscape:deskcolor="#d1d1d1"
- inkscape:document-units="mm"
- showgrid="false" />
- <defs
- id="defs2">
- <marker
- style="overflow:visible"
- id="Arrow1L"
- refX="0"
- refY="0"
- orient="auto-start-reverse"
- inkscape:stockid="Arrow1L"
- markerWidth="5"
- markerHeight="2.8571429"
- viewBox="0 0 8.75 5"
- inkscape:isstock="true"
- inkscape:collect="always"
- preserveAspectRatio="xMidYMid">
- <path
- style="fill:context-stroke;fill-rule:evenodd;stroke:none"
- d="M 0,0 5,-5 -12.5,0 5,5 Z"
- id="arrow1L"
- transform="scale(-0.5)" />
- </marker>
- </defs>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <circle
- style="display:none;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-opacity:1"
- id="path111"
- cx="26.458334"
- cy="26.45833"
- r="25.135416" />
- <circle
- style="display:none;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:1.17475, 1.32292;stroke-dashoffset:0.529167;stroke-opacity:1"
- id="path111-3"
- cx="26.458334"
- cy="26.458334"
- r="23.846424"
- inkscape:export-filename="clock.svg"
- inkscape:export-xdpi="103.64"
- inkscape:export-ydpi="103.64" />
- <path
- style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1;marker-end:url(#Arrow1L)"
- d="M 26.458333,26.458333 V 9.2604167"
- id="path2766"
- sodipodi:nodetypes="cc" />
- </g>
-</svg>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- width="200"
- height="200"
- viewBox="0 0 52.916665 52.916666"
- version="1.1"
- id="svg5"
- 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="namedview7"
- pagecolor="#5b5b5b"
- bordercolor="#000000"
- borderopacity="0.25"
- inkscape:showpageshadow="2"
- inkscape:pageopacity="0"
- inkscape:pagecheckerboard="true"
- inkscape:deskcolor="#d1d1d1"
- inkscape:document-units="mm"
- showgrid="false" />
- <defs
- id="defs2">
- <marker
- style="overflow:visible"
- id="Arrow1L"
- refX="0"
- refY="0"
- orient="auto-start-reverse"
- inkscape:stockid="Arrow1L"
- markerWidth="5"
- markerHeight="2.8571429"
- viewBox="0 0 8.75 5"
- inkscape:isstock="true"
- inkscape:collect="always"
- preserveAspectRatio="xMidYMid">
- <path
- style="fill:context-stroke;fill-rule:evenodd;stroke:none"
- d="M 0,0 5,-5 -12.5,0 5,5 Z"
- id="arrow1L"
- transform="scale(-0.5)" />
- </marker>
- </defs>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <circle
- style="display:inline;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-opacity:1"
- id="path111"
- cx="26.458334"
- cy="26.45833"
- r="25.135416" />
- <circle
- style="display:inline;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:1.17475, 1.32292;stroke-dashoffset:0.529167;stroke-opacity:1"
- id="path111-3"
- cx="26.458334"
- cy="26.458334"
- r="23.846424"
- inkscape:export-filename="clock.svg"
- inkscape:export-xdpi="103.64"
- inkscape:export-ydpi="103.64" />
- <path
- style="display:none;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1;marker-end:url(#Arrow1L)"
- d="M 26.458333,26.458333 V 9.2604167"
- id="path2766"
- sodipodi:nodetypes="cc" />
- </g>
-</svg>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- width="400"
- height="400"
- viewBox="0 0 105.83333 105.83333"
- version="1.1"
- id="svg1"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:dc="http://purl.org/dc/elements/1.1/">
- <defs
- id="defs1" />
- <metadata
- id="metadata1">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <path
- style="fill:#bd0000;fill-opacity:1;stroke-width:0.264798;stroke-dasharray:none"
- id="path1"
- d="M 71.019533,63.52164 69.13196,77.122826 56.678371,71.338072 48.243092,82.173258 40.350345,70.93672 27.627586,76.102627 26.410535,62.425126 12.809349,60.537554 18.594102,48.083965 7.7589164,39.648686 18.995454,31.755938 13.829547,19.03318 27.507048,17.816129 29.394621,4.214943 41.84821,9.9996963 50.283489,-0.83548968 58.176236,10.401048 70.898995,5.2351411 l 1.217051,13.6775009 13.601186,1.887573 -5.784754,12.453589 10.835186,8.435278 -11.236537,7.892748 5.165907,12.722759 z"
- transform="matrix(1.274966,0,0,1.274966,-9.8923536,1.065222)" />
-</svg>
+++ /dev/null
-{"currentPosition":0,"movementSpeed":30,"function":"ease","lightingCues":["false","false","true","false","false","true","false","true","false","true"]}
\ No newline at end of file
+++ /dev/null
-{"topText":["It's here!"],"bottomText":["Something bright and radiant for all of you at home!","I take your life and raise you 10 over the hour!","We grab the bullion counterweight and throw ourselves down!","We have been falling for a looong time people!","Let me slip into something less spacious!","Down the side of a large golden pyramid!","So we dress ourselves in your Ballast!","I used to be so unimaginably tall!"],"bannerText":"Discounts on XMDV!","itemId":1,"currency":{"prefix":"$","postfix":" hairs"},"discount":-5000,"percentLeft":20,"prefix":"false","round":"true","priceChange":50,"showingMain":"true","showingAll":"true","showingExtra":"true","showingTimer1":"true","showingTimer2":"false","showingTimer3":"false","timer3Main":"false","showingBanner":"true","showingSigil":["false","false","false","false"],"timer1End":1724849889,"timer2End":1724848450,"timer3End":1724787297,"timerOffset":0}
\ No newline at end of file
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <style>
-@font-face {
- font-family: "Archivo";
- font-weight: normal;
- font-style: normal;
- src:
- local("Archivo"),
- url("fonts/Archivo-Regular.woff") format("woff"),
-}
-@font-face {
- font-family: "Archivo";
- font-weight: bold;
- font-style: normal;
- src:
- local("Archivo"),
- url("fonts/Archivo-SemiBold.woff") format("woff"),
-}
-@font-face {
- font-family: "Archivo";
- font-weight: normal;
- font-style: italic;
- src:
- local("Archivo"),
- url("fonts/Archivo-Italic.woff") format("woff"),
-}
-@font-face {
- font-family: "Archivo";
- font-weight: bold;
- font-style: italic;
- src:
- local("Archivo"),
- url("fonts/Archivo-SemiBoldItalic.woff") format("woff"),
-}
-
-body {
- --star-light: #FFE0B3;
- --star-dark: #FFC266;
- --dark: #DEDEDE;
- --black: #3b3b45;
- --feature-dark: #AAAAEE;
- --feature-light: #BBBBEE;
- --feature-white: #EAEAFA;
- --background-light: #FFFFFF;
- --background-dark: #EEEEEE;
- --feature-gradient: linear-gradient(var(--feature-dark), var(--feature-light));
- --background: linear-gradient(var(--background-light), var(--background-dark));
- --clock: conic-gradient(var(--background-light) 0deg, var(--background-light) 0deg, rgb(0,0,0,0) 0.1deg), rgb(0,0,0,0) 360deg);
-
- font-family: "Archivo", sans-serif;
- font-size: 1.6vh;
- color: var(--black);
-
- opacity: 0;
- transition: opacity 1.5s;
-}
-@keyframes spin {
- 0% {transform: rotate(0turn) scale(1.2);}
- 50% {transform: rotate(0.25turn) scale(1);}
- 100% {transform: rotate(0.5turn) scale(1.2);}
-}
-@keyframes spinText {
- 0% {transform: rotate(-0.01turn);}
- 50% {transform: rotate(0.01turn);}
- 100% {transform: rotate(-0.01turn);}
-}
-.container {
- position: absolute;
- border-radius: 1vh;
-}
-.box {
- border-radius: 1vh;
- box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
- background-image: var(--background);
-}
-.side {
- left: 6vh;
- top: 6vh;
- width: 40vh;
- opacity: 0;
- transition: opacity 1.5s;
-}
-.soldBox {
- margin: 5px;
- padding: 5px;
- border-radius: 5px;
- background-image: var(--background);
- box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.19) 0px 1px 2px;
-}
-.soldBox > * {
- margin: 0;
- font-style: italic;
-}
-.soldBox > * > span {
- font-weight: bold;
- font-style: normal;
-}
-#raiting {
- margin: 5px;
- margin-top: 0;
- border-radius: 5px;
- font-size: 2em;
- line-height: 1em;
- color: var(--star-dark);
- text-shadow: -1px -1px 0 var(--star-light), 1px -1px 0 var(--star-light), -1px 1px 0 var(--star-light), 1px 1px 0 var(--star-light), 2px 2px 0 var(--dark);
- background-image: linear-gradient(var(--background-dark), var(--dark));
- box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.19) 0px 1px 2px;
-}
-.main {
- z-index: 0;
-}
-.banner {
- left: 48vh;
- top: 6vh;
- height: 2em;
- width: 0vh;
- padding: 0.5vh;
- overflow: hidden;
- opacity: 0;
- transition: opacity 1.5s, width 1.5s, left 1.5s;
- background: linear-gradient(var(--feature-white), rgb(0,0,0,0), var(--feature-white)), var(--background);
- border: 1px solid var(--feature-dark);
-}
-.banner.moved, .timer2.moved {
- left: 6vh;
-}
-.timer2.movedUp {
- top: 6vh;
-}
-.timer3.moved {
- bottom: calc(50% - 15vh);
- right: calc(50% - 15vh);
- width: 30vh;
- height: 30vh;
- border-radius: 15vh;
- font-size: 6em;
- line-height: 30vh;
-}
-.extra {
- position: relative;
- top: -2vh;
- z-index: -1;
- transform: translateY(-100%);
- padding-top: 2vh;
- padding-bottom: 1px;
- background-image: var(--feature-gradient);
- transition: transform 1.5s;
-}
-.showExtra > .extra {
- transform: translateY(0%);
-}
-.main > *, .extra > *:not(.soldBox), .bottom > *, .bottom > div > *, .marquee > * {
- margin: 0;
- padding: 0.5vh 1vh;
-}
-.banner > h1 {
- width: 49vh;
- margin: 0;
- font-size: 2em;
- line-height: 1;
- font-style: italic;
- font-weight: normal;
- text-align: center;
-}
-.sigilBox {
- opacity: 0;
- transition: opacity 10s;
-}
-.sigil {
- width: 20vh;
- height: 20vh;
-}
-#sigilNE {
- right: 4vh;
- top: 4vh;
-}
-#sigilSE {
- right: 4vh;
- bottom: 4vh;
-}
-#sigilSW {
- left: 4vh;
- bottom: 4vh;
-}
-#sigilNW {
- left: 4vh;
- top: 4vh;
-}
-.badgeContainer {
- position: relative;
- padding: 0;
-}
-.badge {
- position: absolute;
- top: -2vh;
- left: 32vh;
- width: 10vh;
- height: 10vh;
- padding: 0;
- animation: spin 10s linear 0s infinite;
-}
-h2.badge {
- top: -1vh;
- left: 26.5vh;
- color: white;
- width: 20vh;
- animation: spinText 10s linear 0s infinite;
- rotate: 0.025turn;
- text-align: center;
- text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
-}
-.badgeContainer {
- transform: translateX(12vh) scale(0%);
- transition: transform 1.5s;
-}
-.main > hr, .extra > hr {
- margin: 0 1vh;
- padding: 0;
- border-color: var(--dark);
-}
-.feature {
- border-top: 2px solid var(--dark);
- border-bottom: 2px solid var(--dark);
- background-image: var(--feature-gradient);
-}
-.bottom {
- left: 6vh;
- bottom: 6vh;
- width: 100vh;
-}
-.bottom > *:not(#timer1) {
- display: inline-block;
- vertical-align: top;
-}
-#topTextBox {
- width: calc(100% - 20px);
- margin: 5px;
- margin-bottom: 0;
- padding: 5px;
- border-radius: 5px;
- background-image: linear-gradient(0.25turn, var(--feature-dark), rgba(0,0,0,0) 40%), var(--background);
- box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.19) 0px 1px 2px;
-}
-#topText {
- display: inline-block;
- width: 50%;
- padding: 0;
-}
-#idText {
- display: inline-block;
- width: 50%;
- padding: 0;
- text-align: right;
- font-size: 1.5em;
- font-weight: bold;
- font-style: italic;
- text-shadow: 0 0 3px rgba(0,0,0,0.25);
- color: var(--feature-dark);
-}
-.marquee {
- padding: 0;
- width: 100%;
- mask-image: linear-gradient(0.25turn, transparent, black 5%, black 95%, transparent);
- overflow: hidden;
-}
-.marqueeText {
- white-space: nowrap;
- overflow: hidden;
-}
-.timer1 {
- position: absolute;
- left: 101vh;
- padding: 0.5vh;
- opacity: 0;
- text-align: center;
- border: 2px solid var(--dark);
- border-radius: calc(1vh - 2px);
- transition: opacity 1.5s;
-}
-.timer2 {
- position: absolute;
- left: calc(48vh + 2px);
- top: calc(11.5vh + 2px);
- height: 1.2em;
- width: 3em;
- margin: 0;
- text-align: center;
- font-size: 2em;
- font-weight: bold;
- line-height: 1.2em;
- border: 2px solid var(--dark);
- outline: 2px solid var(--feature-dark);
- opacity: 0;
- transition: opacity 1.5s, left 1.5s, top 1.5s;
-}
-.timer3 {
- position: absolute;
- bottom: 4vh;
- right: 4vh;
- opacity: 0;
- width: 12vh;
- height: 12vh;
- border-radius: 6vh;
- margin: 0;
- text-align: center;
- vertical-align: center;
- font-size: 2em;
- font-weight: bold;
- line-height: 12vh;
- background-image: var(--background);
- border: 2px solid var(--feature-dark);
- outline: 2px solid var(--dark);
- opacity: 0;
- transition: opacity 1.5s, bottom 1.5s, right 1.5s, width 1.5s, height 1.5s, border-radius 1.5s, font-size 1.5s, line-height 1.5s, transform 1.5s;
-}
-.show {
- opacity: 1;
-}
-.show.badgeContainer {
- transform: translateX(0vh) scale(100%);
-}
-.show.banner {
- width: 50vh;
-}
-.show.sigilBox {
- opacity: 0.9;
-}
-@keyframes spinBox {
- 0% {transform: translateY(100%);}
- 50% {}
- 100% {}
-
-}
-line, polyline, circle {
- fill: none;
- stroke: var(--feature-dark);
- stroke-width: 3;
-}
-circle {
- fill: #F2F2F2;
-}
-.sigilGrid {
- stroke: var(--dark);
- stroke-width: 1;
-}
- </style>
- <script>
-// independantly itterated counters for the bottom (marquee) text and the text displayed above it
-// this changes at the end of each marquee cycle
-var topTextCounter = 0;
-var bottomTextCounter = 0;
-
-var marqueeOffset = 100.;
-
-// current item number. used to work out which item from the static item file to pick
-var itemId = 0;
-
-// bool for whether the price is rounded to the nearest 100th
-var round = true;
-
-// whether to display the price with a pre or postfix and what each fix string is
-var prefix = true;
-var prefixString = "";
-var postfixString = "";
-
-// 2 discount variables are tracked for smooth transitions between them
-// "newDiscount" is the requested discount from the server. "discount" is the current client discount
-// "priceCange" is the current requested discount change measured in %/s at which the "discount" is animated
-var discount = 1;
-var newDiscount = 1;
-var priceChange = 0;
-
-var percentLeft = 1;
-
-var showingSigils = true;
-
-// helper function for creating price strings
-function makePrice(price) {
- if (round == "true") {price = Math.round(price * 100) / 100;}
- if (prefix == "true") {
- return `${prefixString}${price}`;
- } else {
- return `${price}${postfixString}`;
- }
-}
-// helper function for creating time strings
-function makeTime(t, o) {
- let current = Math.round((Date.now() + o) / 1000);
- var time = t - current;
- if (Math.sign(time) == -1) {time = 0;}
- var minutes = Math.floor(time / 60);
- var seconds = (time - (minutes * 60));
-
- var minutesString = minutes.toString();
- var secondsString = seconds.toString();
- minutesString = minutesString.padStart(2, "0");
- secondsString = secondsString.padStart(2, "0");
-
- return `${minutesString}:${secondsString}`;
-}
-function makeSeconds(t, o, d) {
- let current = Math.round((Date.now() + o) / 1000);
- var time = t - current;
- if (Math.sign(time) == -1) {time = 0;}
- var minutes = Math.floor(time / 60);
- var seconds = (time - (minutes * 60));
-
- if (d == true) {
- if (minutes > 0) {return 360;}
- else {return Math.round(seconds * (360 / 60))}
- } else {
- if (minutes > 0) {return 100;}
- else {return Math.round(seconds * (100 / 60))}
- }
-}
-
-// handles all updates from the server data that are not required to be instantanious (2 times per second)
-function update() {
- fetch("./data.json", {cache: "no-store"})
- .then(data => data.json())
- .then(data => {
- itemId = data.itemId;
-
- // handle all optional elements showing/hiding
- // All element handling
- const all = document.getElementById("all");
- if (data.showingAll == "true") {
- all.classList.add("show");
- } else {
- all.classList.remove("show");
- }
-
-
- // Timer 1
- const timer1 = document.getElementsByClassName("timer1")[0];
- timer1.innerHTML = makeTime(data.timer1End, data.timerOffset);
- if (data.showingTimer1 == "true") {
- timer1.classList.add("show");
- } else {
- timer1.classList.remove("show");
- }
-
- // Timer 2
- const timer2 = document.getElementsByClassName("timer2")[0];
- timer2.innerHTML = makeTime(data.timer2End, data.timerOffset);
- if (data.showingTimer2 == "true") {
- timer2.classList.add("show");
- } else {
- timer2.classList.remove("show");
- }
- var angle = makeSeconds(data.timer2End, data.timerOffset, true);
- var property = `conic-gradient(rgb(0,0,0,0) 0deg, rgb(0,0,0,0) ${angle}deg, var(--feature-light) ${angle}.1deg, var(--feature-light) 360deg)`;
- timer2.style.background = `${property}, var(--background)`;
-
- // Timer 3
- const timer3 = document.getElementsByClassName("timer3")[0];
- timer3.innerHTML = makeTime(data.timer3End, data.timerOffset);
- if (data.showingTimer3 == "true") {
- timer3.classList.add("show");
- } else {
- timer3.classList.remove("show");
- }
- if (data.timer3Main == "true") {
- timer3.classList.add("moved");
- } else {
- timer3.classList.remove("moved");
- }
- var radius = makeSeconds(data.timer3End, data.timerOffset, false);
- var property = `linear-gradient(rgb(0,0,0,0) 0%, rgb(0,0,0,0) ${radius}%, var(--feature-light) ${radius}.1%, var(--feature-dark) 100%)`;
- timer3.style.background = `${property}, var(--background)`;
-
- // Banner handling
- const banner = document.getElementsByClassName("banner")[0];
- if (data.showingBanner == "true") {
- if (!banner.classList.contains("show")) {
- document.getElementById("bannerText").innerHTML = data.bannerText;
- }
- banner.classList.add("show");
- timer2.classList.remove("movedUp");
-
- } else {
- banner.classList.remove("show");
- timer2.classList.add("movedUp");
- }
-
- // Side product information
- const side = document.getElementsByClassName("side")[0];
- if (data.showingMain == "true") {
- side.classList.add("show");
- banner.classList.remove("moved");
- timer2.classList.remove("moved");
- } else {
- side.classList.remove("show");
- banner.classList.add("moved");
- timer2.classList.add("moved");
- }
-
- // Product extra information
- if (data.showingExtra == "true" && data.showingMain == "true") {
- side.classList.add("showExtra");
- } else {
- side.classList.remove("showExtra");
- }
-
- // Discount badge
- const badgeContainer = document.getElementsByClassName("badgeContainer")[0];
- if (discount <= 0.99 && data.showingExtra == "true") {
- badgeContainer.classList.add("show");
- } else {
- badgeContainer.classList.remove("show");
- }
-
- // Sigil handling
- const sigilNE = document.getElementById("sigilNE");
- if (data.showingSigil[0] == "true") {
- sigilNE.classList.add("show");
- } else {
- sigilNE.classList.remove("show");
- }
- const sigilSE = document.getElementById("sigilSE");
- if (data.showingSigil[1] == "true") {
- sigilSE.classList.add("show");
- } else {
- sigilSE.classList.remove("show");
- }
- const sigilSW = document.getElementById("sigilSW");
- if (data.showingSigil[2] == "true") {
- sigilSW.classList.add("show");
- } else {
- sigilSW.classList.remove("show");
- }
- const sigilNW = document.getElementById("sigilNW");
- if (data.showingSigil[3] == "true") {
- sigilNW.classList.add("show");
- } else {
- sigilNW.classList.remove("show");
- }
-
- // Check for sigil freeze
- if (data.showingSigil.every(i => i == "false")) {
- showingSigils = false;
- } else {
- showingSigils = true;
- }
-
- // set variables for the frame process to allow it to function without requesting data 200x per second
- round = data.round;
-
- prefix = data.prefix;
- prefixString = data.currency.prefix;
- postfixString = data.currency.postfix;
-
- priceChange = data.priceChange / 100;
- newDiscount = data.discount / 100;
-
- percentLeft = data.percentLeft /100;
-
- });
-
- // get the requested item data
- fetch("./items.json") // this is static so there is no need to request it every time
- .then(data => data.json())
- .then(data => {
- // set item properties
- document.getElementById("code").innerHTML = data[itemId].code;
- document.getElementById("raiting").innerHTML = data[itemId].rating;
- document.getElementById("subtext").innerHTML = data[itemId].subtext;
- document.getElementById("description").innerHTML = data[itemId].description;
-
- // set discount figures
- let discountPricePlan = (data[itemId].origionalPrice * discount * 1.1) / 12
- document.getElementById("currentPrice").innerHTML = `<em>Now only:</em> ${makePrice(data[itemId].origionalPrice * discount)}`;
- document.getElementById("monthlyPrice").innerHTML = `12 monthly payments of <b>${makePrice(discountPricePlan)}</b>`;
- document.getElementById("badgeText").innerHTML = `${Math.round((1 - discount) * 100)}% OFF`;
-
- document.getElementById("stock").innerHTML = `${data[itemId].stockCount} units`;
- document.getElementById("sold").innerHTML = `${Math.round(data[itemId].stockCount * (1 - percentLeft))} units`;
-
- if (percentLeft < 0.1) { descriptor = "Quick! Only"; }
- else if (percentLeft < 0.25) { descriptor = "Only"; }
- else if (percentLeft < 0.5) { descriptor = "Just"; }
- else { descriptor = ""; }
-
- if (percentLeft == 0) { document.getElementById("unitsLeft").innerHTML = "Sold out!"; }
- else { document.getElementById("unitsLeft").innerHTML = `${descriptor} ${Math.round(data[itemId].stockCount * (percentLeft))} left!`;}
-
- if (discount <= 0.99) {
- document.getElementById("origionalPrice").innerHTML = `<s><em>WAS:</em> ${makePrice(data[itemId].origionalPrice)}</s>`;
- } else {
- document.getElementById("origionalPrice").innerHTML = `<em>NOW:</em> ${makePrice(data[itemId].origionalPrice)}`;
- }
- });
-}
-
-// function handles all animated events that are required to look smooth (marquee movement / price changes) (200 times per second)
-function frame() {
-
- // move the current discount towards the target distance at the supplied rate
- if (Math.abs(discount - newDiscount) <= priceChange / 200) {
- discount = newDiscount;
- } else if (discount > newDiscount) {
- discount -= priceChange / 200;
- } else {
- discount += priceChange / 200;
- }
-
- const topTextElement = document.getElementById("topText")
- const bottomTextElement = document.getElementById("bottomText")
-
- const marqueeContainer = document.getElementsByClassName("marquee")[0];
- const marqueeElement = document.getElementsByClassName("marqueeText")[0];
-
- marqueeElement.style.transform = `translateX(${marqueeOffset}px)`;
- marqueeOffset += 0.2;
-
- // only fetch data where neccicary or where the page has just loaded
- if (marqueeOffset >= marqueeContainer.offsetWidth || (topTextElement.innerHTML == "" && bottomTextElement.innerHTML == "")) {
- fetch("./data.json", {cache: "no-store"})
- .then(data => data.json())
- .then(data => {
- topTextElement.innerHTML = data.topText[topTextCounter];
- topTextCounter += 1;
- if (topTextCounter >= data.topText.length) {
- topTextCounter = 0;
- }
-
- bottomTextElement.innerHTML = data.bottomText[bottomTextCounter];
- bottomTextCounter += 1;
- if (bottomTextCounter >= data.bottomText.length) {
- bottomTextCounter = 0;
- }
- });
-
- // create a canvas element to measure the size of text precicely
- const canvas = document.getElementById("canvas");
- const ctx = canvas.getContext("2d");
-
- var style = window.getComputedStyle(marqueeElement, null).getPropertyValue("font-size");
- var fontSize = parseFloat(style);
-
- ctx.font = `bold ${fontSize}px sans-serif`;
- let text = ctx.measureText(marqueeElement.innerHTML);
-
- marqueeOffset = -1 * text.width;
- }
-}
-// function handles the periodic drawing of the sigils (2 per second)
-function draw() {
- if (showingSigils == false) {return;} // break if the sigils are not being shown to save resources
- fetch("./sigil.json", {cache: "no-store"})
- .then(data => data.json())
- .then(data => {
-
- // itterate through each of the sigils
- for (let sigilIndex = 0; sigilIndex < document.getElementsByClassName("sigil").length; sigilIndex ++) {
- const sigilCap = document.getElementsByClassName("sigilCap")[sigilIndex];
- const sigilLine = document.getElementsByClassName("sigilLine")[sigilIndex];
- const sigilEnd = document.getElementsByClassName("sigilEnd")[sigilIndex];
-
- let phrase = data.phrases[Math.floor(Math.random()*data.phrases.length)];
- let pointString = "";
-
- for (let i = 0; i < phrase.length; i++) {
- let point = phrase.charCodeAt(i) - 97;
- if (i == 0) {
- var startPoint = data.square[point];
- }
- else if (i == phrase.length - 1) {
- var endPoint = data.square[point];
- }
- pointString += `${data.square[point]} `;
- }
-
- startPoint = startPoint.split(",");
- endPoint = endPoint.split(",");
-
- sigilCap.setAttribute("x1", Number(startPoint[0]) - 5)
- sigilCap.setAttribute("y1", startPoint[1])
- sigilCap.setAttribute("x2", Number(startPoint[0]) + 5)
- sigilCap.setAttribute("y2", startPoint[1])
- sigilLine.setAttribute("points", pointString);
- sigilEnd.setAttribute("cx", endPoint[0]);
- sigilEnd.setAttribute("cy", endPoint[1]);
- }
- })
-}
-setInterval(frame, 5);
-setInterval(update, 500);
-setInterval(draw, 500)
- </script>
- </head>
- <body id="all" class="show" onload="frame();update();draw();">
-
- <canvas id="canvas" style="display: none;"></canvas>
-
- <div class="container side show">
- <div class="box main">
- <h1 id="code">Code</h1>
- <h3 id="raiting">Raiting</h3>
- <h2 class="feature" id="subtext">Subtext</h2>
- <h3 id="description">Description</h3>
- <hr>
- <h4 id="origionalPrice">Origional Price</h4>
- <div class="badgeContainer">
- <img class="badge" src="./assets/star.svg"></img>
- <h2 class="badge" id="badgeText">Percent off</h2>
- </div>
- </div>
- <div class="box extra">
- <h2 style="margin-top: 1vh;" id="currentPrice">Current Price</h4>
- <p id="monthlyPrice">Monthly Price</p>
- <div class="soldBox">
- <p style="font-size: 1.2em;">In stock: <span style="margin-right:10px;" id="stock"></span> Sold: <span id="sold"></span></p>
- <p style="font-size: 1.6em;" class="alert" id="unitsLeft"></p>
- </div>
- </div>
- </div>
-
- <div class="container box bottom">
- <h3 class="feature timer1 box"></h3>
- <div id="topTextBox">
- <h2 id="topText"></h2><p id="idText">Welcome to <span style="font-family: serif;">XMDV</span> Shopping!</p>
- </div>
- <div class="marquee">
- <h1 class="marqueeText" id="bottomText"></h1>
- </div>
- </div>
-
- <div class="container box banner">
- <h1 id="bannerText"></h1>
- </div>
- <div class="timer2 box"></div>
- <div class="timer3"></div>
-
- <div class="container box sigilBox" id="sigilNE">
- <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
- <path class="sigilGrid" d="
- M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
- M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
- " />
-
- <line class="sigilCap"/>
- <polyline class="sigilLine"/>
- <circle class="sigilEnd" r="3">
- </svg>
- </div>
- <div class="container box sigilBox" id="sigilSE">
- <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
- <path class="sigilGrid" d="
- M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
- M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
- " />
- <line class="sigilCap"/>
- <polyline class="sigilLine"/>
- <circle class="sigilEnd" r="3">
- </svg>
- </div>
- <div class="container box sigilBox" id="sigilSW">
- <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
- <path class="sigilGrid" d="
- M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
- M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
- " />
- <line class="sigilCap"/>
- <polyline class="sigilLine"/>
- <circle class="sigilEnd" r="3">
- </svg>
- </div>
- <div class="container box sigilBox" id="sigilNW">
- <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
- <path class="sigilGrid" d="
- M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
- M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
- " />
- <line class="sigilCap"/>
- <polyline class="sigilLine"/>
- <circle class="sigilEnd" r="3">
- </svg>
- </div>
- </body>
-</html>
+++ /dev/null
-[
- {
- "code":"Placeholder item code",
- "rating":"★★★★★",
- "subtext":"Test item name",
- "description":"Test item description",
- "origionalPrice":0,
- "stockCount":0,
- "notes":"<p>[autocue] Showing: test product.<br>Commencing shortly.</p>",
- "crew_notes":"testing"
- },
- {
- "code":"Item 1",
- "rating":"✶✶✶✶✶",
- "subtext":"Gold <em>Stick-Strip</em>™",
- "description":"50m shiny gold <em>Stick-Strip</em>™! Origional beautiful formula! Cannot be broken! Too beautiful! Stuck!",
- "origionalPrice":500,
- "stockCount":100,
- "notes":"<p>ITEM IS A REEL OF GOLDEN TAPE THAT CANNOT BE TORN</p><ul><li>EMPASIZE AS MUCH AS POSSIBLE HOW BEAUTIFUL IT IS</li></ul>",
- "crew_notes":"testing"
- },
- {
- "code":"Item 2",
- "rating":"✦✦✦",
- "subtext":"Item subtext",
- "description":"Product 2 description. It's really long to see if the badge element moves down the page at all. I f**king hate CSS positioning. Oops family show.",
- "origionalPrice":170,
- "stockCount":200,
- "notes":"Some more notes...",
- "crew_notes":"testing"
- }
-]
+++ /dev/null
-5 cat lookin' things\r
-T3 (unshown) corresponds to a doomsday strike
\ No newline at end of file
--- /dev/null
+CREATE TABLE state (
+ id INTEGER PRIMARY KEY,
+ item_id INTEGER,
+ discount REAL,
+ discount_change INTEGER,
+ percent_remaining INTEGER,
+ currency_symbol TEXT,
+ crawler_top_index INTEGER,
+ list_crawler_bottom TEXT,
+ banner_index INTEGER,
+ bool_prefix INTEGER,
+ bool_rounding INTEGER,
+ bool_all INTEGER,
+ bool_product INTEGER,
+ bool_extra INTEGER,
+ bool_banner INTEGER,
+ bool_sigil_1 INTEGER,
+ bool_sigil_2 INTEGER,
+ bool_sigil_3 INTEGER,
+ bool_sigil_4 INTEGER,
+ bool_number INTEGER,
+ end_timer_1 INTEGER,
+ end_timer_2 INTEGER,
+ end_timer_3 INTEGER,
+ end_timer_4 INTEGER,
+ end_timer_5 INTEGER,
+ end_timer_6 INTEGER,
+ focus_timer_1 INTEGER,
+ focus_timer_2 INTEGER,
+ focus_timer_3 INTEGER,
+ focus_timer_4 INTEGER,
+ focus_timer_5 INTEGER,
+ focus_timer_6 INTEGER,
+ timer_offset INTEGER,
+ current_position INTEGER,
+ movement_speed INTEGER,
+ movement_function TEXT,
+ note TEXT
+);
+INSERT INTO state (
+ id,
+ item_id,
+ discount,
+ discount_change,
+ percent_remaining,
+ currency_symbol,
+ crawler_top_index,
+ list_crawler_bottom,
+ banner_index,
+ bool_prefix,
+ bool_rounding,
+ bool_all,
+ bool_product,
+ bool_extra,
+ bool_banner,
+ bool_sigil_1,
+ bool_sigil_2,
+ bool_sigil_3,
+ bool_sigil_4,
+ bool_number,
+ end_timer_1,
+ end_timer_2,
+ end_timer_3,
+ end_timer_4,
+ end_timer_5,
+ end_timer_6,
+ focus_timer_1,
+ focus_timer_2,
+ focus_timer_3,
+ focus_timer_4,
+ focus_timer_5,
+ focus_timer_6,
+ timer_offset,
+ current_position,
+ movement_speed,
+ movement_function,
+ note) VALUES (1, 0, 0.0, 50, 100, '£', 0, '[0,1]', 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 40, 'linear', 'test note');
+++ /dev/null
-{
- "square":[
- "110,10",
- "90,110",
- "50,10",
- "70,110",
- "30,110",
- "10,10",
- "10,30",
- "90,30",
- "70,90",
- "50,90",
- "30,30",
- "110,90",
- "110,70",
- "30,50",
- "70,50",
- "50,50",
- "90,70",
- "10,70",
- "10,50",
- "30,70",
- "70,70",
- "50,70",
- "90,50",
- "110,50",
- "10,90",
- "90,90",
- "50,30",
- "70,30",
- "30,90",
- "110,30",
- "110,110",
- "30,10",
- "50,110",
- "70,10",
- "90,10",
- "10,110"
- ],
- "phrases":[
- "anchor",
- "thebeast",
- "buyitall",
- "consumer",
- "runners",
- "xmdv",
- "broadcast",
- "bleedout",
- "quant",
- "bullion",
- "adarksun",
- "dollar",
- "beast"
- ]
-
-}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="200"
+ height="200"
+ viewBox="0 0 52.916665 52.916666"
+ version="1.1"
+ id="svg5"
+ inkscape:export-filename="clock.svg"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ 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="namedview7"
+ pagecolor="#5b5b5b"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="true"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="mm"
+ showgrid="false" />
+ <defs
+ id="defs2">
+ <marker
+ style="overflow:visible"
+ id="Arrow1L"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow1L"
+ markerWidth="5"
+ markerHeight="2.8571429"
+ viewBox="0 0 8.75 5"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ style="fill:context-stroke;fill-rule:evenodd;stroke:none"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ id="arrow1L"
+ transform="scale(-0.5)" />
+ </marker>
+ </defs>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <circle
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-opacity:1"
+ id="path111"
+ cx="26.458334"
+ cy="26.45833"
+ r="25.135416" />
+ <circle
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:1.17475, 1.32292;stroke-dashoffset:0.529167;stroke-opacity:1"
+ id="path111-3"
+ cx="26.458334"
+ cy="26.458334"
+ r="23.846424"
+ inkscape:export-filename="clock.svg"
+ inkscape:export-xdpi="103.64"
+ inkscape:export-ydpi="103.64" />
+ <path
+ style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1;marker-end:url(#Arrow1L)"
+ d="M 26.458333,26.458333 V 9.2604167"
+ id="path2766"
+ sodipodi:nodetypes="cc" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="200"
+ height="200"
+ viewBox="0 0 52.916665 52.916666"
+ version="1.1"
+ id="svg5"
+ 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="namedview7"
+ pagecolor="#5b5b5b"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="true"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="mm"
+ showgrid="false" />
+ <defs
+ id="defs2">
+ <marker
+ style="overflow:visible"
+ id="Arrow1L"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow1L"
+ markerWidth="5"
+ markerHeight="2.8571429"
+ viewBox="0 0 8.75 5"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ style="fill:context-stroke;fill-rule:evenodd;stroke:none"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ id="arrow1L"
+ transform="scale(-0.5)" />
+ </marker>
+ </defs>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <circle
+ style="display:inline;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-opacity:1"
+ id="path111"
+ cx="26.458334"
+ cy="26.45833"
+ r="25.135416" />
+ <circle
+ style="display:inline;fill:none;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:1.17475, 1.32292;stroke-dashoffset:0.529167;stroke-opacity:1"
+ id="path111-3"
+ cx="26.458334"
+ cy="26.458334"
+ r="23.846424"
+ inkscape:export-filename="clock.svg"
+ inkscape:export-xdpi="103.64"
+ inkscape:export-ydpi="103.64" />
+ <path
+ style="display:none;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.32292;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1;marker-end:url(#Arrow1L)"
+ d="M 26.458333,26.458333 V 9.2604167"
+ id="path2766"
+ sodipodi:nodetypes="cc" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="400"
+ height="400"
+ viewBox="0 0 105.83333 105.83333"
+ version="1.1"
+ id="svg1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs1" />
+ <metadata
+ id="metadata1">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <path
+ style="fill:#bd0000;fill-opacity:1;stroke-width:0.264798;stroke-dasharray:none"
+ id="path1"
+ d="M 71.019533,63.52164 69.13196,77.122826 56.678371,71.338072 48.243092,82.173258 40.350345,70.93672 27.627586,76.102627 26.410535,62.425126 12.809349,60.537554 18.594102,48.083965 7.7589164,39.648686 18.995454,31.755938 13.829547,19.03318 27.507048,17.816129 29.394621,4.214943 41.84821,9.9996963 50.283489,-0.83548968 58.176236,10.401048 70.898995,5.2351411 l 1.217051,13.6775009 13.601186,1.887573 -5.784754,12.453589 10.835186,8.435278 -11.236537,7.892748 5.165907,12.722759 z"
+ transform="matrix(1.274966,0,0,1.274966,-9.8923536,1.065222)" />
+</svg>
--- /dev/null
+@font-face {
+ font-family: "Archivo";
+ font-weight: normal;
+ font-style: normal;
+ src:
+ local("Archivo"),
+ url("fonts/Archivo-Regular.woff") format("woff"),
+}
+@font-face {
+ font-family: "Archivo";
+ font-weight: bold;
+ font-style: normal;
+ src:
+ local("Archivo"),
+ url("fonts/Archivo-SemiBold.woff") format("woff"),
+}
+@font-face {
+ font-family: "Archivo";
+ font-weight: normal;
+ font-style: italic;
+ src:
+ local("Archivo"),
+ url("fonts/Archivo-Italic.woff") format("woff"),
+}
+@font-face {
+ font-family: "Archivo";
+ font-weight: bold;
+ font-style: italic;
+ src:
+ local("Archivo"),
+ url("fonts/Archivo-SemiBoldItalic.woff") format("woff"),
+}
--- /dev/null
+{
+ "text": {
+ "crawler_top":[
+ "Test top text 1",
+ "Test top text 2",
+ "It's here!"
+ ],
+ "crawler_bottom":[
+ "Something bright and radiant for all of you at home!",
+ "I take your life and raise you 10 over the hour!",
+ "We grab the bullion counterweight and throw ourselves down!",
+ "We have been falling for a looong time people!",
+ "Let me slip into something less spacious!",
+ "Down the side of a large golden pyramid!",
+ "So we dress ourselves in your Ballast!",
+ "I used to be so unimaginably tall!",
+ "It's here!"
+ ],
+ "banner":[
+ "Buy XMDV!",
+ "Discounts on XMDV!",
+ "Imagine thinking on a piece of paper!",
+ "Imagine thinking on a dime!",
+ "Imagine thinking on a dollar!",
+ "The quant we created!",
+ "The quant we fear!",
+ "It's here!"
+ ],
+ "sigil":[
+ "anchor",
+ "thebeast",
+ "buyitall",
+ "consumer",
+ "runners",
+ "xmdv",
+ "broadcast",
+ "bleedout",
+ "quant",
+ "bullion",
+ "adarksun",
+ "dollar",
+ "beast"
+ ],
+ "square":[
+ "110,10",
+ "90,110",
+ "50,10",
+ "70,110",
+ "30,110",
+ "10,10",
+ "10,30",
+ "90,30",
+ "70,90",
+ "50,90",
+ "30,30",
+ "110,90",
+ "110,70",
+ "30,50",
+ "70,50",
+ "50,50",
+ "90,70",
+ "10,70",
+ "10,50",
+ "30,70",
+ "70,70",
+ "50,70",
+ "90,50",
+ "110,50",
+ "10,90",
+ "90,90",
+ "50,30",
+ "70,30",
+ "30,90",
+ "110,30",
+ "110,110",
+ "30,10",
+ "50,110",
+ "70,10",
+ "90,10",
+ "10,110"
+ ]
+ },
+ "items": [
+ {
+ "code":"Placeholder item code",
+ "rating":"★★★★★",
+ "subtext":"Test item name",
+ "description":"Test item description",
+ "origional_price":0,
+ "stock_count":0,
+ "notes":"<p>[autocue] Showing: test product.<br>Commencing shortly.</p>",
+ "crew_notes":"testing"
+ },
+ {
+ "code":"Item 1",
+ "rating":"✶✶✶✶✶",
+ "subtext":"Gold <em>Stick-Strip</em>™",
+ "description":"50m shiny gold <em>Stick-Strip</em>™! Origional beautiful formula! Cannot be broken! Too beautiful! Stuck!",
+ "origional_price":500,
+ "stock_count":100,
+ "notes":"<p>ITEM IS A REEL OF GOLDEN TAPE THAT CANNOT BE TORN</p><ul><li>EMPASIZE AS MUCH AS POSSIBLE HOW BEAUTIFUL IT IS</li></ul>",
+ "crew_notes":"testing"
+ },
+ {
+ "code":"Item 2",
+ "rating":"✦✦✦",
+ "subtext":"Item subtext",
+ "description":"Product 2 description. It's really long to see if the badge element moves down the page at all. I f**king hate CSS positioning. Oops family show.",
+ "origional_price":170,
+ "stock_count":200,
+ "notes":"Some more notes...",
+ "crew_notes":"testing"
+ }
+ ]
+}
--- /dev/null
+#!.venv/bin/python
+
+from flask import Flask, Response, request, render_template, jsonify
+from os import path
+import sqlite3
+import json
+from ast import literal_eval
+
+app = Flask(__name__)
+
+def database(field: str | list):
+ query = f"SELECT {','.join(field) if type(field) == list else field} FROM state WHERE id=1;"
+
+ with sqlite3.connect(path.join(app.root_path, "data.db")) as connection:
+ cursor = connection.cursor()
+ result = cursor.execute(query).fetchone()
+
+ column_names = [description[0] for description in cursor.description]
+
+ return dict(zip(column_names, result)) # combine column names with data into dict
+
+@app.route("/")
+def gfx_main():
+ return Response(render_template("gfx.html"), mimetype="text/html")
+
+@app.route("/<string:page>")
+def gfx_page(page):
+ if page in ["autocue", "docs"]:
+ return Response(render_template(f"{page}.html"), mimetype="text/html")
+ else:
+ return "", 404
+
+@app.route("/admin")
+def admin_main():
+ ...
+
+@app.route("/admin/<string:page>")
+def admin_page(page):
+ ...
+
+@app.route("/api")
+def api():
+ if request.method == "GET":
+ data = database("*")
+
+ focus_extra = {}
+ for key, value in data.items():
+ if key[:4] == "bool": data[key] = bool(value) # if the key starts with "bool" make the data a bool
+ elif key[:4] == "list": data[key] = literal_eval(value) # if the key starts with "list" make the data a literal list
+ elif key[:5] == "focus": # if the key starts with "focus" make the data into a bool with an additional dict entry for "showing" bool
+ data[key] = bool((value-1)+abs(value-1)) # if 2 then True, if 1,0 then False
+ focus_extra[f"bool_{key[6:]}"] = bool(value) # if 1,2 then True, if 0 then False
+ data |= focus_extra
+
+ return jsonify(data)
+
+ elif request.method == "POST":
+ ...
+ else:
+ return "", 404
+
+@app.route("/api/items")
+def api_items():
+ if request.method == "GET":
+ return app.send_static_file("static.json")
+
+ else:
+ return "", 404
+
+@app.route("/api/clock")
+def api_clock():
+ if request.method == "GET":
+ keys = ["current_position", "movement_speed", "movement_function"]
+ return jsonify(database(keys))
+
+ else:
+ return "", 404
+
+if __name__ == "__main__":
+ app.run(host='127.0.0.1', port=5000, debug=True)
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>XMDV</title>
+ <style>
+body {
+ font-family: sans-serif;
+ color: white;
+ background-color: black;
+}
+#slider {
+ position: fixed;
+ top: 10px;
+ left: 10px;
+}
+#notes, #message {
+ margin: 20px;
+}
+#timer {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ margin: 20px;
+}
+#clock {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 250px;
+ height: 250px;
+ margin: 10px;
+}
+#arrow {
+ transition: transform 1.5s;
+}
+ </style>
+ <script>
+
+function makeTime(end, offset, strike) {
+ let current = Math.round((Date.now() + offset) / 1000);
+ var time = end - current;
+
+ if (Math.sign(time) == -1) {time = 0;}
+ var minutes = Math.floor(time / 60);
+ var seconds = (time - (minutes * 60));
+
+ var minutesString = minutes.toString().padStart(2, "0");
+ var secondsString = seconds.toString().padStart(2, "0");
+ if (strike) {
+ return `${minutesString}:${secondsString}`;
+ } else {
+ return `<s>${minutesString}:${secondsString}</s>`;
+ }
+}
+
+// dynamically resize the UI
+function resize() {
+ var slider = document.getElementById("slider");
+ for (let id of ["note", "producer", "timer"]) {
+ document.getElementById(id).style.fontSize = `${slider.value}px`;
+ }
+}
+
+function update () {
+ // fetch the item manifest and chache it
+ fetch("./api/items", {cache: "default"})
+ .then(data => data.json())
+ .then(data => data.items)
+ .then(items => {
+ // fetch the current state without cache
+ fetch("./api", {cache: "no-store"})
+ .then(data => data.json())
+ .then(data => {
+ let id = data.item_id;
+
+ // update the producer note
+ const producer = document.getElementById("producer");
+ producer.innerHTML = data.note.replaceAll("\n", "<br>");
+
+ // update the item information
+ const note = document.getElementById("note");
+ note.innerHTML = items[id].notes;
+
+ // update the clock
+ document.getElementById("arrow").style.transform = `rotate(${data.current_position}deg)`;
+
+ // update the total items and the items sold already
+ document.getElementById("left").innerHTML = Math.round(
+ items[id].stock_count * (data.percent_remaining / 100)
+ );
+ document.getElementById("stock").innerHTML = items[id].stock_count;
+
+ // update the timers
+ for (let t = 1; t <= 6; t++) {
+ document.getElementById(`timer_${t}`).innerHTML = makeTime(
+ data[`end_timer_${t}`],
+ data['timer_offset'],
+ data[`bool_timer_${t}`]
+ )
+ }
+ })
+ });
+}
+
+setInterval(resize, 50);
+setInterval(update, 1000);
+
+ </script>
+ </head>
+ <body onload="resize(); update();">
+ <input type="range" min="30" max="100" value="45" id="slider">
+ <div style="font-size: 50px;" id="note">some test text!</div>
+ <div style="font-size: 50px; color: yellow;" id="producer">Producer Notes!</div>
+ <div id="timer">
+ T1: <span style="background-color: red;" id="timer_1"></span> -
+ T2: <span style="background-color: green;" id="timer_2"></span> -
+ T3: <span style="background-color: blue;" id="timer_3"></span><br>
+ T4: <span style="background-color: yellow;" id="timer_4"></span> -
+ T5: <span style="background-color: magenta;" id="timer_5"></span> -
+ T6: <span style="background-color: orange; margin-right: 40px;" id="timer_6"></span>
+ Left: <span id="left"></span>/<span id="stock"></span>
+ </div>
+ <div id="clock">
+ <img style="width: 100%; margin-right: -100%;" src="./static/assets/clock.svg"><img id="arrow" style="width: 100%;" src="./static/assets/arrow.svg">
+ </div>
+ </body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+body {
+ --star-light: #FFE0B3;
+ --star-dark: #FFC266;
+ --dark: #DEDEDE;
+ --black: #3b3b45;
+ --feature-dark: #AAAAEE;
+ --feature-light: #BBBBEE;
+ --feature-white: #EAEAFA;
+ --background-light: #FFFFFF;
+ --background-dark: #EEEEEE;
+ --feature-gradient: linear-gradient(var(--feature-dark), var(--feature-light));
+ --background: linear-gradient(var(--background-light), var(--background-dark));
+ --clock: conic-gradient(var(--background-light) 0deg, var(--background-light) 0deg, rgb(0,0,0,0) 0.1deg), rgb(0,0,0,0) 360deg);
+
+ font-family: "Archivo", sans-serif;
+ font-size: 1.6vh;
+ color: var(--black);
+
+ opacity: 0;
+ transition: opacity 1.5s;
+}
+@keyframes spin {
+ 0% {transform: rotate(0turn) scale(1.2);}
+ 50% {transform: rotate(0.25turn) scale(1);}
+ 100% {transform: rotate(0.5turn) scale(1.2);}
+}
+@keyframes spinText {
+ 0% {transform: rotate(-0.01turn);}
+ 50% {transform: rotate(0.01turn);}
+ 100% {transform: rotate(-0.01turn);}
+}
+.container {
+ position: absolute;
+ border-radius: 1vh;
+}
+.box {
+ border-radius: 1vh;
+ box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
+ background-image: var(--background);
+}
+#side {
+ left: 6vh;
+ top: 6vh;
+ width: 40vh;
+ opacity: 0;
+ transition: opacity 1.5s;
+}
+.soldBox {
+ margin: 5px;
+ padding: 5px;
+ border-radius: 5px;
+ background-image: var(--background);
+ box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.19) 0px 1px 2px;
+}
+.soldBox > * {
+ margin: 0;
+ font-style: italic;
+}
+.soldBox > * > span {
+ font-weight: bold;
+ font-style: normal;
+}
+#raiting {
+ margin: 5px;
+ margin-top: 0;
+ border-radius: 5px;
+ font-size: 2em;
+ line-height: 1em;
+ color: var(--star-dark);
+ text-shadow: -1px -1px 0 var(--star-light), 1px -1px 0 var(--star-light), -1px 1px 0 var(--star-light), 1px 1px 0 var(--star-light), 2px 2px 0 var(--dark);
+ background-image: linear-gradient(var(--background-dark), var(--dark));
+ box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.19) 0px 1px 2px;
+}
+.main {
+ z-index: 0;
+}
+#banner {
+ left: 48vh;
+ top: 6vh;
+ height: 2em;
+ width: 0vh;
+ padding: 0.5vh;
+ overflow: hidden;
+ opacity: 0;
+ transition: opacity 1.5s, width 1.5s, left 1.5s;
+ background: linear-gradient(var(--feature-white), rgb(0,0,0,0), var(--feature-white)), var(--background);
+ border: 1px solid var(--feature-dark);
+}
+#banner.moved, #timer2.moved {
+ left: 6vh;
+}
+#timer2.movedUp {
+ top: 6vh;
+}
+#timer3.focus {
+ bottom: calc(50% - 15vh);
+ right: calc(50% - 15vh);
+ width: 30vh;
+ height: 30vh;
+ border-radius: 15vh;
+ font-size: 6em;
+ line-height: 30vh;
+}
+.extra {
+ position: relative;
+ top: -2vh;
+ z-index: -1;
+ transform: translateY(-100%);
+ padding-top: 2vh;
+ padding-bottom: 1px;
+ background-image: var(--feature-gradient);
+ transition: transform 1.5s;
+}
+.showExtra > .extra {
+ transform: translateY(0%);
+}
+.main > *, .extra > *:not(.soldBox), .bottom > *, .bottom > div > *, #marquee > * {
+ margin: 0;
+ padding: 0.5vh 1vh;
+}
+#banner > h1 {
+ width: 49vh;
+ margin: 0;
+ font-size: 2em;
+ line-height: 1;
+ font-style: italic;
+ font-weight: normal;
+ text-align: center;
+}
+.sigilBox {
+ opacity: 0;
+ transition: opacity 10s;
+}
+.sigil {
+ width: 20vh;
+ height: 20vh;
+}
+#sigil1 {
+ right: 4vh;
+ top: 4vh;
+}
+#sigil2 {
+ right: 4vh;
+ bottom: 4vh;
+}
+#sigil3 {
+ left: 4vh;
+ bottom: 4vh;
+}
+#sigil4 {
+ left: 4vh;
+ top: 4vh;
+}
+#badgeContainer {
+ position: relative;
+ padding: 0;
+}
+.badge {
+ position: absolute;
+ top: -2vh;
+ left: 32vh;
+ width: 10vh;
+ height: 10vh;
+ padding: 0;
+ animation: spin 10s linear 0s infinite;
+}
+h2.badge {
+ top: -1vh;
+ left: 26.5vh;
+ color: white;
+ width: 20vh;
+ animation: spinText 10s linear 0s infinite;
+ rotate: 0.025turn;
+ text-align: center;
+ text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
+}
+#badgeContainer {
+ transform: translateX(12vh) scale(0%);
+ transition: transform 1.5s;
+}
+.main > hr, .extra > hr {
+ margin: 0 1vh;
+ padding: 0;
+ border-color: var(--dark);
+}
+.feature {
+ border-top: 2px solid var(--dark);
+ border-bottom: 2px solid var(--dark);
+ background-image: var(--feature-gradient);
+}
+.bottom {
+ left: 6vh;
+ bottom: 6vh;
+ width: 100vh;
+}
+.bottom > *:not(#timer1) {
+ display: inline-block;
+ vertical-align: top;
+}
+#topTextBox {
+ width: calc(100% - 20px);
+ margin: 5px;
+ margin-bottom: 0;
+ padding: 5px;
+ border-radius: 5px;
+ background-image: linear-gradient(0.25turn, var(--feature-dark), rgba(0,0,0,0) 40%), var(--background);
+ box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.19) 0px 1px 2px;
+}
+#topText {
+ display: inline-block;
+ width: 50%;
+ padding: 0;
+}
+#idText {
+ display: inline-block;
+ width: 50%;
+ padding: 0;
+ text-align: right;
+ font-size: 1.5em;
+ font-weight: bold;
+ font-style: italic;
+ text-shadow: 0 0 3px rgba(0,0,0,0.25);
+ color: var(--feature-dark);
+}
+#marquee {
+ padding: 0;
+ width: 100%;
+ mask-image: linear-gradient(0.25turn, transparent, black 5%, black 95%, transparent);
+ overflow: hidden;
+}
+#marqueeText {
+ white-space: nowrap;
+ overflow: hidden;
+}
+#timer1 {
+ position: absolute;
+ left: 101vh;
+ padding: 0.5vh;
+ opacity: 0;
+ text-align: center;
+ border: 2px solid var(--dark);
+ border-radius: calc(1vh - 2px);
+ transition: opacity 1.5s;
+}
+#timer2 {
+ position: absolute;
+ left: calc(48vh + 2px);
+ top: calc(11.5vh + 2px);
+ height: 1.2em;
+ width: 3em;
+ margin: 0;
+ text-align: center;
+ font-size: 2em;
+ font-weight: bold;
+ line-height: 1.2em;
+ border: 2px solid var(--dark);
+ outline: 2px solid var(--feature-dark);
+ opacity: 0;
+ transition: opacity 1.5s, left 1.5s, top 1.5s;
+}
+#timer3 {
+ position: absolute;
+ bottom: 4vh;
+ right: 4vh;
+ opacity: 0;
+ width: 12vh;
+ height: 12vh;
+ border-radius: 6vh;
+ margin: 0;
+ text-align: center;
+ vertical-align: center;
+ font-size: 2em;
+ font-weight: bold;
+ line-height: 12vh;
+ background-image: var(--background);
+ border: 2px solid var(--feature-dark);
+ outline: 2px solid var(--dark);
+ opacity: 0;
+ transition: opacity 1.5s, bottom 1.5s, right 1.5s, width 1.5s, height 1.5s, border-radius 1.5s, font-size 1.5s, line-height 1.5s, transform 1.5s;
+ animation: spinText 10s linear 0s infinite;
+}
+.show {
+ opacity: 1 !important;
+}
+#badgeContainer.show {
+ transform: translateX(0vh) scale(100%);
+}
+#banner.show {
+ width: 50vh;
+}
+.sigilBox.show {
+ opacity: 0.9;
+}
+@keyframes spinBox {
+ 0% {transform: translateY(100%);}
+ 50% {}
+ 100% {}
+
+}
+line, polyline, circle {
+ fill: none;
+ stroke: var(--feature-dark);
+ stroke-width: 3;
+}
+circle {
+ fill: #F2F2F2;
+}
+.sigilGrid {
+ stroke: var(--dark);
+ stroke-width: 1;
+}
+ </style>
+ <link rel="stylesheet" href="static/fonts.css">
+ </head>
+ <body id="all" class="show" onload="frame();update();">
+
+ <canvas id="canvas" style="display: none;"></canvas>
+
+ <div id=side class="container show">
+ <div class="box main">
+ <h1 id="code">Code</h1>
+ <h3 id="raiting">Raiting</h3>
+ <h2 class="feature" id="subtext">Subtext</h2>
+ <h3 id="description">Description</h3>
+ <hr>
+ <h4 id="origionalPrice">Origional Price</h4>
+ <div id="badgeContainer">
+ <img class="badge" src="./static/assets/star.svg"></img>
+ <h2 class="badge" id="badgeText">Percent off</h2>
+ </div>
+ </div>
+ <div class="box extra">
+ <h2 style="margin-top: 1vh;" id="currentPrice">Current Price</h4>
+ <p id="monthlyPrice">Monthly Price</p>
+ <div class="soldBox">
+ <p style="font-size: 1.2em;">In stock: <span style="margin-right:10px;" id="stock"></span> Sold: <span id="sold"></span></p>
+ <p style="font-size: 1.6em;" class="alert" id="unitsLeft"></p>
+ </div>
+ </div>
+ </div>
+
+ <div class="container box bottom">
+ <h3 id="timer1" class="feature box"></h3>
+ <div id="topTextBox">
+ <h2 id="topText"></h2><p id="idText">Welcome to <span style="font-family: serif;">XMDV</span> Shopping!</p>
+ </div>
+ <div id="marquee">
+ <h1 class="marqueeText" id="bottomText"></h1>
+ </div>
+ </div>
+
+ <div id="banner" class="container box">
+ <h1 id="bannerText"></h1>
+ </div>
+ <div id="timer2" class="box"></div>
+ <div id="timer3"></div>
+
+ <div class="container box sigilBox" id="sigil1">
+ <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
+ <path class="sigilGrid" d="
+ M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
+ M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
+ " />
+
+ <line class="sigilCap"/>
+ <polyline class="sigilLine"/>
+ <circle class="sigilEnd" r="3">
+ </svg>
+ </div>
+ <div class="container box sigilBox" id="sigil2">
+ <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
+ <path class="sigilGrid" d="
+ M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
+ M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
+ " />
+ <line class="sigilCap"/>
+ <polyline class="sigilLine"/>
+ <circle class="sigilEnd" r="3">
+ </svg>
+ </div>
+ <div class="container box sigilBox" id="sigil3">
+ <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
+ <path class="sigilGrid" d="
+ M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
+ M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
+ " />
+ <line class="sigilCap"/>
+ <polyline class="sigilLine"/>
+ <circle class="sigilEnd" r="3">
+ </svg>
+ </div>
+ <div class="container box sigilBox" id="sigil4">
+ <svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
+ <path class="sigilGrid" d="
+ M 10,10 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100 v 100 m 20,-100
+ M 10,10 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20 h 100 m -100,20
+ " />
+ <line class="sigilCap"/>
+ <polyline class="sigilLine"/>
+ <circle class="sigilEnd" r="3">
+ </svg>
+ </div>
+
+ <script>
+
+/*
+structure
+ *: function
+ -: section
+
+- setup of all const html elements
+- setup of all variables shared between the main call function and the other funtions
+* main call function (update) happens 2x per second
+
+- setup of all variables remebered between frame calls
+* frame function (frame) 200x per second
+
+* sigil function (sigil) 2X per second
+
+*/
+
+const all = document.getElementById("all");
+
+const timer1 = document.getElementById("timer1");
+const timer2 = document.getElementById("timer2");
+const timer3 = document.getElementById("timer3");
+const timer4 = document.getElementById("timer4");
+const timer5 = document.getElementById("timer5");
+const timer6 = document.getElementById("timer6");
+const timers = [
+ timer1,
+ timer2,
+ timer3,
+ timer4,
+ timer5,
+ timer6
+ ];
+const banner = document.getElementById("banner");
+const side = document.getElementById("side");
+const badgeContainer = document.getElementById("badgeContainer");
+// 1:NE 2:SE 3:SW 4:NW
+const sigils = [
+ document.getElementById("sigil1"),
+ document.getElementById("sigil2"),
+ document.getElementById("sigil3"),
+ document.getElementById("sigil4")
+ ];
+const remaining = document.getElementById("unitsLeft");
+const origional = document.getElementById("origionalPrice");
+const topTextElement = document.getElementById("topText");
+const bottomTextElement = document.getElementById("bottomText");
+
+const marqueeContainer = document.getElementById("marquee");
+
+// sigil cap end and line lists
+const sigilCap = document.getElementsByClassName("sigilCap");
+const sigilLine = document.getElementsByClassName("sigilLine");
+const sigilEnd = document.getElementsByClassName("sigilEnd");
+
+// all dynamic variables used between functions
+
+let discountHard = 0;
+let discountRate = 0;
+
+let topText = "";
+let bottomText = [];
+
+// dynamic variables used in frame function below update function
+
+// handles all updates from the server data that are not required to be instantanious (2 times per second)
+function update() {
+ // fetch the item manifest and chache it
+ fetch("./api/items", {cache: "default"})
+ .then(data => data.json())
+ .then(dataStatic => {
+ fetch("./api", {cache: "no-store"})
+ .then(data => data.json())
+ .then(data => {
+
+ // some variable setup
+ let id = data.item_id;
+ const items = dataStatic.items
+ const item = items[id];
+
+ // frame function variable hand-over
+ discountHard = data.discount;
+ discountRate = data.discount_change / 200; // happens 200x per second in the frame function
+ topText = dataStatic.text.crawler_top[data.crawler_top_index]
+ bottomText = [];
+ for (let i of data.list_crawler_bottom) {bottomText.push(dataStatic.text.crawler_bottom[i])}
+
+ // handle all optional elements showing/hiding
+ if (data.bool_all) {all.classList.add("show");}
+ else {all.classList.remove("show");}
+
+ // set all timers to correct time and show/focus
+ for (let t = 1; t <= 6; t++) {
+ const timer = timers[t-1];
+
+ if (timer != null) {
+ let current = Math.round((Date.now() + data['timer_offset']) / 1000);
+ var time = data[`end_timer_${t}`] - current;
+ if (Math.sign(time) == -1) {time = 0;}
+ var minutes = Math.floor(time / 60);
+ var seconds = (time - (minutes * 60));
+
+ var minutesString = minutes.toString().padStart(2, "0");
+ var secondsString = seconds.toString().padStart(2, "0");
+
+ timer.innerHTML = `${minutesString}:${secondsString}`;
+
+ if (data[`bool_timer_${t}`]) {timer.classList.add("show");}
+ else {timer.classList.remove("show");}
+
+ if (data[`focus_timer_${t}`]) {timer.classList.add("focus");}
+ else {timer.classList.remove("focus");}
+
+ // additional styling for individual timers
+ switch (t) {
+ case 2: {
+ let angle;
+ if (minutes > 0) {angle = 360;}
+ else {angle = Math.round(seconds * (360 / 60))};
+ timer2.style.background = `
+ conic-gradient(
+ rgb(0,0,0,0) 0deg,
+ rgb(0,0,0,0) ${angle}deg,
+ var(--feature-light) ${angle}.1deg,
+ var(--feature-light) 360deg
+ ), var(--background)`;
+ }
+
+ case 3: {
+ let radius;
+ if (minutes > 0) {radius = 100;}
+ else {radius = Math.round(seconds * (100 / 60));}
+ timer3.style.background = `
+ linear-gradient(
+ rgb(0,0,0,0) 0%, rgb(0,0,0,0) ${radius}%,
+ var(--feature-light) ${radius}.1%,
+ var(--feature-dark) 100%
+ ), var(--background)`;
+ }
+ }
+ }
+ }
+
+ // banner placement
+ if (data.bool_banner) {
+ if (!banner.classList.contains("show")) { // only change the banner text when the banner is turning back on
+ document.getElementById("bannerText").innerHTML = dataStatic.text.banner[data.banner_index];
+ }
+ banner.classList.add("show");
+ timer2.classList.remove("movedUp");
+ } else {
+ banner.classList.remove("show");
+ timer2.classList.add("movedUp");
+ }
+
+ // handle element showing
+ // hierarchy: main product information -> extra information -> discount badge
+ if (data.bool_product) {
+ side.classList.add("show");
+ banner.classList.remove("moved");
+ timer2.classList.remove("moved");
+
+ if (data.bool_extra) {
+ side.classList.add("showExtra");
+
+ if (discount <= 0.99) {
+ badgeContainer.classList.add("show");
+ } else {
+ badgeContainer.classList.remove("show");
+ }
+
+ } else {
+ side.classList.remove("showExtra");
+ }
+
+ } else {
+ side.classList.remove("show");
+ banner.classList.add("moved");
+ timer2.classList.add("moved");
+ side.classList.remove("showExtra");
+ badgeContainer.classList.remove("show");
+ }
+
+ // Sigil handling
+ for (let s = 0; s < 4; s++) {
+ if (data[`bool_sigil_${s+1}`]) {
+ let phrase = dataStatic.text.sigil[Math.floor(Math.random()*dataStatic.text.sigil.length)];
+ let pointString = "";
+ for (let i = 0; i < phrase.length; i++) {
+ let point = phrase.charCodeAt(i) - 97; // get the coords of the letter
+ if (i == 0) {var startPoint = dataStatic.text.square[point].split(",");} // get the start and end points
+ else if (i == phrase.length - 1) {var endPoint = dataStatic.text.square[point].split(",");}
+
+ pointString += `${dataStatic.text.square[point]} `;
+ }
+
+ sigilCap[s].setAttribute("x1", Number(startPoint[0]) - 5)
+ sigilCap[s].setAttribute("y1", startPoint[1])
+ sigilCap[s].setAttribute("x2", Number(startPoint[0]) + 5)
+ sigilCap[s].setAttribute("y2", startPoint[1])
+ sigilLine[s].setAttribute("points", pointString);
+ sigilEnd[s].setAttribute("cx", endPoint[0]);
+ sigilEnd[s].setAttribute("cy", endPoint[1]);
+
+ sigils[s].classList.add("show");
+ } else {
+ sigils[s].classList.remove("show");
+ }
+ }
+
+ // set item properties
+ document.getElementById("code").innerHTML = item.code;
+ document.getElementById("raiting").innerHTML = item.rating;
+ document.getElementById("subtext").innerHTML = item.subtext;
+ document.getElementById("description").innerHTML = item.description;
+
+ // calculate the price sting with the pre/postfix
+ let price = item.origional_price * discount;
+ if (data.bool_rounding) {
+ price = Math.round(price * 100) / 100;
+ }
+
+ let priceString;
+ let ezString;
+ if (data.bool_prefix) {priceString = `${data.currency_symbol}${price}`}
+ else {priceString = `${price}${data.currency_symbol}`};
+ let ezPrice = Math.round((price * 1.1) / 12);
+ if (data.bool_prefix) {ezString = `${data.currency_symbol}${ezPrice}`}
+ else {ezString = `${ezPrice}${data.currency_symbol}`};
+
+ // set discount, pricing and ez pay
+ document.getElementById("currentPrice").innerHTML = `<em>Now only:</em> ${priceString}`;
+ document.getElementById("monthlyPrice").innerHTML = `12 monthly payments of <b>${ezString}</b>`;
+ document.getElementById("badgeText").innerHTML = `${Math.round((1 - discount) * 100)}% OFF`;
+ document.getElementById("stock").innerHTML = `${item.stock_count} units`;
+ document.getElementById("sold").innerHTML = `${Math.round(item.stock_count * (1 - data.percent_remaining))} units`;
+
+ if (discount <= 0.99) {
+ let origionalPrice = Math.round(item.origional_price);
+ if (data.bool_prefix) {origionalString = `${data.currency_symbol}${origionalPrice}`}
+ else {origionalString = `${origionalPrice}${data.currency_symbol}`};
+ origional.innerHTML = `<s><em>WAS:</em> ${origionalString}</s>`;
+ } else {
+ origional.innerHTML = `<em>NOW:</em> ${priceString}`;
+ }
+
+ if (data.percent_remaining == 0) {
+ remaining.innerHTML = "Sold out!";
+ }
+ else { // work out the prefix for the discount to be more reactive
+ let descriptior;
+ if (data.percent_remaining < 0.1) {descriptor = "Quick! Only"}
+ else if (data.percent_remaining < 0.25) {descriptor = "Only"}
+ else if (data.percent_remaining < 0.5) {descriptor = "Just"}
+ else {descriptor = ""}
+ remaining.innerHTML = `
+ ${descriptor} ${Math.round(item.stock_count * (data.percent_remaining))} left!
+ `; }
+ });
+ });
+}
+
+// frame dynamic variables
+
+let discount = 0;
+let marqueeOffset = 0;
+let bottomTextIndex = 0;
+
+// function handles all animated events that are required to look smooth (marquee movement / price changes) (200 times per second)
+function frame() {
+
+ // move the current discount towards the target distance at the supplied rate
+ if (Math.abs(discount - discountHard) <= discountRate) { // if the discount is within one tick, snap to current discount
+ discount = discountHard;
+ } else if (discount > discountHard) {
+ discount -= discountRate;
+ } else {
+ discount += discountRate;
+ }
+
+ bottomTextElement.style.transform = `translateX(${marqueeOffset}px)`;
+ marqueeOffset += 0.2;
+
+ // only replace text where neccicary or where the page has just loaded
+ if (marqueeOffset >= marqueeContainer.offsetWidth || (topTextElement.innerHTML == "" && bottomTextElement.innerHTML == "")) {
+
+ topTextElement.innerHTML = topText;
+
+ bottomTextElement.innerHTML = bottomText[bottomTextIndex];
+ bottomTextIndex += 1;
+ if (bottomTextIndex >= bottomText.length) { // make sure the changed list wont ovreflow the bottom text index
+ bottomTextIndex = 0;
+ }
+
+ // use a canvas element to measure the size of text precicely
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext("2d");
+
+ var style = window.getComputedStyle(bottomTextElement, null).getPropertyValue("font-size");
+ var fontSize = parseFloat(style);
+
+ ctx.font = `bold ${fontSize}px sans-serif`;
+ let text = ctx.measureText(bottomTextElement.innerHTML);
+
+ marqueeOffset = -1.1 * text.width; // place text just before its end-point
+ }
+}
+
+setInterval(frame, 5);
+setInterval(update, 500);
+
+ </script>
+ </body>
+</html>
+++ /dev/null
-{
- "topText":[
- "Test top text 1",
- "Test top text 2",
- "It's here!"
- ],
- "bottomText":[
- "Something bright and radiant for all of you at home!",
- "I take your life and raise you 10 over the hour!",
- "We grab the bullion counterweight and throw ourselves down!",
- "We have been falling for a looong time people!",
- "Let me slip into something less spacious!",
- "Down the side of a large golden pyramid!",
- "So we dress ourselves in your Ballast!",
- "I used to be so unimaginably tall!",
- "It's here!"
- ],
- "bannerText":[
- "Buy XMDV!",
- "Discounts on XMDV!",
- "Imagine thinking on a piece of paper!",
- "Imagine thinking on a dime!",
- "Imagine thinking on a dollar!",
- "The quant we created!",
- "The quant we fear!",
- "It's here!"
- ]
-}