mai2: add basic webui

This commit is contained in:
Hay1tsme 2024-06-09 03:05:57 -04:00
parent e7ddfcda2e
commit b4b8650acc
9 changed files with 813 additions and 2 deletions

View File

@ -44,12 +44,13 @@ class ShopOwner():
self.permissions = perms self.permissions = perms
class UserSession(): class UserSession():
def __init__(self, usr_id: int = 0, ip: str = "", perms: int = 0, ongeki_ver: int = 7, chunithm_ver: int = -1): def __init__(self, usr_id: int = 0, ip: str = "", perms: int = 0, ongeki_ver: int = 7, chunithm_ver: int = -1, maimai_version: int = -1):
self.user_id = usr_id self.user_id = usr_id
self.current_ip = ip self.current_ip = ip
self.permissions = perms self.permissions = perms
self.ongeki_version = ongeki_ver self.ongeki_version = ongeki_ver
self.chunithm_version = chunithm_ver self.chunithm_version = chunithm_ver
self.maimai_version = maimai_version
class FrontendServlet(): class FrontendServlet():
def __init__(self, cfg: CoreConfig, config_dir: str) -> None: def __init__(self, cfg: CoreConfig, config_dir: str) -> None:
@ -216,6 +217,7 @@ class FE_Base():
sesh.current_ip = tk['current_ip'] sesh.current_ip = tk['current_ip']
sesh.permissions = tk['permissions'] sesh.permissions = tk['permissions']
sesh.chunithm_version = tk['chunithm_version'] sesh.chunithm_version = tk['chunithm_version']
sesh.maimai_version = tk['maimai_version']
if sesh.user_id <= 0: if sesh.user_id <= 0:
self.logger.error("User session failed to validate due to an invalid ID!") self.logger.error("User session failed to validate due to an invalid ID!")
@ -260,7 +262,17 @@ class FE_Base():
def encode_session(self, sesh: UserSession, exp_seconds: int = 86400) -> str: def encode_session(self, sesh: UserSession, exp_seconds: int = 86400) -> str:
try: try:
return jwt.encode({ "user_id": sesh.user_id, "current_ip": sesh.current_ip, "permissions": sesh.permissions, "ongeki_version": sesh.ongeki_version, "chunithm_version": sesh.chunithm_version, "exp": int(datetime.now(tz=timezone.utc).timestamp()) + exp_seconds }, b64decode(self.core_config.frontend.secret), algorithm="HS256") return jwt.encode({
"user_id": sesh.user_id,
"current_ip": sesh.current_ip,
"permissions": sesh.permissions,
"ongeki_version": sesh.ongeki_version,
"chunithm_version": sesh.chunithm_version,
"maimai_version": sesh.maimai_version,
"exp": int(datetime.now(tz=timezone.utc).timestamp()) + exp_seconds },
b64decode(self.core_config.frontend.secret),
algorithm="HS256"
)
except jwt.InvalidKeyError: except jwt.InvalidKeyError:
self.logger.error("Failed to encode User session because the secret is invalid!") self.logger.error("Failed to encode User session because the secret is invalid!")
return "" return ""

View File

@ -2,10 +2,12 @@ from titles.mai2.index import Mai2Servlet
from titles.mai2.const import Mai2Constants from titles.mai2.const import Mai2Constants
from titles.mai2.database import Mai2Data from titles.mai2.database import Mai2Data
from titles.mai2.read import Mai2Reader from titles.mai2.read import Mai2Reader
from .frontend import Mai2Frontend
index = Mai2Servlet index = Mai2Servlet
database = Mai2Data database = Mai2Data
reader = Mai2Reader reader = Mai2Reader
frontend = Mai2Frontend
game_codes = [ game_codes = [
Mai2Constants.GAME_CODE_DX, Mai2Constants.GAME_CODE_DX,
Mai2Constants.GAME_CODE_FINALE, Mai2Constants.GAME_CODE_FINALE,

190
titles/mai2/frontend.py Normal file
View File

@ -0,0 +1,190 @@
from typing import List
from starlette.routing import Route, Mount
from starlette.requests import Request
from starlette.responses import Response, RedirectResponse
from os import path
import yaml
import jinja2
from core.frontend import FE_Base, UserSession
from core.config import CoreConfig
from .database import Mai2Data
from .config import Mai2Config
from .const import Mai2Constants
class Mai2Frontend(FE_Base):
def __init__(
self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str
) -> None:
super().__init__(cfg, environment)
self.data = Mai2Data(cfg)
self.game_cfg = Mai2Config()
if path.exists(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}"):
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{Mai2Constants.CONFIG_NAME}"))
)
self.nav_name = "maimai"
def get_routes(self) -> List[Route]:
return [
Route("/", self.render_GET, methods=['GET']),
Mount("/playlog", routes=[
Route("/", self.render_GET_playlog, methods=['GET']),
Route("/{index}", self.render_GET_playlog, methods=['GET']),
]),
Route("/update.name", self.update_name, methods=['POST']),
Route("/version.change", self.version_change, methods=['POST']),
]
async def render_GET(self, request: Request) -> bytes:
template = self.environment.get_template(
"titles/mai2/templates/mai2_index.jinja"
)
usr_sesh = self.validate_session(request)
if not usr_sesh:
usr_sesh = UserSession()
if usr_sesh.user_id > 0:
versions = await self.data.profile.get_all_profile_versions(usr_sesh.user_id)
profile = []
if versions:
# maimai_version is -1 means it is not initialized yet, select a default version from existing.
if usr_sesh.maimai_version < 0:
usr_sesh.maimai_version = versions[0]['version']
profile = await self.data.profile.get_profile_detail(usr_sesh.user_id, usr_sesh.maimai_version)
versions = [x['version'] for x in versions]
resp = Response(template.render(
title=f"{self.core_config.server.name} | {self.nav_name}",
game_list=self.environment.globals["game_list"],
sesh=vars(usr_sesh),
user_id=usr_sesh.user_id,
profile=profile,
version_list=Mai2Constants.VERSION_STRING,
versions=versions,
cur_version=usr_sesh.maimai_version
), media_type="text/html; charset=utf-8")
if usr_sesh.maimai_version >= 0:
encoded_sesh = self.encode_session(usr_sesh)
resp.delete_cookie("DIANA_SESH")
resp.set_cookie("DIANA_SESH", encoded_sesh)
return resp
else:
return RedirectResponse("/gate/", 303)
async def render_GET_playlog(self, request: Request) -> bytes:
template = self.environment.get_template(
"titles/mai2/templates/mai2_playlog.jinja"
)
usr_sesh = self.validate_session(request)
if not usr_sesh:
print("wtf")
usr_sesh = UserSession()
if usr_sesh.user_id > 0:
if usr_sesh.maimai_version < 0:
print(usr_sesh.maimai_version)
return RedirectResponse("/game/mai2/", 303)
path_index = request.path_params.get('index')
if not path_index or int(path_index) < 1:
index = 0
else:
index = int(path_index) - 1 # 0 and 1 are 1st page
user_id = usr_sesh.user_id
playlog_count = await self.data.score.get_user_playlogs_count(user_id)
if playlog_count < index * 20 :
return Response(template.render(
title=f"{self.core_config.server.name} | {self.nav_name}",
game_list=self.environment.globals["game_list"],
sesh=vars(usr_sesh),
playlog_count=0
), media_type="text/html; charset=utf-8")
playlog = await self.data.score.get_playlogs(user_id, index, 20)
playlog_with_title = []
for record in playlog:
music_chart = await self.data.static.get_music_chart(usr_sesh.maimai_version, record.musicId, record.level)
if music_chart:
difficultyNum=music_chart.chartId
difficulty=music_chart.difficulty
artist=music_chart.artist
title=music_chart.title
else:
difficultyNum=0
difficulty=0
artist="unknown"
title="musicid: " + str(record.musicId)
playlog_with_title.append({
"raw": record,
"title": title,
"difficultyNum": difficultyNum,
"difficulty": difficulty,
"artist": artist,
})
return Response(template.render(
title=f"{self.core_config.server.name} | {self.nav_name}",
game_list=self.environment.globals["game_list"],
sesh=vars(usr_sesh),
user_id=usr_sesh.user_id,
playlog=playlog_with_title,
playlog_count=playlog_count
), media_type="text/html; charset=utf-8")
else:
return RedirectResponse("/gate/", 303)
async def update_name(self, request: Request) -> bytes:
usr_sesh = self.validate_session(request)
if not usr_sesh:
return RedirectResponse("/gate/", 303)
form_data = await request.form()
new_name: str = form_data.get("new_name")
new_name_full = ""
if not new_name:
return RedirectResponse("/gate/?e=4", 303)
if len(new_name) > 8:
return RedirectResponse("/gate/?e=8", 303)
for x in new_name: # FIXME: This will let some invalid characters through atm
o = ord(x)
try:
if o == 0x20:
new_name_full += chr(0x3000)
elif o < 0x7F and o > 0x20:
new_name_full += chr(o + 0xFEE0)
elif o <= 0x7F:
self.logger.warn(f"Invalid ascii character {o:02X}")
return RedirectResponse("/gate/?e=4", 303)
else:
new_name_full += x
except Exception as e:
self.logger.error(f"Something went wrong parsing character {o:04X} - {e}")
return RedirectResponse("/gate/?e=4", 303)
if not await self.data.profile.update_name(usr_sesh.user_id, new_name_full):
return RedirectResponse("/gate/?e=999", 303)
return RedirectResponse("/game/mai2/?s=1", 303)
async def version_change(self, request: Request):
usr_sesh = self.validate_session(request)
if not usr_sesh:
usr_sesh = UserSession()
if usr_sesh.user_id > 0:
form_data = await request.form()
maimai_version = form_data.get("version")
self.logger.info(f"version change to: {maimai_version}")
if(maimai_version.isdigit()):
usr_sesh.maimai_version=int(maimai_version)
encoded_sesh = self.encode_session(usr_sesh)
self.logger.info(f"Created session with JWT {encoded_sesh}")
resp = RedirectResponse("/game/mai2/", 303)
resp.set_cookie("DIANA_SESH", encoded_sesh)
return resp
else:
return RedirectResponse("/gate/", 303)

View File

@ -511,6 +511,11 @@ rival = Table(
) )
class Mai2ProfileData(BaseData): class Mai2ProfileData(BaseData):
async def get_all_profile_versions(self, user_id: int) -> Optional[List[Row]]:
result = await self.execute(detail.select(detail.c.user == user_id))
if result:
return result.fetchall()
async def put_profile_detail( async def put_profile_detail(
self, user_id: int, version: int, detail_data: Dict, is_dx: bool = True self, user_id: int, version: int, detail_data: Dict, is_dx: bool = True
) -> Optional[Row]: ) -> Optional[Row]:
@ -899,3 +904,14 @@ class Mai2ProfileData(BaseData):
result = await self.execute(rival.delete(and_(rival.c.user == user_id, rival.c.rival == rival_id))) result = await self.execute(rival.delete(and_(rival.c.user == user_id, rival.c.rival == rival_id)))
if not result: if not result:
self.logger.error(f"Failed to remove rival {rival_id} for user {user_id}!") self.logger.error(f"Failed to remove rival {rival_id} for user {user_id}!")
async def update_name(self, user_id: int, new_name: str) -> bool:
sql = detail.update(detail.c.user == user_id).values(
userName=new_name
)
result = await self.execute(sql)
if result is None:
self.logger.warning(f"Failed to set user {user_id} name to {new_name}")
return False
return True

View File

@ -398,3 +398,23 @@ class Mai2ScoreData(BaseData):
if result is None: if result is None:
return None return None
return result.fetchall() return result.fetchall()
async def get_playlogs(self, user_id: int, idx: int = 0, limit: int = 0) -> Optional[List[Row]]:
sql = playlog.select(playlog.c.user == user_id)
if limit:
sql = sql.limit(limit)
if idx:
sql = sql.offset(idx * limit)
result = await self.execute(sql)
if result:
return result.fetchall()
async def get_user_playlogs_count(self, aime_id: int) -> Optional[Row]:
sql = select(func.count()).where(playlog.c.user == aime_id)
result = await self.execute(sql)
if result is None:
self.logger.warning(f"aime_id {aime_id} has no playlog ")
return None
return result.scalar()

View File

@ -0,0 +1,195 @@
.mai2-header {
text-align: center;
}
ul.mai2-navi {
list-style-type: none;
padding: 0;
overflow: hidden;
background-color: #333;
text-align: center;
display: inline-block;
}
ul.mai2-navi li {
display: inline-block;
}
ul.mai2-navi li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
ul.mai2-navi li a:hover:not(.active) {
background-color: #111;
}
ul.mai2-navi li a.active {
background-color: #4CAF50;
}
ul.mai2-navi li.right {
float: right;
}
@media screen and (max-width: 600px) {
ul.mai2-navi li.right,
ul.mai2-navi li {
float: none;
display: block;
text-align: center;
}
}
table {
border-collapse: collapse;
border-spacing: 0;
border-collapse: separate;
overflow: hidden;
background-color: #555555;
}
th, td {
text-align: left;
border: none;
}
th {
color: white;
}
.table-rowdistinct tr:nth-child(even) {
background-color: #303030;
}
.table-rowdistinct tr:nth-child(odd) {
background-color: #555555;
}
caption {
text-align: center;
color: white;
font-size: 18px;
font-weight: bold;
}
.table-large {
margin: 16px;
}
.table-large th,
.table-large td {
padding: 8px;
}
.table-small {
width: 100%;
margin: 4px;
}
.table-small th,
.table-small td {
padding: 2px;
}
.bg-card {
background-color: #555555;
}
.card-hover {
transition: all 0.2s ease-in-out;
}
.card-hover:hover {
transform: scale(1.02);
}
.basic {
color: #28a745;
font-weight: bold;
}
.hard {
color: #ffc107;
font-weight: bold;
}
.expert {
color: #dc3545;
font-weight: bold;
}
.master {
color: #dd09e8;
font-weight: bold;
}
.ultimate {
color: #000000;
font-weight: bold;
}
.score {
color: #ffffff;
font-weight: bold;
}
.rainbow {
background: linear-gradient(to right, red, yellow, lime, aqua, blue, fuchsia) 0 / 5em;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: bold;
}
.platinum {
color: #FFFF00;
font-weight: bold;
}
.gold {
color: #FFFF00;
font-weight: bold;
}
.scrolling-text {
overflow: hidden;
}
.scrolling-text p {
white-space: nowrap;
display: inline-block;
}
.scrolling-text h6 {
white-space: nowrap;
display: inline-block;
}
.scrolling-text h5 {
white-space: nowrap;
display: inline-block;
}
.scrolling {
animation: scroll 10s linear infinite;
}
@keyframes scroll {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(-100%);
}
}

View File

@ -0,0 +1,17 @@
<div class="mai2-header">
<h1>maimai</h1>
<ul class="mai2-navi">
<li><a class="nav-link" href="/game/mai2/">PROFILE</a></li>
<li><a class="nav-link" href="/game/mai2/playlog/">RECORD</a></li>
</ul>
</div>
<script>
$(document).ready(function () {
var currentPath = window.location.pathname;
if (currentPath === '/game/mai2/') {
$('.nav-link[href="/game/mai2/"]').addClass('active');
} else if (currentPath.startsWith('/game/mai2/playlog/')) {
$('.nav-link[href="/game/mai2/playlog/"]').addClass('active');
}
});
</script>

View File

@ -0,0 +1,134 @@
{% extends "core/templates/index.jinja" %}
{% block content %}
<style>
{% include 'titles/mai2/templates/css/mai2_style.css' %}
</style>
<div class="container">
{% include 'titles/mai2/templates/mai2_header.jinja' %}
{% if profile is defined and profile is not none and profile|length > 0 %}
<div class="row">
<div class="col-lg-8 m-auto mt-3">
<div class="card bg-card rounded">
<table class="table-large table-rowdistinct">
<caption align="top">OVERVIEW</caption>
<tr>
<th>{{ profile.userName }}</th>
<th>
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#name_change">Edit</button>
</th>
</tr>
<tr>
<td>version:</td>
<td>
<select name="version" id="version" onChange="changeVersion(this)">
{% for ver in versions %}
{% if ver == cur_version %}
<option value="{{ ver }}" selected>{{ version_list[ver] }}</option>
{% else %}
<option value="{{ ver }}">{{ version_list[ver] }}</option>
{% endif %}
{% endfor %}
</select>
{% if versions | length > 1 %}
<p style="margin-block-end: 0;">You have {{ versions | length }} versions.</p>
{% endif %}
</td>
</tr>
<tr>
<td>Rating:</td>
<td>
<span class="{% if profile.playerRating >= 15000 %}rainbow{% elif profile.playerRating < 15000 and profile.playerRating >= 14500 %}platinum{% elif profile.playerRating < 14500 and profile.playerRating >=14000 %}platinum{% endif %}">
{{ profile.playerRating }}
</span>
<span>
(highest: {{ profile.highestRating }})
</span>
</td>
</tr>
<tr>
<td>Play Counts:</td>
<td>{{ profile.playCount }}</td>
</tr>
<tr>
<td>Last Play Date:</td>
<td>{{ profile.lastPlayDate }}</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-8 m-auto mt-3">
<div class="card bg-card rounded">
<table class="table-large table-rowdistinct">
<caption align="top">SCORE</caption>
<tr>
<td>Total Delux Score:</td>
<td>{{ profile.totalDeluxscore }}</td>
</tr>
<tr>
<td>Total Basic Delux Score:</td>
<td>{{ profile.totalBasicDeluxscore }}</td>
</tr>
<tr>
<td>Total Advanced Delux Score:</td>
<td>{{ profile.totalAdvancedDeluxscore }}</td>
</tr>
<tr>
<td>Total Expert Delux Score:</td>
<td>{{ profile.totalExpertDeluxscore }}</td>
</tr>
<tr>
<td>Total Master Delux Score:</td>
<td>{{ profile.totalMasterDeluxscore }}</td>
</tr>
<tr>
<td>Total ReMaster Delux Score:</td>
<td>{{ profile.totalReMasterDeluxscore }}</td>
</tr>
</table>
</div>
</div>
</div>
{% if error is defined %}
{% include "core/templates/widgets/err_banner.jinja" %}
{% endif %}
{% elif sesh is defined and sesh is not none and sesh.user_id > 0 %}
No profile information found for this account.
{% else %}
Login to view profile information.
{% endif %}
</div>
<div class="modal fade" id="name_change" tabindex="-1" aria-labelledby="name_change_label" data-bs-theme="dark"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Name change</h5>
</div>
<div class="modal-body">
<form id="new_name_form" action="/game/mai2/update.name" method="post" style="outline: 0;">
<label class="form-label" for="new_name">new name:</label>
<input class="form-control" aria-describedby="newNameHelp" form="new_name_form" id="new_name"
name="new_name" maxlength="14" type="text" required>
<div id="newNameHelp" class="form-text">name must be full-width character string.
</div>
</form>
</div>
<div class="modal-footer">
<input type=submit class="btn btn-primary" type="button" form="new_name_form">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
function changeVersion(sel) {
$.post("/game/mai2/version.change", { version: sel.value })
.done(function (data) {
location.reload();
})
.fail(function () {
alert("Failed to update version.");
});
}
</script>
{% endblock content %}

View File

@ -0,0 +1,225 @@
{% extends "core/templates/index.jinja" %}
{% block content %}
<style>
{% include 'titles/mai2/templates/css/mai2_style.css' %}
</style>
<div class="container">
{% include 'titles/mai2/templates/mai2_header.jinja' %}
{% if playlog is defined and playlog is not none %}
<div class="row">
<h4 style="text-align: center;">Playlog counts: {{ playlog_count }}</h4>
{% set rankName = ['D', 'C', 'B', 'BB', 'BBB', 'A', 'AA', 'AAA', 'S', 'S+', 'SS', 'SS+', 'SSS', 'SSS+'] %}
{% set difficultyName = ['basic', 'hard', 'expert', 'master', 'ultimate'] %}
{% for record in playlog %}
<div class="col-lg-6 mt-3">
<div class="card bg-card rounded card-hover">
<div class="card-header row">
<div class="col-8 scrolling-text">
<h5 class="card-text"> {{ record.title }} </h5>
<br>
<h6 class="card-text"> {{ record.artist }} </h6>
</div>
<div class="col-4">
<h6 class="card-text">{{ record.raw.userPlayDate }}</h6>
<h6 class="card-text">TRACK {{ record.raw.trackNo }}</h6>
</div>
</div>
<div class="card-body row">
<div class="col-3" style="text-align: center;">
<h4 class="card-text">{{ record.raw.deluxscore }}</h4>
<h2>{{ rankName[record.raw.rank] }}</h2>
<h6
class="{% if record.raw.level == 0 %}basic{% elif record.raw.level == 1 %}advanced{% elif record.raw.level == 2 %}expert{% elif record.raw.level == 3 %}master{% elif record.raw.level == 4 %}remaster{% endif %}">
{{ difficultyName[record.raw.level] }}&nbsp&nbsp{{ record.difficulty }}
</h6>
</div>
<div class="col-6" style="text-align: center;">
<table class="table-small table-rowdistinc">
<tr>
<td>CRITICAL PERFECT</td>
<td>
Tap: {{ record.raw.tapCriticalPerfect }}<br>
Hold: {{ record.raw.holdCriticalPerfect }}<br>
Slide: {{ record.raw.slideCriticalPerfect }}<br>
Touch: {{ record.raw.touchCriticalPerfect }}<br>
Break: {{ record.raw.breakCriticalPerfect }}
</td>
</tr>
<tr>
<td>PERFECT</td>
<td>
Tap: {{ record.raw.tapPerfect }}<br>
Hold: {{ record.raw.holdPerfect }}<br>
Slide: {{ record.raw.slidePerfect }}<br>
Touch: {{ record.raw.touchPerfect }}<br>
Break: {{ record.raw.breakPerfect }}
</td>
</tr>
<tr>
<td>GREAT</td>
<td>
Tap: {{ record.raw.tapGreat }}<br>
Hold: {{ record.raw.holdGreat }}<br>
Slide: {{ record.raw.slideGreat }}<br>
Touch: {{ record.raw.touchGreat }}<br>
Break: {{ record.raw.breakGreat }}
</td>
</tr>
<tr>
<td>GOOD</td>
<td>
Tap: {{ record.raw.tapGood }}<br>
Hold: {{ record.raw.holdGood }}<br>
Slide: {{ record.raw.slideGood }}<br>
Touch: {{ record.raw.touchGood }}<br>
Break: {{ record.raw.breakGood }}
</td>
</tr>
<tr>
<td>MISS</td>
<td>
Tap: {{ record.raw.tapMiss }}<br>
Hold: {{ record.raw.holdMiss }}<br>
Slide: {{ record.raw.slideMiss }}<br>
Touch: {{ record.raw.touchMiss }}<br>
Break: {{ record.raw.breakMiss }}
</td>
</tr>
</table>
</div>
<div class="col-3" style="text-align: center;">
{%if record.raw.comboStatus == 1 %}
<h6>FULL COMBO</h6>
{% endif %}
{%if record.raw.comboStatus == 2 %}
<h6>FULL COMBO +</h6>
{% endif %}
{%if record.raw.comboStatus == 3 %}
<h6>ALL PERFECT</h6>
{% endif %}
{%if record.raw.comboStatus == 4 %}
<h6>ALL PERFECT +</h6>
{% endif %}
{%if record.raw.syncStatus == 1 %}
<h6>FULL SYNC</h6>
{% endif %}
{%if record.raw.syncStatus == 2 %}
<h6>FULL SYNC +</h6>
{% endif %}
{%if record.raw.syncStatus == 3 %}
<h6>FULL SYNC DX</h6>
{% endif %}
{%if record.raw.syncStatus == 4 %}
<h6>FULL SYNC DX +</h6>
{% endif %}
{%if record.raw.isAchieveNewRecord == 1 or record.raw.isDeluxscoreNewRecord == 1 %}
<h6>NEW RECORD</h6>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% set playlog_pages = playlog_count // 20 + 1 %}
{% elif sesh is defined and sesh is not none and sesh.user_id > 0 %}
No Playlog information found for this account.
{% else %}
Login to view profile information.
{% endif %}
</div>
<footer class="navbar-fixed-bottom">
<nav aria-label="Playlog page navigation">
<ul class="pagination justify-content-center mt-3">
<li class="page-item"><a id="prev_page" class="page-link" href="#">Previous</a></li>
<li class="page-item"><a id="first_page" class="page-link" href="/game/mai2/playlog/">1</a></li>
<li class="page-item"><a id="prev_3_page" class="page-link" href="">...</a></li>
<li class="page-item"><a id="front_page" class="page-link" href="">2</a></li>
<li class="page-item"><a id="cur_page" class="page-link active" href="">3</a></li>
<li class="page-item"><a id="back_page" class="page-link" href="">4</a></li>
<li class="page-item"><a id="next_3_page" class="page-link" href="">...</a></li>
<li class="page-item"><a id="last_page" class="page-link" href="/game/mai2/playlog/{{ playlog_pages }}">{{
playlog_pages }}</a></li>
<li class="page-item"><a id="next_page" class="page-link" href="#">Next</a></li>
&nbsp
</ul>
</nav>
<div class="row">
<div class="col-5"></div>
<div class="col-2">
<div class="input-group rounded">
<input id="page_input" type="text" class="form-control" placeholder="go to page">
<span class="input-group-btn">
<button id="go_button" class="btn btn-light" type="button">
Go!
</button>
</span>
</div>
</div>
<div class="col-5"></div>
</div>
</footer>
<script>
$(document).ready(function () {
$('.scrolling-text p, .scrolling-text h1, .scrolling-text h2, .scrolling-text h3, .scrolling-text h4, .scrolling-text h5, .scrolling-text h6').each(function () {
var parentWidth = $(this).parent().width();
var elementWidth = $(this).outerWidth();
var elementWidthWithPadding = $(this).outerWidth(true);
if (elementWidthWithPadding > parentWidth) {
$(this).addClass('scrolling');
}
});
var currentUrl = window.location.pathname;
var currentPage = parseInt(currentUrl.split('/').pop());
var rootUrl = '/game/mai2/playlog/';
var playlogPages = {{ playlog_pages }};
if (Number.isNaN(currentPage)) {
currentPage = 1;
}
$('#cur_page').text(currentPage);
$('#prev_page').attr('href', rootUrl + (currentPage - 1))
$('#next_page').attr('href', rootUrl + (currentPage + 1))
$('#front_page').attr('href', rootUrl + (currentPage - 1))
$('#front_page').text(currentPage - 1);
$('#back_page').attr('href', rootUrl + (currentPage + 1))
$('#back_page').text(currentPage + 1);
$('#prev_3_page').attr('href', rootUrl + (currentPage - 3))
$('#next_3_page').attr('href', rootUrl + (currentPage + 3))
if ((currentPage - 1) < 3) {
$('#prev_3_page').hide();
if ((currentPage - 1) < 2) {
$('#front_page').hide();
if (currentPage === 1) {
$('#first_page').hide();
$('#prev_page').addClass('disabled');
}
}
}
if ((playlogPages - currentPage) < 3) {
$('#next_3_page').hide();
if ((playlogPages - currentPage) < 2) {
$('#back_page').hide();
if (currentPage === playlogPages) {
$('#last_page').hide();
$('#next_page').addClass('disabled');
}
}
}
$('#go_button').click(function () {
var pageNumber = parseInt($('#page_input').val());
if (!Number.isNaN(pageNumber) && pageNumber <= playlogPages && pageNumber >= 0) {
var url = '/game/mai2/playlog/' + pageNumber;
window.location.href = url;
} else {
$('#page_input').val('');
$('#page_input').attr('placeholder', 'invalid input!');
}
});
});
</script>
{% endblock content %}