Compare commits

...

52 commits
v1.0.0 ... main

Author SHA1 Message Date
shy
8fec9e6ef7 Merge pull request 'Reintroduced item counter and fixed text in map.' (#9) from develop into main
All checks were successful
deployment / deployment (push) Successful in 20s
Reviewed-on: #9
2024-05-16 22:24:10 +02:00
Shy
45c1d0f9cc Converted text to path
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m21s
2024-05-16 22:09:54 +02:00
Shy
c2334642fb Added item counter. 2024-05-16 22:07:04 +02:00
Shy
6e98d1979c Merge branch 'develop' of ssh://git.koeln.ccc.de:2222/snoopy/in-c4 into develop 2024-05-16 21:45:44 +02:00
1171e0de40 Merge pull request 'Refined map and design.' (#8) from develop into main
All checks were successful
deployment / deployment (push) Successful in 21s
Reviewed-on: #8
2024-05-16 21:24:19 +02:00
Shy
360164be7a Refined corridor
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m15s
2024-05-16 21:16:56 +02:00
Shy
e5e61fb9c4 Change misleading file name 2024-05-16 21:16:56 +02:00
3b0a40ace8 Merge branch 'change-color-of-buttons'
All checks were successful
deployment / deployment (push) Successful in 2m18s
2024-05-16 21:13:50 +02:00
Shy
060560d8a2 Refined corridor
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 2m50s
2024-05-10 14:50:22 +02:00
Shy
0fa4e48ff5 Change misleading file name 2024-05-10 14:40:36 +02:00
Shy
62699c90eb Limit marker roundness on small screens
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m20s
2024-05-09 03:02:42 +02:00
Shy
6b3783e69f Merge branch 'change-color-of-buttons' of ssh://git.koeln.ccc.de:2222/snoopy/in-c4 into change-color-of-buttons 2024-05-08 07:30:34 +02:00
Shy
15d914fae3 Enhance visibility of marker
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m20s
2024-05-07 22:23:47 +02:00
Shy
bf182251ec Yellow outline around search input 2024-05-07 22:23:47 +02:00
Shy
0c1a280a19 More beautiful #mapnote 2024-05-07 22:23:47 +02:00
Shy
d98342bd02 Fixed colors for #mapnote 2024-05-07 22:23:47 +02:00
ebf06fa163 Merge pull request 'Added buttons for known types.' (#5) from type-buttons into main
All checks were successful
deployment / deployment (push) Successful in 18s
Reviewed-on: #5
2024-05-07 20:31:08 +02:00
Shy
7236d859eb Enhance visibility of marker
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m20s
2024-05-07 19:13:45 +02:00
Shy
f8ecf20b75 Yellow outline around search input
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m14s
2024-05-07 18:38:00 +02:00
Shy
065fa5adbf More beautiful #mapnote
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m16s
2024-05-06 18:43:03 +02:00
Shy
1b3de458fa Fixed colors for #mapnote 2024-05-06 17:26:31 +02:00
Shy
c9cda14f38 Fix indentation and use const where preferable
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 1m17s
2024-05-06 14:42:21 +02:00
Shy
9f95e704b9 Added buttons for known types.
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 2m34s
2024-05-04 18:28:09 +02:00
b9e87f09d5 Merge pull request 'Add drop shadow to map' (#4) from change-color-of-buttons into main
All checks were successful
deployment / deployment (push) Successful in 1m29s
Reviewed-on: #4
2024-05-02 20:50:07 +02:00
Shy
5b5728f7e2 Add drop shadow to map
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 53s
2024-05-01 17:17:35 +02:00
457af44a79 Merge pull request 'Changes colors slightly' (#3) from change-color-of-buttons into main
All checks were successful
deployment / deployment (push) Successful in 22s
Reviewed-on: #3
2024-05-01 01:10:05 +02:00
9935c0562b Changes colors slightly
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 55s
2024-05-01 01:08:43 +02:00
7b51b3414d Merge pull request 'Change button names' (#2) from change-color-of-buttons into main
All checks were successful
deployment / deployment (push) Successful in 19s
Reviewed-on: #2
2024-05-01 00:18:23 +02:00
a6e363220a Fixes wrong active button color and use primary color
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 48s
2024-05-01 00:04:37 +02:00
Shy
299a2f5914 Modified button colors
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 47s
2024-04-30 21:45:01 +02:00
b6a0d22674 Change button names
All checks were successful
deployment-on-pull-request / deployment (pull_request) Successful in 48s
2024-04-30 21:22:07 +02:00
b41f74bf7b Fix gitignore
All checks were successful
deployment / deployment (push) Successful in 18s
Goal is to not have the contents of /data/ inside the git workflow
2024-04-30 20:19:15 +02:00
c25188611e Adds actions workflow for pull-requests 2024-04-30 20:18:37 +02:00
2ec7600fa4 Merge pull request 'Updated map und grid' (#1) from shy/in-c4:c4-design into main
All checks were successful
deployment / deployment (push) Successful in 18s
Reviewed-on: #1
2024-04-30 19:54:18 +02:00
Shy
2aae5dd441 Include C4 style sheet 2024-04-30 18:18:56 +02:00
Shy
f91b83d3ca C4 inspired stylesheet 2024-04-30 18:18:11 +02:00
Shy
6cdeb1a3da Updated map und grid 2024-04-30 16:27:00 +02:00
0bc3cac6f5 Puts database into a subfolder
All checks were successful
deployment / deployment (push) Successful in 17s
2024-04-30 02:14:18 +02:00
277b2aec57 Add Dockerfile build action
All checks were successful
deployment / deployment (push) Successful in 1m50s
2024-04-30 02:05:36 +02:00
111a0f6e45 Changes map and update coordinate grid to c4 map 2024-04-30 01:56:38 +02:00
jomo
703a210f1e use BASE var in bulk_create output 2024-02-01 02:03:02 +01:00
jomo
ec64e81829 add contrib/ to .dockerignore 2024-02-01 01:48:51 +01:00
clonejo
8f5a82fb41 add client-side python script to create items in bulk 2024-02-01 01:46:22 +01:00
jomo
ea0538f90c ignore all dotfiles in .dockerignore 2024-02-01 01:42:00 +01:00
jomo
2b7342b902 update openapi docs for PUT request 2024-02-01 01:04:49 +01:00
jomo
6d01523d8d update python version and modules 2024-02-01 01:04:20 +01:00
jomo
621db6ad2c use current month for last_updated value 2024-02-01 00:03:33 +01:00
jomo
f1384b72f2 orm_mode has been renamed to from_attributes 2024-02-01 00:01:55 +01:00
jomo
f3658a76ff format database.py using black 2024-02-01 00:01:22 +01:00
clonejo
1bf4eb801f Add last_updated field to be able to track outdated information / lost items 2024-01-14 20:24:14 +01:00
clonejo
99df802a40 make return value of crud.put_item easier to understand 2024-01-14 20:24:14 +01:00
clonejo
eba9b410c6 autoformat by black+isort 2024-01-14 20:24:14 +01:00
21 changed files with 794 additions and 55 deletions

View file

@ -1,6 +1,6 @@
/.dockerignore
/.gitignore
.*
/inventory.sqlite3
/README.md
*.pyc
Dockerfile
/contrib/

View file

@ -0,0 +1,22 @@
name: deployment-on-pull-request
on:
pull_request:
pull_request_target:
types:
- opened
- reopened
- synchronize
jobs:
deployment:
runs-on: docker
container:
image: node:bookworm
steps:
- uses: actions/checkout@v4
- name: Build inventory
uses: https://codeberg.org/umglurf/kaniko-action@main
with:
cache: false
push: 'false'
context: /workspace/snoopy/in-c4/

View file

@ -0,0 +1,24 @@
name: deployment
on:
push:
branches:
- 'main'
jobs:
deployment:
runs-on: docker
container:
image: node:bookworm
steps:
- uses: actions/checkout@v4
- name: Build inventory
uses: https://codeberg.org/umglurf/kaniko-action@main
with:
cache: true
cache_repo: git.koeln.ccc.de/${{ github.repository }}/cache
push: 'true'
context: /workspace/snoopy/in-c4/
credentials: |
git.koeln.ccc.de=snoopy:${{ secrets.REGISTRY_WRITE }}
destinations: |
git.koeln.ccc.de/${{ github.repository }}/in-c4:latest

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
/inventory.sqlite3
/data/*
*.pyc

View file

@ -1,4 +1,4 @@
FROM python:3.10-alpine
FROM python:3.12-alpine3.19
WORKDIR /app

47
contrib/bulk_create.py Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env python3
import sys
import requests
BASE = "https://in.ccc.ac"
COUNT = 0
START_ID = 1
session = requests.Session()
# session.auth = (":)", ":)")
res = session.get(f"{BASE}/api/items")
res.raise_for_status()
ids = res.json().keys()
kiste_ids = {int(i.lstrip("K")) for i in ids if i.startswith("K") and i != "K"}
print("existing ids:", repr(kiste_ids), file=sys.stderr)
current_id = START_ID
new_ids = []
while len(new_ids) < COUNT:
if current_id not in kiste_ids:
new_ids.append(current_id)
current_id += 1
print("created:", file=sys.stderr)
if len(new_ids) == 0:
print("(none)", file=sys.stderr)
for i in new_ids:
j = {
"id": f"K{i}",
"is_in": None,
"coords_bl": None,
"coords_tr": None,
"type": "Kiste",
"name": None,
"content": None,
"note": "Bitte Name, Ort, Inhalt eintragen",
"hidden": False,
}
res = session.put(f"{BASE}/api/items/{j['id']}", json=j)
res.raise_for_status()
print(f"{j['id']},{BASE}/form.html?id={j['id']}")

24
crud.py
View file

@ -1,18 +1,32 @@
from sqlalchemy.orm import Session
from sqlalchemy import DECIMAL, cast
from enum import Enum
from sqlalchemy import DECIMAL, cast
from sqlalchemy.orm import Session
import models
import schemas
import models, schemas
def get_items(db: Session) -> list[models.Item]:
return db.query(models.Item).all()
def put_item(db: Session, id: str, item: schemas.Item):
updated = bool(db.query(models.Item).filter(models.Item.id == id).update(item.dict()))
updated = bool(
db.query(models.Item).filter(models.Item.id == id).update(item.dict())
)
if not updated:
db.add(models.Item(**item.dict()))
db.commit()
return updated
if updated:
return PutItemResult.UPDATED
else:
return PutItemResult.ADDED
class PutItemResult(Enum):
ADDED = 1
UPDATED = 2
def delete_item(db: Session, id: str):

View file

@ -2,12 +2,10 @@ from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./inventory.sqlite3"
SQLALCHEMY_DATABASE_URL = "sqlite:///./data/inventory.sqlite3"
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
connect_args = {"check_same_thread": False},
echo=True
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}, echo=True
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

35
main.py
View file

@ -1,9 +1,12 @@
import re
from calendar import timegm
from datetime import date
from fastapi import Depends, FastAPI, Response
from fastapi.staticfiles import StaticFiles
from sqlalchemy import event
from sqlalchemy.orm import Session
from sqlalchemy.engine import Engine
import re
from sqlalchemy.orm import Session
import crud
from database import SessionLocal, engine
@ -14,6 +17,7 @@ Base.metadata.create_all(bind=engine)
app = FastAPI()
def get_db():
db = SessionLocal()
try:
@ -21,6 +25,7 @@ def get_db():
finally:
db.close()
@event.listens_for(Engine, "connect")
def _set_sqlite_pragma(conn, _):
cursor = conn.cursor()
@ -31,21 +36,35 @@ def _set_sqlite_pragma(conn, _):
@app.get("/api/items", response_model=dict[str, Item])
async def list_items(db: Session = Depends(get_db)):
# natural sort by id
natsort = lambda item: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', item.id)]
natsort = lambda item: [
int(t) if t.isdigit() else t.lower() for t in re.split(r"(\d+)", item.id)
]
items = crud.get_items(db)
items = sorted(items, key=natsort)
return {i.id:i for i in items}
return {i.id: i for i in items}
@app.put("/api/items/{id}")
@app.put(
"/api/items/{id}",
status_code=201,
responses={201: {"description": "created"}, 204: {"description": "updated"}},
)
async def put_item(id: str, item: Item, db: Session = Depends(get_db)):
if crud.put_item(db, id, item):
return Response(b'', status_code=204)
return Response(b'', status_code=201)
if item.last_updated is None:
item.last_updated = month_timestamp()
if crud.put_item(db, id, item) == crud.PutItemResult.UPDATED:
return Response(b"", status_code=204)
return Response(b"", status_code=201)
@app.delete("/api/items/{id}", status_code=204)
async def delete_item(id: str, db: Session = Depends(get_db)):
crud.delete_item(db, id)
def month_timestamp() -> int:
"""Provides the timestamp of the current month's beginning (for improved privacy)"""
return timegm(date.today().replace(day=1).timetuple())
app.mount("/", StaticFiles(directory="static", html=True), name="static")

View file

@ -1,7 +1,8 @@
from sqlalchemy import Column, ForeignKey, String, Text, Boolean
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text
from database import Base
class Item(Base):
__tablename__ = "items"
@ -14,3 +15,4 @@ class Item(Base):
content = Column(Text)
note = Column(Text)
hidden = Column(Boolean, nullable=False)
last_updated = Column(Integer)

View file

@ -1,3 +1,3 @@
fastapi>=0.79.0
sqlalchemy>=1.4.39
uvicorn>=0.17.6
fastapi>=0.109.0
sqlalchemy>=2.0.25
uvicorn>=0.27.0.post1

View file

@ -1,4 +1,4 @@
from pydantic import BaseModel
from pydantic import BaseModel, Field
class Item(BaseModel):
@ -11,6 +11,7 @@ class Item(BaseModel):
content: str | None
note: str | None
hidden: bool
last_updated: int | None = Field(None)
class Config:
orm_mode = True
from_attributes = True

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 60 KiB

122
static/c4.css Normal file
View file

@ -0,0 +1,122 @@
body {
color: #f0f0f0;
background-color: #333;
}
h1, h1 a {
color: #dfaa37; /* C4 Yellow */
}
#search.failed {
color: #200;
background-color: #ec6d5f;
}
#map {
border: 1px solid #202020;
background-color: #4d4d4d;
max-width: 1440px;
max-height: 50vh;
aspect-ratio: 2 / 1;
box-shadow: 0px 6px 12px #000c;
}
#mapnote {
background-color: #ccc;
color: #333;
border-color: #202020;
padding: 0.25em 0.5em;
position: relative;
top: -1px;
box-shadow: 0px 6px 12px #000c;
}
#mapnote:empty {
display: none;
}
#itemcount {
display: inline-block;
margin-right: 1ch;
}
.result {
background-color: #202020;
border-color: #4d4d4d;
}
.result:focus {
background-color: #202020;
border-color: #dfaa37;
outline-color: #dfaa37;
}
.result a {
color: #dfaa37;
}
.note {
background-color: #333;
font-size: smaller;
}
#knowntypes button {
color: #220;
background-color: #dfaa37;
border: 1px solid #220;
border-radius: 4px;
padding: 0.2em 0.4em;
margin: 0.2em;
font-weight: bolder;
cursor: pointer;
}
#knowntypes button:hover, #knowntypes button:focus {
background-color: #f5c251;
}
.btn {
box-shadow: 0 3px 6px -1px rgba(0,0,0,0.3);
}
.btn.primary {
color: #220;
background-color: #dfaa37;
}
.btn.primary:hover,.btn.primary:focus {
background-color: #f5c251;
}
.btn.grn {
color: #020;
background-color: #9f6;
}
.btn.grn:hover ,.btn.grn:focus {
background-color: #3f0;
}
.btn.red {
color: #200;
background-color: #ec6d5f;
}
.btn.red:hover, .btn.red:focus {
background-color: #ff7667;
}
@keyframes blinker {
0% { opacity: 1; }
40% { opacity: 1; }
90% { opacity: 0; }
100% { opacity: 1; }
}
#mapgrid {
outline: 2px solid #333;
background-color: #dfaa37;
border-radius: min(4px, 25%);
animation: blinker 1s infinite;
}

View file

@ -3,6 +3,7 @@
<head>
<title>in?</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="c4.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="form.js"></script>
<script src="shared.js"></script>
@ -21,7 +22,7 @@
<b>Coordinates</b> (click on map)
<div id="map" title="Map for reference only. Not to scale!">
<img src="80x80.svg" onclick="mapClick(event)">
<img src="map.svg" onclick="mapClick(event)">
<div id="mapgrid"></div>
</div>
@ -43,9 +44,12 @@
<label for="hidden">Hide by default</label>
<input type="checkbox" id="hidden">
<label for="last_updated">Last updated</label>
<input id="last_updated" type="text" disabled>
</form>
<button id="delete" class="btn red" autocomplete="off">Delete</button>
<button id="save" class="btn green" autocomplete="off">Save</button>
<button id="save" class="btn primary" autocomplete="off">Save</button>
</body>
</html>

View file

@ -39,6 +39,7 @@ function fillForm() {
document.getElementById('content').value = item.content;
document.getElementById('note').value = item.note;
document.getElementById('hidden').checked = item.hidden;
document.getElementById('last_updated').value = formatTimestamp(item.last_updated);
formCoordsToMap();
}
@ -64,8 +65,22 @@ function knownTypes() {
map[i.type] = true;
}
}
let types = Object.keys(map).sort().join(', ')
document.getElementById('knowntypes').textContent = `Known types: ${types}.`;
// Get location in document.
const known_types = document.getElementById('knowntypes');
known_types.textContent = `Known types: `;
// Create a button for every known type.
for (let label of Object.keys(map).sort()) {
btn = document.createElement('button');
btn.setAttribute('type', 'button');
btn.textContent = label;
btn.addEventListener('click', function() {
document.getElementById('type').value = label;
});
known_types.appendChild(btn);
}
}
function save() {
@ -135,14 +150,14 @@ function del() {
let clicks = {x: [], y: []};
function mapClick(e) {
let x = Math.floor(31 / e.target.width * e.layerX);
let y = Math.floor(10 / e.target.height * e.layerY);
let x = Math.floor(36 / e.target.width * e.layerX);
let y = Math.floor(18 / e.target.height * e.layerY);
let humanPos = (x, y) => {
return `${String.fromCharCode(65 + 8 - y)}${x}`;
return `${String.fromCharCode(65 + 16 - y)}${x}`;
};
if (x > 0 && x < 31 && y > 0 && y < 9) {
if (x > 0 && x < 36 && y > 0 && y < 18) {
if (clicks.x.length > 1) {
clicks.x = [x];
clicks.y = [y];
@ -172,3 +187,9 @@ function formCoordsToMap() {
coordsToMap(coords_bl, coords_tr);
}
}
function formatTimestamp(ts) {
const date = new Date(ts * 1000);
// using Swedish format as a hack to get an iso formatted date
return date.toLocaleDateString("sv", {timeZone: "UTC"}).replace(/\-\d+$/,'')
}

View file

@ -3,6 +3,7 @@
<head>
<title>in?</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="c4.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="index.js"></script>
<script src="shared.js"></script>
@ -11,12 +12,13 @@
<div id="grid">
<div id="searchcontainer">
<input type="text" id="search" value="" autocomplete="off" placeholder="RegEx search" tabindex="1" autofocus>
<p id="itemcount"></p>
<label for="showhidden">Show hidden items</label>
<input type="checkbox" id="showhidden">
</div>
<div id="mapcontainer">
<div id="map">
<img src="80x80.svg" title="Map for reference only. Not to scale!">
<img src="map.svg" title="Map for reference only. Not to scale!">
<div id="mapgrid"></div>
</div>
<p id="mapnote"></p>
@ -37,7 +39,7 @@
</div>
</template>
</div>
<a href="form.html" class="btn green">+</a>
<a href="form.html" class="btn primary">+</a>
</div>
</body>
</html>

View file

@ -17,6 +17,7 @@ function renderItems() {
const container = document.getElementById('results');
const template = document.getElementById('item');
const loading = document.getElementById('loading');
let count = 0;
for (const [id, item] of Object.entries(items)) {
const clone = template.content.cloneNode(true);
@ -31,11 +32,14 @@ function renderItems() {
clone.querySelector(".result").id = `item-${id}`;
clone.querySelector("a").href = `form.html?id=${encodeURIComponent(id)}`;
if (item.hidden) {
clone.querySelector(".result").classList.add('hidden')
};
clone.querySelector(".result").classList.add('hidden');
} else {
count++;
}
container.appendChild(clone);
};
}
loading.remove();
updateCounter(count);
}
function getLocString(items, item) {
@ -67,6 +71,7 @@ function search(e) {
const searchAttrs = ['id', 'name', 'type', 'note', 'content'];
const query = e.target.value;
const regex = new RegExp(query, 'i')
let count = 0;
for (const elem of document.getElementsByClassName('result')) {
const item = items[elem.id.slice(5)];
@ -85,10 +90,36 @@ function search(e) {
if (found) {
elem.classList.remove('filtered');
count++;
} else {
elem.classList.add('filtered');
}
}
// Indicate failed search.
if (count) {
e.target.classList.remove('failed');
} else {
e.target.classList.add('failed');
}
updateCounter(count);
}
function updateCounter(count) {
const itemcount = document.getElementById('itemcount');
switch(count) {
case 0:
itemcount.textContent = 'No items found.';
break;
case 1:
itemcount.textContent = '1 item found.';
break;
default:
itemcount.textContent = `${count} items found.`;
break;
}
}
function showhidden(e){
@ -118,4 +149,4 @@ function showItem(e) {
function hideItem(e) {
clearMap();
}
}

433
static/map.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 96 KiB

View file

@ -1,12 +1,12 @@
function renderMap(xx, yy) {
if (xx[0] > 0 && xx[0] < 31 && yy[0] > 0 && yy[0] < 9 &&
xx[1] > 0 && xx[1] < 31 && yy[1] > 0 && yy[1] < 9 &&
if (xx[0] > 0 && xx[0] < 36 && yy[0] > 0 && yy[0] < 18 &&
xx[1] > 0 && xx[1] < 36 && yy[1] > 0 && yy[1] < 18 &&
xx[1] >= xx[0] && yy[1] >= yy[0]) {
let grid = document.getElementById('mapgrid');
grid.style.top = `${yy[0]*10}%`;
grid.style.left = `${100/31*xx[0]}%`;
grid.style.height = `${(yy[1] - yy[0] + 1) * 10}%`;
grid.style.width = `${100/31*(xx[1] - xx[0] + 1)}%`;
grid.style.top = `${yy[0]*100/18}%`;
grid.style.left = `${100/36*xx[0]}%`;
grid.style.height = `${(yy[1] - yy[0] + 1) * 100/18}%`;
grid.style.width = `${100/36*(xx[1] - xx[0] + 1)}%`;
} else {
alert(`invalid coordinates x=${xx} y=${yy}`);
}
@ -26,8 +26,8 @@ function coordsToMap(coords_bl, coords_tr) {
let bl_x = parseInt(coords_bl.slice(1));
let tr_x = parseInt(coords_tr.slice(1));
bl_y = 8 - (bl_y.charCodeAt(0) - 65);
tr_y = 8 - (tr_y.charCodeAt(0) - 65);
bl_y = 16 - (bl_y.charCodeAt(0) - 65);
tr_y = 16 - (tr_y.charCodeAt(0) - 65);
renderMap([bl_x, tr_x], [tr_y, bl_y]);
}
}

View file

@ -171,10 +171,10 @@ textarea {
#map {
position: relative;
border: 1px solid black;
background: #fff;
max-width: 800px;
background: #4d4d4d;
max-width: 1440px;
max-height: 50vh;
aspect-ratio: 31 / 10;
aspect-ratio: 2 / 1;
margin: 0 auto;
}
@ -238,4 +238,4 @@ form #map {
max-width: 100%;
text-align: center;
margin: auto;
}
}