CREATE TABLE state (
id INTEGER PRIMARY KEY,
item_id INTEGER,
+ discount INTEGER,
discount_1 INTEGER,
discount_2 INTEGER,
discount_3 INTEGER,
discount_4 INTEGER,
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,
end_timer_5 INTEGER,
end_timer_6 INTEGER,
end_timer_main INTEGER,
+ end_timer_shout INTEGER,
focus_timer_1 INTEGER,
focus_timer_2 INTEGER,
focus_timer_3 INTEGER,
current_position INTEGER,
movement_speed INTEGER,
movement_function TEXT,
+ shout TEXT,
note TEXT
);
INSERT INTO state (
id,
item_id,
+ discount,
discount_1,
discount_2,
discount_3,
discount_4,
discount_change,
percent_remaining,
- currency_symbol,
crawler_top_index,
list_crawler_bottom,
banner_index,
- bool_prefix,
bool_rounding,
bool_all,
bool_product,
end_timer_4,
end_timer_5,
end_timer_6,
+ end_timer_main,
+ end_timer_shout,
focus_timer_1,
focus_timer_2,
focus_timer_3,
current_position,
movement_speed,
movement_function,
- note) VALUES (1, 0, 0.0, 0.0, 0.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');
+ shout,
+ note) VALUES (1, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 50, 100, 0, '[0,1]', 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 40, 'linear', 'test shout', 'test note');
--- /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"
+ sodipodi:docname="star5.svg"
+ inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+ 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"
+ 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/">
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#696969"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="0.49132984"
+ inkscape:cx="177.07046"
+ inkscape:cy="193.3528"
+ inkscape:window-width="1366"
+ inkscape:window-height="704"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1" />
+ <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
+ sodipodi:type="star"
+ style="fill:#ff4eac;fill-opacity:1;stroke:#ffffff;stroke-width:3.36072;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+ id="path1"
+ inkscape:flatsided="false"
+ sodipodi:sides="9"
+ sodipodi:cx="26.30571"
+ sodipodi:cy="27.046715"
+ sodipodi:r1="53.991825"
+ sodipodi:r2="37.25436"
+ sodipodi:arg1="0.81451614"
+ sodipodi:arg2="1.163582"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 63.356003,66.320025 41.06041,61.254699 29.443484,80.947286 15.619998,62.73569 -5.9372351,70.35387 -4.8204511,47.517412 -26.231122,39.496555 -10.696624,22.720668 -21.942441,2.8138212 0.74100701,-0.05187047 4.9220858,-22.530072 24.140646,-10.14468 41.792248,-24.676436 48.553343,-2.8352227 71.416087,-2.6209626 62.556124,18.456325 79.932279,33.316347 59.596934,43.767412 Z"
+ inkscape:transform-center-x="-0.51152643"
+ inkscape:transform-center-y="1.0220882"
+ transform="matrix(0.93880616,0,0,0.93880616,27.68061,26.445875)" />
+</svg>
"rating":"★★★★★",
"subtext":"Starting soon!",
"description":"The show will be starting soon!",
- "currency":"£",
- "prefix":true,
+ "currency":" Units",
+ "prefix":false,
"origional_price":1,
"gallery_price":1,
"cost_price":1,
WHERE id = 1;
"""
+ print(query)
+
cursor.execute(query)
connection.commit()
cursor.execute(load)
connection.commit()
- generate_docs() # This might be a bad idea
+ # generate_docs() # This might be a bad idea
first_request = False
opacity: 0;
transition: opacity 1.5s;
+
+ overflow: hidden;
}
@keyframes spin {
0% {transform: rotate(0turn) scale(1.2);}
75% {transform: rotate(0.75turn) scale(1);}
100% {transform: rotate(1turn) scale(1.2);}
}
+@keyframes spinOffset {
+ 0% {transform: perspective(300px) translate(-50%, -50%) rotateY( 15deg ) rotateZ(0turn) scale(1.2);}
+ 25% {transform: perspective(300px) translate(-50%, -50%) rotateY( 15deg ) rotateZ(0.25turn) scale(1);}
+ 50% {transform: perspective(300px) translate(-50%, -50%) rotateY( 15deg ) rotateZ(0.5turn) scale(1.2);}
+ 75% {transform: perspective(300px) translate(-50%, -50%) rotateY( 15deg ) rotateZ(0.75turn) scale(1);}
+ 100% {transform: perspective(300px) translate(-50%, -50%) rotateY( 15deg ) rotateZ(1turn) scale(1.2);}
+}
@keyframes spinText {
0% {transform: rotate(-0.01turn);}
50% {transform: rotate(0.01turn);}
100% {transform: rotate(-0.01turn);}
}
+@keyframes spinTextOffset {
+ 0% {transform: perspective(400px) translateX(-50%) rotateY( -15deg ) rotateZ(-0.01turn);}
+ 50% {transform: perspective(400px) translateX(-50%) rotateY( -15deg) rotateZ(0.01turn);}
+ 100% {transform: perspective(400px) translateX(-50%) rotateY( -15deg ) rotateZ(-0.01turn);}
+}
+@keyframes flicker {
+ 0% {
+ color: black;
+ text-shadow: -2px -2px 0 #FFF, 2px -2px 0 #FFF, -2px 2px 0 #FFF, 2px 2px 0 #FFF;
+ }
+ 49.999% {
+ color: black;
+ text-shadow: -2px -2px 0 #FFF, 2px -2px 0 #FFF, -2px 2px 0 #FFF, 2px 2px 0 #FFF;
+ }
+ 50% {
+ color: white;
+ text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000;
+ }
+ 99.999% {
+ color: white;
+ text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000;
+ }
+ 100% {
+ color: black;
+ text-shadow: -2px -2px 0 #FFF, 2px -2px 0 #FFF, -2px 2px 0 #FFF, 2px 2px 0 #FFF;
+ }
+}
+
.container {
position: absolute;
}
text-align: center;
/*text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;*/
}
+#shoutContainer {
+ position: absolute;
+ top: 50vh;
+ left: 50vw;
+ padding: 0;
+}
+#shoutBadgeContainer {
+ opacity: 0;
+ transform: translateX(-30vw);
+ transition: opacity 2s ease, transform 2s ease;
+}
+#shoutContainer.show > #shoutBadgeContainer {
+ opacity: 1;
+ transform: translateX(0vw);
+}
+#shoutTextContainer {
+ position: absolute;
+ top: 0vh;
+ opacity: 0;
+ transform: translateX(30vw);
+ transition: opacity 2s ease, transform 2s ease;
+ animation: flicker 1s linear 0s infinite;
+}
+#shoutContainer.show > #shoutTextContainer {
+ opacity: 1;
+ transform: translateX(0vw);
+}
+#shoutBadgeContainer > img {
+ width: 40vh;
+ height: 40vh;
+ padding: 0;
+ animation: spinOffset 10s linear 0s infinite;
+}
+#shout {
+ position: absolute;
+ top: -1em;
+ width: 100vw;
+ perspective: 400px;
+ animation: spinTextOffset 10s linear 0s infinite;
+ rotate: 0.025turn;
+ font-size: 6em;
+ text-align: center;
+}
#currentPrice {
line-height: 0.5em;
}
</div>
<div id="timer2" class="box"></div>
<div id="timer3"></div>
+ <div id="shoutContainer">
+ <div id="shoutBadgeContainer">
+ <img class="badge" src="./static/assets/star5.svg"></img>
+ </div>
+ <div id="shoutTextContainer">
+ <h1 id="shout">SHOUT TEXT</h1>
+ </div>
+ </div>
<div class="container box sigilBox" id="sigil1">
<svg class="sigil" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
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])}
+ shout
// handle all optional elements showing/hiding
if (data.bool_all) {all.classList.add("show");}
badgeContainer.classList.remove("show");
}
+ // Shout
+ document.getElementById("shout").innerHTML = data.shout;
+ let current = Math.round((Date.now() + data['timer_offset']) / 1000);
+ var time = data[`end_timer_shout`] - current;
+ if (time > 0) {
+ document.getElementById("shoutContainer").classList.add("show");
+ } else {
+ document.getElementById("shoutContainer").classList.remove("show");
+ }
+
// Sigil handling
for (let s = 0; s < 4; s++) {
if (data[`bool_sigil_${s+1}`]) {
price = Math.round(price * 100) / 100;
}
+ console.log(item.currency);
+ console.log(item.prefix);
+
let priceString;
let ezString;
- if (data.bool_prefix) {priceString = `${data.currency_symbol}${price}`}
- else {priceString = `${price}${data.currency_symbol}`};
+ if (item.prefix) {priceString = `${item.currency}${price}`}
+ else {priceString = `${price}${item.currency}`};
let ezPrice = Math.round((price * 1.1) / 12);
- if (data.bool_prefix) {ezString = `${data.currency_symbol}${ezPrice}`}
- else {ezString = `${ezPrice}${data.currency_symbol}`};
+ if (item.prefix) {ezString = `${item.currency}${ezPrice}`}
+ else {ezString = `${ezPrice}${item.currency}`};
// set discount, pricing and ez pay
document.getElementById("currentPrice").innerHTML = `<em>Now only:</em> ${priceString}`;
if (discount.reduce((a, b)=> a*b, 1) <= 0.99) {
let origionalPrice = Math.round(item.origional_price);
- if (data.bool_prefix) {origionalString = `${data.currency_symbol}${origionalPrice}`}
- else {origionalString = `${origionalPrice}${data.currency_symbol}`};
+ if (item.prefix) {origionalString = `${item.currency}${origionalPrice}`}
+ else {origionalString = `${origionalPrice}${item.currency}`};
origional.innerHTML = `<s><em>WAS:</em> ${origionalString}</s>`;
} else {
origional.innerHTML = `<em>NOW:</em> ${priceString}`;
<fieldset>
<legend>Pricing</legend>
- <input type='text' name='currency_symbol' value='{{data.currency_symbol}}'>
- <label>Currency</label>
- {% if data.bool_prefix %}
- <input type='radio' name='bool_prefix' value='0'>
- <label>Postfix</label>
- <input type='radio' name='bool_prefix' value='1' checked='checked'>
- <label style='color: red;'>Prefix</label>
- {% else %}
- <input type='radio' name='bool_prefix' value='0' checked='checked'>
- <label style='color: red;'>Postfix</label>
- <input type='radio' name='bool_prefix' value='1'>
- <label>Prefix</label>
- {% endif %}
- <br>
<input type='number' name='percent_remaining' value='{{data.percent_remaining}}'>
<label>Stock left (%)</label> <label style='color: red;'>was {{data.percent_remaining}}</label><br>
<br>
+
+ <input type='number' name='discount' step='1' value='{{data.discount}}' id="discount">
+ <label>Discount</label> <label style='color: red;'>was {{data.discount}}</label><br>
+ <em>Individual discounts (automatic)</em><br>
{% for d in (1,2,3,4) %}
- <input type='number' name='discount_{{d}}' step='1' value='{{data['discount_' ~ d]}}'>
- <label>Discount {{d}} (%)</label> <label style='color: red;'>was {{data['discount_' ~ d]}}</label><br>
+ <label>Discount {{d}}</label>
+ <input type='text' name='discount_{{d}}_display' value='{{data["discount_" ~ d]}}' disabled>
+ <br>
{% endfor %}
+
<br>
<input type='number' name='discount_change' value='{{data.discount_change}}'>
{% endif %}
</fieldset>
- <input type="submit" name="update" value="Update">
+ <div style="display: none;">
+ {% for d in (1,2,3,4) %}
+ <input type='text' name='discount_{{d}}' value='{{data["discount_" ~ d]}}' id="{{d}}">
+ {% endfor %}
+ </div>
+
+ <input type="button" value="Update" onclick="submitForm();">
</form>
+
+ <script>
+
+const deviation = 0.05;
+
+async function submitForm() {
+
+ if (document.getElementById("discount").value != {{data.discount}}) {
+
+ const totalDiscount = document.getElementById("discount").value / 100;
+ const discount1 = document.getElementById("1");
+ const discount2 = document.getElementById("2");
+ const discount3 = document.getElementById("3");
+ const discount4 = document.getElementById("4");
+
+ discount1.value = 0;
+ discount2.value = 0;
+ discount3.value = 0;
+ discount4.value = 0;
+
+ let activeDiscounts;
+ let targetValue;
+ switch (Math.floor(totalDiscount * 4)) {
+ case 0:
+ activeDiscounts = [discount1];
+ break;
+ case 1:
+ activeDiscounts = [discount1, discount2];
+ targetLeftValue = Math.sqrt(1 - totalDiscount);
+ break;
+ case 2:
+ activeDiscounts = [discount1, discount2, discount3];
+ targetLeftValue = Math.cbrt(1 - totalDiscount);
+ break;
+ default:
+ activeDiscounts = [discount1, discount2, discount3, discount4];
+ targetLeftValue = Math.pow(1 - totalDiscount, 0.25);
+ break;
+ }
+
+ let discountRemaining = 1 - totalDiscount;
+ const lastDiscount = activeDiscounts.pop();
+
+ for (discount of activeDiscounts) {
+ const u = 1 - Math.random();
+ const v = Math.random();
+ const z = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
+ discountLeft = z * deviation + targetLeftValue;
+
+ discount.value = Math.round((1 - discountLeft) * 100);
+ discountRemaining /= discountLeft;
+ }
+
+ lastDiscount.value = Math.round((1 - discountRemaining) * 100);
+ }
+
+ document.getElementsByTagName("form")[0].submit();
+}
+
+ </script>
</body>
</html>
</tr>
<tr>
<td><audio id="timer2" src=/static/sounds/timer2.wav controls></audio></td>
- <td>Timer 1</td>
+ <td>Timer 2</td>
</tr>
<tr>
<td><audio id="timer3" src=/static/sounds/timer3.wav controls></audio></td>
- <td>Timer 1</td>
+ <td>Timer 3</td>
</tr>
<tr>
<td><audio id="timer4" src=/static/sounds/timer4.wav controls></audio></td>
- <td>Timer 1</td>
+ <td>Timer 4</td>
</tr>
<tr>
<td><audio id="timer5" src=/static/sounds/timer5.wav controls></audio></td>
- <td>Timer 1</td>
+ <td>Timer 5</td>
</tr>
<tr>
<td><audio id="timer6" src=/static/sounds/timer6.wav controls></audio></td>
- <td>Timer 1</td>
+ <td>Timer 6</td>
</tr>
</table>
<h2>Doomsday</h2>
<audio id="clock" src=/static/sounds/clock.wav controls></audio>
+ <h2>Shout</h2>
+ <audio id="shout" src=/static/sounds/shout.wav controls></audio>
<br>
<input type="button" onclick="start();" value="Start listen" id="start" /> <em style="color:green" id="state"></em>
0
];
let pastClock = 0;
+let pastShout = 0;
let timersPlaying = 0;
const context = new AudioContext();
document.getElementById("clock")
];
const clock = document.getElementById("clock");
+const shout = document.getElementById("shout");
for (let t = 0; t < 6; t++) {
let timer = timers[t];
clock.play();
}
+ if (pastShout != data["end_timer_shout"]) {
+ pastShout = data["end_timer_shout"]
+ if (context.state === "suspended") {
+ context.resume();
+ }
+
+ console.log(`Shout!`);
+ shout.currentTime = 0.0;
+ shout.play();
+ }
+
timersPlaying = 0;
for (let t = 0; t < 6; t++) {
// if the timer is playing
targetLevel = 1 / timersPlaying;
}
if (clock.currentTime > 0 && !clock.paused && !clock.ended) {
- //targetLevel *= 0.75;
targetWet = 0.75;
}
+ if (shout.currentTime > 0 && !shout.paused && !shout.ended) {
+ targetLevel *= 0.75;
+ }
if (Math.abs(targetLevel - amp.gain.value) < 0.1 / levelLength) {
amp.gain.value = targetLevel;
{% endfor %}
</fieldset>
+ <fieldset>
+ <legend>Number</legend>
+ {% if data.bool_number %}
+ <input type='radio' name='bool_number' value='0'>
+ <label>Redact number</label>
+ <input type='radio' name='bool_number' value='1' checked='checked'>
+ <label style='color: red;'>Show number</label>
+ {% else %}
+ <input type='radio' name='bool_number' value='0' checked='checked'>
+ <label style='color: red;'>Redact number</label>
+ <input type='radio' name='bool_number' value='1'>
+ <label>Show number</label>
+ {% endif %}
+ </fieldset>
+
<fieldset>
<legend>Note</legend>
<textarea name='note' rows='10' style='width: 100%;'>{{data.note|safe}}</textarea>
<input type="submit" name="update" value="Update">
</form>
+ <br>
+
+ <form action="/admin/timer" method="POST">
+ <fieldset>
+ <legend>Shout text</legend>
+ <textarea name='shout' rows='2' style='width: 100%;'></textarea>
+ <label>Time to live </label>
+ <input type="number" value="10" name="end_timer_shout"></input>
+ <label> seconds (this defaults to 10s)</label>
+ </fieldset>
+ <input type="submit" name="update" value="Shout">
+ </form>
+
<script>
function update() {