mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-24 17:27:17 +00:00
improve tempalte
This commit is contained in:
parent
5ce870714c
commit
59051dee7a
@ -105,4 +105,5 @@ Service which allows to manage savage loot distribution easy.
|
||||
Web server related settings.
|
||||
|
||||
* `host`: address to bind, string, required.
|
||||
* `port`: port to bind, int, required.
|
||||
* `port`: port to bind, int, required.
|
||||
* `templates`: path to directory with jinja templates, string, required.
|
@ -16,6 +16,7 @@ from service.api.views.html.index import IndexHtmlView
|
||||
from service.api.views.html.loot import LootHtmlView
|
||||
from service.api.views.html.loot_suggest import LootSuggestHtmlView
|
||||
from service.api.views.html.player import PlayerHtmlView
|
||||
from service.api.views.html.static import StaticHtmlView
|
||||
|
||||
|
||||
def setup_routes(app: Application) -> None:
|
||||
@ -33,6 +34,7 @@ def setup_routes(app: Application) -> None:
|
||||
|
||||
# html routes
|
||||
app.router.add_get('/', IndexHtmlView)
|
||||
app.router.add_get('/static/{resource_id}', StaticHtmlView)
|
||||
|
||||
app.router.add_get('/party', PlayerHtmlView)
|
||||
app.router.add_post('/party', PlayerHtmlView)
|
||||
|
@ -18,7 +18,7 @@ from service.models.player import Player, PlayerId
|
||||
class PlayerBaseView(View):
|
||||
|
||||
def player_add(self, job: Job, nick: str, link: Optional[str], priority: int) -> PlayerId:
|
||||
player = Player(job, nick, BiS(), [], link, priority)
|
||||
player = Player(job, nick, BiS(), [], link, int(priority))
|
||||
player_id = player.player_id
|
||||
self.request.app['party'].set_player(player)
|
||||
|
||||
|
@ -31,6 +31,7 @@ class LootSuggestHtmlView(LootBaseView, PlayerBaseView):
|
||||
async def post(self) -> Union[Dict[str, Any], Response]:
|
||||
data = await self.request.post()
|
||||
error = None
|
||||
item_values: Dict[str, Any] = {}
|
||||
players: List[PlayerIdWithCounters] = []
|
||||
|
||||
required = ['piece']
|
||||
@ -40,20 +41,22 @@ class LootSuggestHtmlView(LootBaseView, PlayerBaseView):
|
||||
try:
|
||||
piece = Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})
|
||||
players = self.loot_put(piece)
|
||||
item_values = {'piece': piece.name, 'is_tome': piece.is_tome}
|
||||
|
||||
except Exception as e:
|
||||
self.request.app.logger.exception('could not manage loot')
|
||||
error = repr(e)
|
||||
|
||||
return {
|
||||
'item': item_values,
|
||||
'pieces': Piece.available() + [upgrade.name for upgrade in Upgrade],
|
||||
'request_error': error,
|
||||
'suggest': [
|
||||
{
|
||||
'player': player.pretty_name,
|
||||
'loot_count_bis': player.loot_count_bis,
|
||||
'loot_count_bis': player.loot_count_total_bis,
|
||||
'loot_count': player.loot_count,
|
||||
}
|
||||
for player in players
|
||||
],
|
||||
'request_error': error
|
||||
]
|
||||
}
|
@ -38,7 +38,7 @@ class PlayerHtmlView(PlayerBaseView):
|
||||
{
|
||||
'job': player.job.name,
|
||||
'nick': player.nick,
|
||||
'loot_count_bis': player.loot_count_bis,
|
||||
'loot_count_bis': player.loot_count_total_bis,
|
||||
'loot_count': player.loot_count,
|
||||
'priority': player.priority
|
||||
}
|
||||
|
22
src/service/api/views/html/static.py
Normal file
22
src/service/api/views/html/static.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2019 Evgeniy Alekseev.
|
||||
#
|
||||
# This file is part of ffxivbis
|
||||
# (see https://github.com/arcan1s/ffxivbis).
|
||||
#
|
||||
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
|
||||
#
|
||||
import os
|
||||
|
||||
from aiohttp.web import HTTPNotFound, Response, View
|
||||
|
||||
|
||||
class StaticHtmlView(View):
|
||||
|
||||
async def get(self) -> Response:
|
||||
resource_name = self.request.match_info['resource_id']
|
||||
resource_path = os.path.join(self.request.app['templates_root'], 'static', resource_name)
|
||||
if not os.path.exists(resource_path) or os.path.isdir(resource_path):
|
||||
return HTTPNotFound()
|
||||
|
||||
with open(resource_path) as resource_file:
|
||||
return Response(text=resource_file.read(), content_type='text/css')
|
@ -41,7 +41,9 @@ def setup_service(config: Configuration, database: Database, loot: LootSelector,
|
||||
app.logger.info('setup routes')
|
||||
setup_routes(app)
|
||||
if config.has_option('web', 'templates'):
|
||||
aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(config.get('web', 'templates')))
|
||||
templates_root = app['templates_root'] = config.get('web', 'templates')
|
||||
app['static_root_url'] = '/static'
|
||||
aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(templates_root))
|
||||
|
||||
app.logger.info('setup configuration')
|
||||
app['config'] = config
|
||||
|
@ -45,6 +45,7 @@ class PlayerIdWithCounters(PlayerId):
|
||||
loot_count: int
|
||||
loot_count_bis: int
|
||||
loot_count_total: int
|
||||
loot_count_total_bis: int
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -67,8 +68,9 @@ class Player:
|
||||
return PlayerId(self.job, self.nick)
|
||||
|
||||
def player_id_with_counters(self, piece: Union[Piece, Upgrade, None]) -> PlayerIdWithCounters:
|
||||
return PlayerIdWithCounters(self.job, self.nick, self.priority, self.loot_count(piece),
|
||||
self.loot_count_bis(piece), self.loot_count_total(piece))
|
||||
return PlayerIdWithCounters(self.job, self.nick, self.priority,
|
||||
abs(self.loot_count(piece)), abs(self.loot_count_bis(piece)),
|
||||
abs(self.loot_count_total(piece)), abs(self.loot_count_total_bis(piece)))
|
||||
|
||||
# ordering methods
|
||||
def is_required(self, piece: Union[Piece, Upgrade, None]) -> bool:
|
||||
@ -89,14 +91,19 @@ class Player:
|
||||
|
||||
def loot_count(self, piece: Union[Piece, Upgrade, None]) -> int:
|
||||
if piece is None:
|
||||
return len(self.loot)
|
||||
return self.loot.count(piece)
|
||||
return -self.loot_count_total(piece)
|
||||
return -self.loot.count(piece)
|
||||
|
||||
def loot_count_bis(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||
return len([piece for piece in self.loot if self.bis.has_piece(piece)])
|
||||
def loot_count_bis(self, piece: Union[Piece, Upgrade, None]) -> int:
|
||||
if piece is None:
|
||||
return -self.loot_count_total_bis(piece)
|
||||
return -len([piece for piece in self.loot if self.bis.has_piece(piece)])
|
||||
|
||||
def loot_count_total(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||
return len(self.loot)
|
||||
return -len(self.loot)
|
||||
|
||||
def loot_count_total_bis(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||
return len([piece for piece in self.bis.pieces if not piece.is_tome])
|
||||
|
||||
def loot_priority(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||
return self.priority
|
||||
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Best in slot</title>
|
||||
|
||||
{% include "style.jinja2" %}
|
||||
<link href="{{ static('styles.css') }}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>best in slot</h2>
|
||||
@ -24,7 +25,7 @@
|
||||
<input name="is_tome" id="is_tome" title="is tome" type="checkbox"/>
|
||||
<input name="action" id="action" type="hidden" value="add"/>
|
||||
<input name="method" id="method" type="hidden" value="post"/>
|
||||
<button>add</button>
|
||||
<input name="add" id="add" type="button" value="add"/>
|
||||
</form>
|
||||
<form action="/bis" method="post">
|
||||
<select name="player" id="player" title="player">
|
||||
@ -34,7 +35,7 @@
|
||||
</select>
|
||||
<input name="bis" id="bis" placeholder="player bis link" title="bis" type="text"/>
|
||||
<input name="method" id="method" type="hidden" value="put"/>
|
||||
<button>add</button>
|
||||
<input name="add" id="add" type="button" value="add"/>
|
||||
</form>
|
||||
|
||||
<table id="result">
|
||||
@ -52,8 +53,11 @@
|
||||
<td>{{ item.is_tome|e }}</td>
|
||||
<td>
|
||||
<form action="/bis" method="post">
|
||||
<input name="player" id="player" type="hidden" value="{{ item.player|e }}"/>
|
||||
<input name="piece" id="piece" type="hidden" value="{{ item.piece|e }}"/>
|
||||
<input name="is_tome" id="is_tome" type="hidden" value="{{ item.is_tome|e }}"/>
|
||||
<input name="action" id="action" type="hidden" value="remove"/>
|
||||
<button>remove</button>
|
||||
<input name="remove" id="remove" type="button" value="x"/>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,14 +1,17 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>FFXIV loot helper</title>
|
||||
|
||||
<link href="{{ static('styles.css') }}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<a href="/party" title="party"><h2>party</h2></a>
|
||||
<a href="/bis" title="bis management"><h2>bis</h2></a>
|
||||
<a href="/loot" title="loot management"><h2>loot</h2></a>
|
||||
<a href="/suggest" title="suggest loot"><h2>suggest</h2></a>
|
||||
<h2><a href="/party" title="party">party</a></h2>
|
||||
<h2><a href="/bis" title="bis management">bis</a></h2>
|
||||
<h2><a href="/loot" title="loot management">loot</a></h2>
|
||||
<h2><a href="/suggest" title="suggest loot">suggest</a></h2>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Loot</title>
|
||||
|
||||
{% include "style.jinja2" %}
|
||||
<link href="{{ static('styles.css') }}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>Loot</h2>
|
||||
@ -23,7 +24,7 @@
|
||||
</select>
|
||||
<input name="is_tome" id="is_tome" title="is tome" type="checkbox"/>
|
||||
<input name="action" id="action" type="hidden" value="add"/>
|
||||
<button>add</button>
|
||||
<input name="add" id="add" type="button" value="add"/>
|
||||
</form>
|
||||
|
||||
<table id="result">
|
||||
@ -41,8 +42,11 @@
|
||||
<td>{{ item.is_tome|e }}</td>
|
||||
<td>
|
||||
<form action="/loot" method="post">
|
||||
<input name="player" id="player" type="hidden" value="{{ item.player|e }}"/>
|
||||
<input name="piece" id="piece" type="hidden" value="{{ item.piece|e }}"/>
|
||||
<input name="is_tome" id="is_tome" type="hidden" value="{{ item.is_tome|e }}"/>
|
||||
<input name="action" id="action" type="hidden" value="remove"/>
|
||||
<button>remove</button>
|
||||
<input name="remove" id="remove" type="button" value="x"/>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Suggest loot</title>
|
||||
|
||||
{% include "style.jinja2" %}
|
||||
<link href="{{ static('styles.css') }}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>suggest loot</h2>
|
||||
@ -17,7 +18,7 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input name="is_tome" id="is_tome" title="is tome" type="checkbox"/>
|
||||
<button>suggest</button>
|
||||
<input name="suggest" id="suggest" type="button" value="suggest"/>
|
||||
</form>
|
||||
|
||||
<table id="result">
|
||||
@ -25,6 +26,7 @@
|
||||
<th>player</th>
|
||||
<th>bis pieces looted</th>
|
||||
<th>total pieces looted</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
{% for player in suggest %}
|
||||
@ -32,6 +34,14 @@
|
||||
<td class="include_search">{{ player.player|e }}</td>
|
||||
<td>{{ player.loot_count_bis|e }}</td>
|
||||
<td>{{ player.loot_count|e }}</td>
|
||||
<td>
|
||||
<form action="/loot" method="post">
|
||||
<input name="player" id="player" type="hidden" value="{{ player.player|e }}"/>
|
||||
<input name="piece" id="piece" type="hidden" value="{{ item.piece|e }}"/>
|
||||
<input name="is_tome" id="is_tome" type="hidden" value="{{ item.is_tome|e }}"/>
|
||||
<input name="action" id="action" type="hidden" value="remove"/>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Party</title>
|
||||
|
||||
{% include "style.jinja2" %}
|
||||
<link href="{{ static('styles.css') }}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>party</h2>
|
||||
@ -20,7 +21,7 @@
|
||||
<input name="bis" id="bis" placeholder="player bis link" title="bis" type="text"/>
|
||||
<input name="priority" id="priority" placeholder="player priority" title="priority" type="number" value="0"/>
|
||||
<input name="action" id="action" type="hidden" value="add"/>
|
||||
<button>add</button>
|
||||
<input name="add" id="add" type="button" value="add"/>
|
||||
</form>
|
||||
|
||||
<table id="result">
|
||||
@ -42,8 +43,10 @@
|
||||
<td>{{ player.priority|e }}</td>
|
||||
<td>
|
||||
<form action="/party" method="post">
|
||||
<input name="nick" id="nick" type="hidden" value="{{ player.nick|e }}"/>
|
||||
<input name="job" id="job" type="hidden" value="{{ player.job|e }}"/>
|
||||
<input name="action" id="action" type="hidden" value="remove"/>
|
||||
<button>remove</button>
|
||||
<input name="remove" id="remove" type="button" value="x"/>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
277
templates/static/styles.css
Normal file
277
templates/static/styles.css
Normal file
@ -0,0 +1,277 @@
|
||||
/* in-text images */
|
||||
figure.img {
|
||||
float: right;
|
||||
border: 0px solid #333;
|
||||
padding: 0px;
|
||||
margin: 5px 0px 5px 10px;
|
||||
}
|
||||
figure.img img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
figure.img figcaption {
|
||||
margin: 0px;
|
||||
font-size: 90%;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 .octicon-link, h2 .octicon-link, h3 .octicon-link, h4 .octicon-link, h5 .octicon-link, h6 .octicon-link {
|
||||
display: none;
|
||||
color: #222222;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, h4:hover .anchor, h5:hover .anchor, h6:hover .anchor{
|
||||
padding-left: 8px;
|
||||
margin-left: -24px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1:hover .anchor .octicon-link, h2:hover .anchor .octicon-link, h3:hover .anchor .octicon-link, h4:hover .anchor .octicon-link, h5:hover .anchor .octicon-link, h6:hover .anchor .octicon-link {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px/1.5 "Liberation Sans", Helvetica, Arial, sans-serif;
|
||||
color: #555555;
|
||||
background: #eaeaea
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #222222;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
p, ul, ol, table, pre, dl {
|
||||
margin: 0 0 20px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #393939;
|
||||
}
|
||||
|
||||
h3, h4, h5, h6 {
|
||||
color: #494949;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3399cc;
|
||||
font-weight: 350;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a small {
|
||||
font-size: 11px;
|
||||
color: #777777;
|
||||
margin-top: -0.6em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 1px solid #ffffff;
|
||||
margin: 0;
|
||||
padding: 0 0 0 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-family: "Liberation Mono", Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
|
||||
color: #222222;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 8px 15px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #e5e5e5;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
input, select{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 5px 10px;
|
||||
border-bottom: 1px solid #ffffff;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
dt {
|
||||
color: #444444;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 20%;
|
||||
float: left;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
header ul {
|
||||
list-style: none;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
background: #eeeeee;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #d2d2d2;
|
||||
box-shadow: inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
header li {
|
||||
width: 8%;
|
||||
float: left;
|
||||
border-right: 1px solid #d2d2d2;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
header ul a {
|
||||
line-height: 1;
|
||||
font-size: 11px;
|
||||
color: #999999;
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding-top: 6px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: #222222;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
header ul li + li {
|
||||
width: 8%;
|
||||
border-left: 1px solid #ffffff;
|
||||
}
|
||||
|
||||
header ul li + li + li {
|
||||
width: 8%;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
header ul a strong {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
section {
|
||||
width: 70%;
|
||||
float: right;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
background: #ffffff;
|
||||
height: 1px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 20%;
|
||||
float: left;
|
||||
position: fixed;
|
||||
bottom: 50px;
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 960px) {
|
||||
div.wrapper {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
header, section, footer {
|
||||
float: none;
|
||||
position: static;
|
||||
width: auto;
|
||||
}
|
||||
header {
|
||||
padding-right: 320px;
|
||||
}
|
||||
section {
|
||||
border: 1px solid #e5e5e5;
|
||||
border-width: 1px 0;
|
||||
padding: 20px 0;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
header a small {
|
||||
display: inline;
|
||||
}
|
||||
header ul {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
top: 52px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 720px) {
|
||||
body {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
header {
|
||||
padding: 0;
|
||||
}
|
||||
header ul, header p.view {
|
||||
position: static;
|
||||
}
|
||||
pre, code {
|
||||
word-wrap: normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 480px) {
|
||||
body {
|
||||
padding: 15px;
|
||||
}
|
||||
header ul {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
padding: 0.4in;
|
||||
font-size: 12pt;
|
||||
color: #444444;
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
<style>
|
||||
table { border-collapse: collapse; }
|
||||
table, th, td { border: 1px solid black; padding: 5px; }
|
||||
input { margin: 5px; }
|
||||
#error { color: #ff0000; }
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user