From 9de0f728d5b7e2dbed80ea9b1e7bf7c53073e949 Mon Sep 17 00:00:00 2001 From: csd4ni3l Date: Thu, 23 Oct 2025 19:52:54 +0200 Subject: [PATCH] Modularize pumpkin.js so it's easy to adapt, make main fetch posts(no creation yet), add very coooool countdown to Halloween with pumpkin carving visualization --- app.py | 8 +- static/pumpkin.js | 185 ++++++++++++++++++++++++------------- templates/countdown.jinja2 | 92 ++++++++++++++++++ templates/index.jinja2 | 13 ++- templates/login.jinja2 | 3 + templates/profile.jinja2 | 3 + templates/register.jinja2 | 3 + 7 files changed, 242 insertions(+), 65 deletions(-) create mode 100644 templates/countdown.jinja2 diff --git a/app.py b/app.py index 29832f7..350b8a8 100644 --- a/app.py +++ b/app.py @@ -71,9 +71,15 @@ def main(): cur.execute("SELECT * FROM Posts LIMIT 20") + posts = cur.fetchall() + cur.close() - return render_template("index.jinja2", username=username) + return render_template("index.jinja2", username=username, posts=posts) + +@app.route("/countdown") +def countdown(): + return render_template("countdown.jinja2", logged_in=flask_login.current_user.is_authenticated, grid_size=os.getenv("GRID_SIZE", 15)) @app.route("/login", methods=["GET", "POST"]) def login(): diff --git a/static/pumpkin.js b/static/pumpkin.js index 4422583..d63b152 100644 --- a/static/pumpkin.js +++ b/static/pumpkin.js @@ -1,80 +1,139 @@ -function setup_pumpkin(canvas_id, clearbtn_id, form_id, pattern_field_id, grid_size) { - const canvas = document.getElementById(canvas_id); - const ctx = canvas.getContext('2d'); - const img = new Image(); - img.src = '/static/pumpkin.png'; +function draw(e, ctx, CELL_SIZE, drawing, canvas, currentPattern) { + if (!drawing) return; + var rect = canvas.getBoundingClientRect(); + var x = e.clientX - rect.left; + var y = e.clientY - rect.top; + var gridX = Math.floor(x / CELL_SIZE); + var gridY = Math.floor(y / CELL_SIZE); + check_and_color(ctx, CELL_SIZE, currentPattern, gridX, gridY); +} - const GRID_SIZE = grid_size; - const CELL_SIZE = canvas.width / GRID_SIZE; +function clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + drawGrid(ctx, canvas, GRID_SIZE); + currentPattern.length = 0; +} - img.onload = () => { - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - drawGrid(); - }; +function drawGrid(ctx, canvas, GRID_SIZE) { + const cell_size = canvas.width / GRID_SIZE; + ctx.strokeStyle = 'rgba(0, 0, 0, 0.6)'; + ctx.lineWidth = 1; + for (let i = 0; i <= GRID_SIZE; i++) { + const pos = i * cell_size; + ctx.beginPath(); + ctx.moveTo(pos, 0); + ctx.lineTo(pos, canvas.height); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(0, pos); + ctx.lineTo(canvas.width, pos); + ctx.stroke(); + } +} - let drawing = false; +function check_colorable(ctx, CELL_SIZE, gridX, gridY) { + var cellX = gridX * CELL_SIZE + CELL_SIZE / 3; + var cellY = gridY * CELL_SIZE + CELL_SIZE / 3; + var pixel = ctx.getImageData(cellX, cellY, 1, 1).data; + return (pixel[0] >= 254 && (pixel[1] >= 124 && pixel[1] <= 126)); +} + +function check_and_color(ctx, CELL_SIZE, currentPattern, gridX, gridY) { + if (check_colorable(ctx, CELL_SIZE, gridX, gridY)) { + var key = `${gridX},${gridY}`; + if (!currentPattern.includes(key)) { + currentPattern.push(key); + var cellX = gridX * CELL_SIZE + CELL_SIZE / 3; + var cellY = gridY * CELL_SIZE + CELL_SIZE / 3; + ctx.fillStyle = 'transparent'; + ctx.clearRect(cellX - CELL_SIZE / 3, cellY - CELL_SIZE / 3, CELL_SIZE, CELL_SIZE); + return true; + } else { + return false; + } + } + + return false; +} + +function includesPoint(arr, [x, y]) { + return arr.some(([a, b]) => a === x && b === y); +} + +function color_amount(ctx, canvas, grid_size, amount) { + let colored = 0; + const cell_size = canvas.width / grid_size; let currentPattern = []; - canvas.addEventListener('mousedown', () => { drawing = true; }); - canvas.addEventListener('mouseup', () => { drawing = false; }); - canvas.addEventListener('mousemove', draw); - canvas.addEventListener('click', (e) => {draw(e, true)}); + for (let y = 0; y < grid_size; y++) { + for (let x = 0; x < grid_size; x++) { + if (colored == amount) { + return currentPattern; + } + if (check_and_color(ctx, cell_size, currentPattern, x, y)) { + colored++; + } + } + } + + return currentPattern; +} - function draw(e, force=false) { - if (!drawing && !force) return; - var rect = canvas.getBoundingClientRect(); - var x = e.clientX - rect.left; - var y = e.clientY - rect.top; +function draw_pattern(ctx, canvas, pattern, grid_size) { + const cell_size = canvas.width / grid_size; + let currentPattern = []; - var gridX = Math.floor(x / CELL_SIZE); - var gridY = Math.floor(y / CELL_SIZE); + for (let x = 0; x < grid_size; x++) { + for (let y = 0; y < grid_size; y++) { + if (includesPoint(pattern, [x, y])) { + check_and_color(ctx, cell_size, currentPattern, x, y); + } + } + } + + return currentPattern; +} - var cellX = gridX * CELL_SIZE + CELL_SIZE / 3; - var cellY = gridY * CELL_SIZE + CELL_SIZE / 3; +function get_colorable(ctx, canvas, grid_size) { + let colorable = 0; + const cell_size = canvas.width / grid_size; - var pixel = ctx.getImageData(cellX, cellY, 1, 1).data; - - if (pixel[0] >= 254 && (pixel[1] >= 124 && pixel[1] <= 126)) { - var key = `${gridX},${gridY}`; - - if (!currentPattern.includes(key)) { - currentPattern.push(key); - ctx.fillStyle = 'black'; - ctx.fillRect(cellX - CELL_SIZE / 3, cellY - CELL_SIZE / 3, CELL_SIZE, CELL_SIZE); + for (let x = 0; x < grid_size; x++) { + for (let y = 0; y < grid_size; y++) { + if (check_colorable(ctx, cell_size, x, y)) { + colorable++; } } } - function drawGrid() { - ctx.strokeStyle = 'rgba(0, 0, 0, 0.6)'; - ctx.lineWidth = 1; + return colorable; +} - for (let i = 0; i <= GRID_SIZE; i++) { - const pos = i * CELL_SIZE; - - ctx.beginPath(); - ctx.moveTo(pos, 0); - ctx.lineTo(pos, canvas.height); - ctx.stroke(); - - ctx.beginPath() - ctx.moveTo(0, pos); - ctx.lineTo(canvas.width, pos); - ctx.stroke() - } +function setup_pumpkin(canvas_id, clearbtn_id, form_id, pattern_field_id, grid_size, allow_drawing=true) { + const canvas = document.getElementById(canvas_id); + const ctx = canvas.getContext('2d'); + const img = new Image(); + img.src = '/static/pumpkin.png'; + const GRID_SIZE = grid_size; + const CELL_SIZE = canvas.width / GRID_SIZE; + let currentPattern = []; + + img.onload = () => { + clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern); + }; + + if (allow_drawing) { + let drawing = false; + canvas.addEventListener('mousedown', () => { drawing = true; }); + canvas.addEventListener('mouseup', () => { drawing = false; }); + canvas.addEventListener('mousemove', (e) => draw(e, ctx, CELL_SIZE, drawing, canvas, currentPattern)); + canvas.addEventListener('click', (e) => {draw(e, ctx, CELL_SIZE, true, canvas, currentPattern)}); + document.getElementById(clearbtn_id).addEventListener('click', () => clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern)); + document.getElementById(form_id).addEventListener('submit', function(event) { + document.getElementById(pattern_field_id).value = JSON.stringify(currentPattern); + }); } - document.getElementById(clearbtn_id).addEventListener('click', clearCanvas); - - function clearCanvas() { - drawing = false; // Fix hold staying after clear - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - drawGrid() - currentPattern = []; - } - - document.getElementById(form_id).addEventListener('submit', function(event) { - document.getElementById(pattern_field_id).value = JSON.stringify(currentPattern); - }); + return [ctx, canvas, img]; } \ No newline at end of file diff --git a/templates/countdown.jinja2 b/templates/countdown.jinja2 new file mode 100644 index 0000000..97fbbfe --- /dev/null +++ b/templates/countdown.jinja2 @@ -0,0 +1,92 @@ +{% extends "base.jinja2" %} + +{% block title %}Halloween Countdown{% endblock title %} + +{% block nav %} +{% if logged_in %} + + + + +{% else %} + + + + +{% endif %} +{% endblock %} + +{% block body %} + +
+

Time to next Halloween: Loading...

+
+

Carved Pumpkin Countdown Visualization (last 30 days to Halloween):

+ +
+ + + +{% endblock body %} \ No newline at end of file diff --git a/templates/index.jinja2 b/templates/index.jinja2 index 59fa90e..3a7fba8 100644 --- a/templates/index.jinja2 +++ b/templates/index.jinja2 @@ -6,6 +6,9 @@ + @@ -16,6 +19,14 @@ {% block body %}
-

LoginWeen is a an app where you login/register with a halloween pumpkin carving as a password.

+ {% for post in posts %} +
+ +
+
{{ post.1 }}: {{ post.3 }}
+

{{ post.2 }}

+
+
+ {% endfor %}
{% endblock body %} \ No newline at end of file diff --git a/templates/login.jinja2 b/templates/login.jinja2 index 02eb494..031da2c 100644 --- a/templates/login.jinja2 +++ b/templates/login.jinja2 @@ -9,6 +9,9 @@ + diff --git a/templates/profile.jinja2 b/templates/profile.jinja2 index ee0a689..4a5aa71 100644 --- a/templates/profile.jinja2 +++ b/templates/profile.jinja2 @@ -6,6 +6,9 @@ + diff --git a/templates/register.jinja2 b/templates/register.jinja2 index fd08330..9215c8a 100644 --- a/templates/register.jinja2 +++ b/templates/register.jinja2 @@ -9,6 +9,9 @@ +