Merge branch 'sksp2022-strategicka' of gitea.ks.matfyz.cz:KSP/ksp into sksp2022-strategicka
This commit is contained in:
commit
e4fc5b4a5b
8 changed files with 240 additions and 64 deletions
|
@ -12,14 +12,14 @@ ses = db.get_session()
|
|||
name="Hlavní hra"
|
||||
|
||||
mode = "occupy"
|
||||
teams_count = 16
|
||||
teams_count = 12
|
||||
configuration = {
|
||||
"teams_width": 4,
|
||||
"teams_height": 4,
|
||||
"width_per_team": 30,
|
||||
"height_per_team": 30,
|
||||
"born_per_round": [1],
|
||||
"initial_remaining_rounds": 1000,
|
||||
"teams_height": 3,
|
||||
"width_per_team": 24,
|
||||
"height_per_team": 24,
|
||||
# "born_per_round": [1],
|
||||
"initial_remaining_rounds": 540,
|
||||
"spawn_price": 10,
|
||||
"last_spawn": 100,
|
||||
# seed=5
|
||||
|
|
|
@ -27,6 +27,7 @@ class Logic:
|
|||
@add_logic
|
||||
class Occupy(Logic):
|
||||
MOVE_VECTORS = {
|
||||
None: (0, 0),
|
||||
"stay": (0, 0),
|
||||
"left": (0, -1),
|
||||
"right": (0, 1),
|
||||
|
@ -60,8 +61,10 @@ class Occupy(Logic):
|
|||
"remaining_rounds": 1
|
||||
}
|
||||
|
||||
# zero_state spawnuje jednoho vojáka, poslední (použitelný, takže
|
||||
# předposlední) spawn umožňuje budget na `last_spawn` nových vojáků
|
||||
def budget_for_round(self, round_id):
|
||||
return (self.last_spawn * self.spawn_price * (round_id + 1)**2) // (self.rounds_total**2)
|
||||
return self.spawn_price + ((round_id * self.spawn_price * self.last_spawn) // self.rounds_total)
|
||||
|
||||
# Počáteční stav
|
||||
def zero_state(self) -> Any:
|
||||
|
|
|
@ -83,7 +83,7 @@ def init_request():
|
|||
g.menu += [
|
||||
MenuItem(app.url_for(pages.web_org_games.__name__), "Hry"),
|
||||
MenuItem(app.url_for(pages.web_org_users.__name__), "Uživatelé"),
|
||||
MenuItem(app.url_for(pages.web_org_logs.__name__), "Logy"),
|
||||
MenuItem(app.url_for(pages.web_org_logs.__name__)+"#main_table" , "Logy"),
|
||||
]
|
||||
else:
|
||||
g.menu += [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from flask import Flask, redirect, flash, session, g, request, get_flashed_messages, Markup
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField, RadioField
|
||||
from wtforms.validators import ValidationError
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_bootstrap import Bootstrap
|
||||
|
@ -11,6 +11,7 @@ import wtforms
|
|||
from wtforms.fields import EmailField
|
||||
from wtforms.widgets import NumberInput
|
||||
from typing import Optional, Any, List
|
||||
from datetime import datetime
|
||||
|
||||
import hra.config as config
|
||||
import hra.web.html as html
|
||||
|
@ -18,7 +19,7 @@ import hra.db as db
|
|||
from hra.web import app
|
||||
import hra.web.jinja_mac as jinja_mac
|
||||
from hra.util import hash_passwd
|
||||
from hra.web.pages import BasePage
|
||||
from hra.web.pages import BasePage, web_game_view
|
||||
|
||||
|
||||
wlogic_by_mode = {}
|
||||
|
@ -39,17 +40,49 @@ class WLogic:
|
|||
self.game = game
|
||||
self.logic = game.get_logic()
|
||||
|
||||
class OccupyViewConfig(FlaskForm):
|
||||
refresh_none = "Bez automatické aktualizace"
|
||||
refresh_meta = "Aktualizace pomocí HTML META"
|
||||
refresh_js = "Aktualizace pomocí java scriptu"
|
||||
|
||||
font_size = IntegerField("Velikost fontu", default=8)
|
||||
refresh = RadioField("Aktualizace", choices=(refresh_none, refresh_meta), default=refresh_none)
|
||||
not_clickable = BooleanField("Vypnout podrobnosti kliknutím (zmenší nároky na sít a procesor)")
|
||||
big_fields = BooleanField("Široká políčka (trojciferné počty)")
|
||||
|
||||
submit = SubmitField("Refresh/Potvrdit")
|
||||
|
||||
@add_wlogic
|
||||
class Occupy(WLogic):
|
||||
def view(self, state: db.State, team: Optional[db.Team], teams: List[db.Team]):
|
||||
meta_refresh_without_reaming = 15
|
||||
|
||||
game = state.game
|
||||
if game.step_mode == db.StepMode.automatic:
|
||||
time_reaming = (state.create_time - datetime.now()).total_seconds() + game.step_every_s
|
||||
else:
|
||||
time_reaming = None
|
||||
s = state.get_state()
|
||||
if team is not None:
|
||||
s = self.logic.personalize_state(s, team.team_id, state.round)
|
||||
conff = OccupyViewConfig(formdata=request.args, csrf_enabled=False)
|
||||
q = db.get_session().query(db.Log).order_by(db.Log.log_id.desc())
|
||||
conff.validate()
|
||||
|
||||
max_num = 999 if conff.big_fields.data else 99
|
||||
|
||||
b = BasePage()
|
||||
b.h2(f"Hra {self.game.print()} kolo {state.round}")
|
||||
b.p().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}")
|
||||
with b.table(_class="game_table"):
|
||||
if not conff.not_clickable.data:
|
||||
b.p("Po kliknutí na buňku se zobrazí další informace.")
|
||||
if conff.refresh.data == conff.refresh_meta and time_reaming is None:
|
||||
b.p(f"Není známo, kdy nastane další kolo, proto může být aktualizace až o {meta_refresh_without_reaming} sekund opožděna.")
|
||||
with b.p():
|
||||
b.line().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}")
|
||||
with b.line().b(_class="pull-right"):
|
||||
if time_reaming is not None:
|
||||
b("Čas do konce kola: ", b._span(id="time")(int(time_reaming)), " sekund")
|
||||
else:
|
||||
b("Konec kola není stanoven")
|
||||
with b.p(style="text-align:center;").table(_class="game_table"):
|
||||
for i, row in enumerate(s["world"]):
|
||||
with b.tr():
|
||||
for j, x in enumerate(row):
|
||||
|
@ -58,7 +91,7 @@ class Occupy(WLogic):
|
|||
members = x["members"]
|
||||
with b.td():
|
||||
classes = []
|
||||
with b.a(href=f"#cell_{i}_{j}"):
|
||||
with b.a(href=f"#cell_{i}_{j}") if not conff.not_clickable.data else b.bucket():
|
||||
if x["protected_for_team"] is not None:
|
||||
classes.append("game_protected")
|
||||
if x["hill"]:
|
||||
|
@ -70,38 +103,80 @@ class Occupy(WLogic):
|
|||
if occupied_by_team is not None:
|
||||
classes.append(f'game_occupied')
|
||||
classes.append(f'game_occupied_by_{occupied_by_team}')
|
||||
if len(members):
|
||||
b(len(members))
|
||||
num = len(members)
|
||||
if num:
|
||||
b(num if num <= max_num else ("+++" if conff.big_fields.data else "++"))
|
||||
else:
|
||||
b(Markup(" "))
|
||||
b(_class=" ".join(classes))
|
||||
|
||||
for i, row in enumerate(s["world"]):
|
||||
for j, x in enumerate(row):
|
||||
occupied_by_team = x["occupied_by_team"]
|
||||
protected_for_team = x["protected_for_team"]
|
||||
home_for_team = x["home_for_team"]
|
||||
members = x["members"]
|
||||
with b.div(id=f"cell_{i}_{j}", _class="game_tab"):
|
||||
b.h4(f"Políčko {i} {j}")
|
||||
if x["hill"]:
|
||||
b.p().b("Pohoří")
|
||||
else:
|
||||
if occupied_by_team is not None:
|
||||
b.p(_class=f"game_team_{occupied_by_team}").b(f"Obsazeno týmem: {teams[occupied_by_team].print()}")
|
||||
if protected_for_team is not None:
|
||||
b.p(_class=f"game_team_{protected_for_team}").b(f"Ochranné území týmu: {teams[protected_for_team].print()}")
|
||||
if home_for_team is not None:
|
||||
b.p(_class=f"game_team_{home_for_team}").b(f"Domov týmu: {teams[home_for_team].print()}")
|
||||
b.p().b(f"Počet osob: {len(members)}")
|
||||
with b.ul():
|
||||
for m in members:
|
||||
b.li(_class=f"game_team_{home_for_team}")(f"Voják {m['id']} týmu {teams[m['team']].print()}")
|
||||
if not conff.not_clickable.data:
|
||||
for i, row in enumerate(s["world"]):
|
||||
for j, x in enumerate(row):
|
||||
occupied_by_team = x["occupied_by_team"]
|
||||
protected_for_team = x["protected_for_team"]
|
||||
home_for_team = x["home_for_team"]
|
||||
members = x["members"]
|
||||
with b.div(id=f"cell_{i}_{j}", _class="game_tab form-frame"):
|
||||
b.h4(f"Políčko {i} {j}", b._a(href=f"#", _class="btn btn-primary pull-right")("Skrýt"))
|
||||
if x["hill"]:
|
||||
b.p().b("Pohoří")
|
||||
else:
|
||||
if occupied_by_team is not None:
|
||||
b.p(_class=f"game_team_{occupied_by_team}").b(f"Obsazeno týmem: {teams[occupied_by_team].print()}")
|
||||
if protected_for_team is not None:
|
||||
b.p(_class=f"game_team_{protected_for_team}").b(f"Ochranné území týmu: {teams[protected_for_team].print()}")
|
||||
if home_for_team is not None:
|
||||
b.p(_class=f"game_team_{home_for_team}").b(f"Domov týmu: {teams[home_for_team].print()}")
|
||||
b.p().b(f"Počet osob: {len(members)}")
|
||||
with b.ul():
|
||||
for m in members:
|
||||
b.li(_class=f"game_team_{home_for_team}")(f"Voják {m['id']} týmu {teams[m['team']].print()}")
|
||||
|
||||
del conff.csrf_token
|
||||
b.p()
|
||||
b.div(_class="form-frame")(jinja_mac.quick_form(conff, form_type="horizontal", method="GET"))
|
||||
|
||||
b.script(Markup(f"""
|
||||
var intervalID = window.setInterval(update_time, 100);
|
||||
var t = { time_reaming*1000 };
|
||||
function update_time() {{
|
||||
document.getElementById("time").innerHTML = Math.floor(t/1000);;
|
||||
t -= 100;
|
||||
}}
|
||||
"""))
|
||||
|
||||
def head(x):
|
||||
refresh_time = time_reaming + 1 if time_reaming is not None else 15
|
||||
font_size = int(conff.font_size.data)
|
||||
box_size = int(font_size * 1.3 + 1)
|
||||
box_w = box_size
|
||||
if conff.big_fields.data:
|
||||
box_w *= 1.5
|
||||
table_w = len(s["world"][0]) * box_w
|
||||
margin_cmd = ""
|
||||
if table_w > 1000:
|
||||
margin_cmd = f"margin-left: {(1000-table_w) / 2}px;"
|
||||
if conff.refresh.data == conff.refresh_meta:
|
||||
b.meta(**{"HTTP-EQUIV": "refresh", "CONTENT": f"{refresh_time};{app.url_for(web_game_view.__name__, game_id=game.game_id, team_id=team.team_id, **request.args)}"})
|
||||
b.link(rel="stylesheet", href=app.url_for('static', filename='occupy.css'), type='text/css', media="all")
|
||||
b.style(Markup(f"""
|
||||
table.game_table tr td {{
|
||||
width: {box_w}px;
|
||||
max-width: {box_w}px;
|
||||
height: {box_size}px;
|
||||
font-size: {font_size}px;
|
||||
}}
|
||||
table.game_table {{
|
||||
width: {table_w}px;
|
||||
{margin_cmd}
|
||||
}}
|
||||
"""))
|
||||
|
||||
|
||||
b.wrap(
|
||||
limited_size=False,
|
||||
sticky_head=False,
|
||||
head=lambda x:x.link(rel="stylesheet", href=app.url_for('static', filename='occupy.css'), type='text/css', media="all")
|
||||
head=head,
|
||||
)
|
||||
return b.print_file()
|
||||
|
|
|
@ -11,7 +11,7 @@ class EscapeError(RuntimeError):
|
|||
|
||||
|
||||
def escape_attribute(x:str) -> str:
|
||||
return x.replace("&", "&ersand;").replace('"', """)
|
||||
return str(x).replace("&", "&").replace('"', """)
|
||||
|
||||
|
||||
def escape_attribute_name(x:str) -> str:
|
||||
|
@ -72,6 +72,14 @@ class Bucket:
|
|||
def _line(self):
|
||||
return Line(self)
|
||||
|
||||
def bucket(self):
|
||||
t = Bucket(self.builder)
|
||||
self.add(t)
|
||||
return t
|
||||
|
||||
def _bucket(self):
|
||||
return Bucket(self)
|
||||
|
||||
def print(self):
|
||||
out = []
|
||||
self.serialize_append_to_list(out, 0)
|
||||
|
@ -160,6 +168,12 @@ class Builder:
|
|||
def _line(self):
|
||||
return Line(self)
|
||||
|
||||
def bucket(self):
|
||||
return self.current_tag.bucket()
|
||||
|
||||
def _bucket(self):
|
||||
return Bucket(self)
|
||||
|
||||
def __call__(self, *arg, **kvarg):
|
||||
self.current_tag(*arg, **kvarg)
|
||||
return self
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from flask import Flask, redirect, flash, session, g, request, get_flashed_messages
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField, SelectMultipleField
|
||||
from wtforms.validators import ValidationError
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_bootstrap import Bootstrap
|
||||
|
@ -84,6 +84,12 @@ def user_link(user):
|
|||
return b.root_tag
|
||||
|
||||
|
||||
def ip_link(ip):
|
||||
b = html.Builder()
|
||||
with b.line():
|
||||
b.a(href=app.url_for(web_org_logs.__name__, ip=ip)+"#main_table")(ip)
|
||||
return b.root_tag
|
||||
|
||||
def right_for_game(game):
|
||||
if g.org:
|
||||
return True
|
||||
|
@ -261,14 +267,16 @@ def web_game(game_id):
|
|||
with b.div(_class="btn-group", role="group"):
|
||||
if right_for_team(team):
|
||||
b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_game_view.__name__, game_id=game.game_id, team_id=team.team_id))("Zobrazit hru")
|
||||
b.a(_class="btn btn-xs btn-default", href=app.url_for(web_org_logs.__name__, game_id=game_id, team_id=team.team_id)+"#main_table")("Log")
|
||||
if g.org:
|
||||
b.a(_class="btn btn-xs btn-default", href=app.url_for(web_org_game_userchange.__name__, game_id=game.game_id, team_id=team.team_id))("Změnit uživatele")
|
||||
|
||||
with b.div(_class="btn-gruser_idoup", role="group"):
|
||||
with b.div(_class="btn-group", role="group"):
|
||||
if right_for_step(game):
|
||||
with b.form(method="POST", _class="btn-group", action=app.url_for(web_game_step.__name__, game_id=game_id)):
|
||||
b.button(_class="btn btn-primary", type="submit", name="su", value="yes")("Krok")
|
||||
if g.org:
|
||||
b.a(_class="btn btn-primary", href=app.url_for(web_org_logs.__name__, game_id=game_id)+"#main_table")("Log")
|
||||
b.a(_class="btn btn-default", href=app.url_for(web_org_game_round_inspect.__name__, game_id=game_id))("Inspekce kola")
|
||||
|
||||
if g.org:
|
||||
|
@ -379,13 +387,18 @@ def web_org_game_round_inspect(game_id, round_id=None):
|
|||
b.line().th()("Bodů")
|
||||
b.line().th()("Stažení")
|
||||
b.line().th()("Nahrání")
|
||||
b.line().th()("Akce")
|
||||
for team, move in zip(teams, moves):
|
||||
with b.tr():
|
||||
b.line().td()(team.team_id)
|
||||
b.line().td()(user_link(team.user),": ", team.name)
|
||||
b.line().td()(move.points)
|
||||
b.line().td()(move.reads_count)
|
||||
b.line().td()(f"{move.ok_pushs_count} {move.warnings_pushs_count} {move.err_pushs_count}")
|
||||
b.line().td()(f"ok: {move.ok_pushs_count} w: {move.warnings_pushs_count} e: {move.err_pushs_count}")
|
||||
with b.td():
|
||||
with b.div(_class="btn-group", role="group"):
|
||||
if right_for_team(team):
|
||||
b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_org_logs.__name__, game_id=game_id, team_id=team.team_id, round_id=round_id)+"#main_table")("Log")
|
||||
return b.print_file()
|
||||
|
||||
|
||||
|
@ -482,6 +495,7 @@ def web_org_user(user_id):
|
|||
b.line().h2("Uživatel ", user.print())
|
||||
with b.div(_class="btn-group", role="group"):
|
||||
with b.form(method="POST", _class="btn-group", action=app.url_for(web_org_user_su.__name__, user_id=user.id)):
|
||||
b.a(_class="btn btn-primary", href=app.url_for(web_org_logs.__name__, user_id=user.id)+"#main_table")("Log")
|
||||
b.button(_class="btn btn-default", type="submit", name="su", value="yes")("Převtělit")
|
||||
b.h3("Týmy")
|
||||
with b.p().table(_class="data full"):
|
||||
|
@ -503,8 +517,8 @@ def web_org_user_su(user_id):
|
|||
flash("Uživatel vtělen!")
|
||||
return redirect('/')
|
||||
|
||||
@app.route("/org/log/<int:log_id>", methods=['GET'])
|
||||
def web_org_log(log_id):
|
||||
@app.route("/org/log/<int:log_id>/data", methods=['GET'])
|
||||
def web_org_log_data(log_id):
|
||||
l = db.get_session().query(db.Log).filter_by(log_id=log_id).one_or_none()
|
||||
if l is None:
|
||||
raise werkzeug.exceptions.NotFound()
|
||||
|
@ -513,31 +527,74 @@ def web_org_log(log_id):
|
|||
b.p().pre(pprint.pformat(l.get_data()))
|
||||
return b.print_file()
|
||||
|
||||
class LogsFindForm(FlaskForm):
|
||||
username = StringField('Jméno uživatele')
|
||||
user_id = OptionalIntField('ID uživatele')
|
||||
game_id = OptionalIntField('ID hry')
|
||||
team_id = OptionalIntField('Tým')
|
||||
round_id = OptionalIntField('Kolo')
|
||||
limit = OptionalIntField('Limit')
|
||||
ip = StringField('IP')
|
||||
status = SelectMultipleField('Status', choices=[("", "*")]+[(x,x) for x in ["ok", "warning", "too_early", "too_late", "error", "http-error"]], render_kw={"size":7})
|
||||
endpoint = SelectMultipleField('Stránky', choices=db.Endpoint.choices())
|
||||
|
||||
submit = SubmitField("Najít")
|
||||
|
||||
|
||||
def validate_captcha(form, field):
|
||||
if field.data != config.CAPTCHA:
|
||||
raise ValidationError("Chyba!")
|
||||
|
||||
@app.route("/org/logs", methods=['GET'])
|
||||
def web_org_logs():
|
||||
logs = db.get_session().query(db.Log).order_by(db.Log.log_id.desc()).all()
|
||||
f = LogsFindForm(formdata=request.args, csrf_enabled=False)
|
||||
q = db.get_session().query(db.Log).order_by(db.Log.log_id.desc())
|
||||
if request.args:
|
||||
f.validate()
|
||||
if f.username.data:
|
||||
q = q.filter(db.User.username.ilike(f"%{f.username.data}%"))
|
||||
if f.user_id.data is not None:
|
||||
q = q.filter_by(user_id=f.user_id.data or None)
|
||||
if f.game_id.data is not None:
|
||||
q = q.filter_by(game_id=f.game_id.data or None)
|
||||
if f.team_id.data is not None:
|
||||
q = q.filter_by(team_id=f.team_id.data or None)
|
||||
if f.round_id.data is not None:
|
||||
q = q.filter_by(round=f.round_id.data)
|
||||
if f.ip.data:
|
||||
q = q.filter(db.Log.source_ip.ilike(f"%{f.ip.data}%"))
|
||||
if f.status.data and "" not in f.status.data:
|
||||
q = q.filter(db.Log.status.in_(f.status.data))
|
||||
if f.endpoint.data:
|
||||
q = q.filter(db.Log.endpoint.in_(f.endpoint.data))
|
||||
|
||||
|
||||
|
||||
limit = f.limit.data or 20
|
||||
logs = q.limit(limit).all()
|
||||
count = q.count()
|
||||
|
||||
b = BasePage()
|
||||
|
||||
del f.csrf_token
|
||||
b.line().h2("Logy")
|
||||
b(jinja_mac.quick_form(f, form_type='horizontal', method="GET"))
|
||||
with b.p().table(_class="data full"):
|
||||
with b.thead():
|
||||
b.line().th()("Čas")
|
||||
b.line().th()("Uživatel")
|
||||
b.line().th()("URL")
|
||||
b.line().th()("Hra")
|
||||
b.line().th()("Tým")
|
||||
b.line().th()("Status")
|
||||
b.line().th()("Popis")
|
||||
b.line().th()("GET")
|
||||
b.line().th()("Akce")
|
||||
with b.thead(id="main_table"):
|
||||
b.line().th("Čas")
|
||||
b.line().th("Uživatel")
|
||||
b.line().th("URL")
|
||||
b.line().th("Hra")
|
||||
b.line().th("Status")
|
||||
b.line().th("Popis")
|
||||
b.line().th("GET")
|
||||
b.line().th("Akce")
|
||||
for l in logs:
|
||||
with b.tr():
|
||||
with b.tr(_class="log_"+l.status):
|
||||
b.line().td(l.time.strftime("%H:%M:%S"))
|
||||
b.line().td(l.user.print() if l.user else None, b._br(), l.source_ip)
|
||||
b.line().td(user_link(l.user) if l.user else None, b._br(), ip_link(l.source_ip))
|
||||
b.line().td(l.endpoint._name_, b._br(), l.url)
|
||||
b.line().td(l.game.print() if l.game else None)
|
||||
b.line().td(l.team_id)
|
||||
b.line().td(l.game.print() if l.game else None, b._br(), "tým: ", l.team_id, " kolo: ", l.round)
|
||||
b.line().td(l.status)
|
||||
b.line().td(l.text)
|
||||
with b.line().td():
|
||||
|
@ -545,7 +602,12 @@ def web_org_logs():
|
|||
b(f"{k}: {v}").br()
|
||||
with b.line().td():
|
||||
with b.div(_class="btn-group", role="group"):
|
||||
b.a(href=app.url_for(web_org_log.__name__, log_id=l.log_id), _class="btn btn-xs btn-primary")("Podrobnosti")
|
||||
if l.data is not None:
|
||||
b.a(href=app.url_for(web_org_log_data.__name__, log_id=l.log_id), _class="btn btn-xs btn-primary")("Data")
|
||||
if count <= limit:
|
||||
b.line().p(f"Toť vše ({count})")
|
||||
else:
|
||||
b.line().p().b(f"Zobrazeno prvních {limit} logů z {count}")
|
||||
return b.print_file()
|
||||
|
||||
from hra.web.game import get_wlogic, wlogic_by_mode
|
||||
|
|
|
@ -12,6 +12,7 @@ body {
|
|||
margin: 0 auto;
|
||||
padding: 0 1em;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.content_limited, main {
|
||||
|
@ -300,3 +301,22 @@ nav#main-menu a.active {
|
|||
.collapsible input[type="checkbox"].toggle:checked ~ .collapsible-inner {
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.log_ok {
|
||||
background-color: #BBFFBB;
|
||||
}
|
||||
.log_warning {
|
||||
background-color: #FFFFBB;
|
||||
}
|
||||
.log_error {
|
||||
background-color: #FFBBBB;
|
||||
}
|
||||
.log_http-error {
|
||||
background-color: #FF0000;
|
||||
}
|
||||
.log_too_late {
|
||||
background-color: #BBFFFF;
|
||||
}
|
||||
.log_too_early {
|
||||
background-color: #BBBBFF;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
|
||||
table.game_table{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.game_table, table.game_table tr, table.game_table tr td {
|
||||
border: thin solid black;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.game_table tr td {
|
||||
width: 10pt;
|
||||
height: 10px;
|
||||
font-size: 8px;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue