WIP
This commit is contained in:
@@ -67,7 +67,7 @@
|
||||
<pairgoth.api.external.url>${pairgoth.api.external.url}</pairgoth.api.external.url>
|
||||
<pairgoth.webapp.external.url>${pairgoth.webapp.external.url}</pairgoth.webapp.external.url>
|
||||
<pairgoth.store>${pairgoth.store}</pairgoth.store>
|
||||
<pairgoth.store.file.path>${pairgoth.store}</pairgoth.store.file.path>
|
||||
<pairgoth.store.file.path>${pairgoth.store.file.path}</pairgoth.store.file.path>
|
||||
<pairgoth.logger.level>${pairgoth.logger.level}</pairgoth.logger.level>
|
||||
<pairgoth.logger.format>${pairgoth.logger.format}</pairgoth.logger.format>
|
||||
</systemProperties>
|
||||
|
@@ -314,7 +314,7 @@ fun Pairing.toJson() = Json.Object(
|
||||
"type" to type.name,
|
||||
"base" to pairingParams.base.toJson(),
|
||||
"main" to pairingParams.main.toJson(),
|
||||
"secondary" to pairingParams.main.toJson(),
|
||||
"secondary" to pairingParams.secondary.toJson(),
|
||||
"geo" to pairingParams.geo.toJson(),
|
||||
"handicap" to pairingParams.handicap.toJson(),
|
||||
"placement" to placementParams.toJson()
|
||||
|
@@ -248,6 +248,7 @@ class ApiServlet : HttpServlet() {
|
||||
response.status = code
|
||||
if (response.isCommitted) return
|
||||
val errorPayload = Json.Object(
|
||||
"success" to false,
|
||||
"error" to (message ?: "unknown error")
|
||||
)
|
||||
setContentType(response)
|
||||
|
@@ -80,11 +80,15 @@ class Translator private constructor(private val iso: String) {
|
||||
if (groupStart == -1) throw RuntimeException("unexpected case")
|
||||
if (groupStart > start) output.print(text.substring(start, groupStart))
|
||||
val capture = matcher.group(group)
|
||||
var token: String = StringEscapeUtils.unescapeHtml4(capture)
|
||||
|
||||
// CB TODO - unescape and escape steps removed, because it breaks text blocks containing unescaped quotes.
|
||||
// See how it impacts the remaining.
|
||||
|
||||
var token: String = capture // StringEscapeUtils.unescapeHtml4(capture)
|
||||
if (StringUtils.containsOnly(token, "\r\n\t -;:.'\"/<>\u00A00123456789€[]!")) output.print(capture) else {
|
||||
token = normalize(token)
|
||||
token = translate(token)
|
||||
output.print(StringEscapeUtils.escapeHtml4(token))
|
||||
output.print(token) // (StringEscapeUtils.escapeHtml4(token))
|
||||
}
|
||||
val groupEnd = matcher.end(group)
|
||||
if (groupEnd < end) output.print(text.substring(groupEnd, end))
|
||||
@@ -168,6 +172,7 @@ class Translator private constructor(private val iso: String) {
|
||||
|
||||
val providedLanguages = setOf("en", "fr")
|
||||
const val defaultLanguage = "en"
|
||||
const val defaultLocale = "en"
|
||||
|
||||
internal fun notifyExiting() {
|
||||
translators.values.filter {
|
||||
|
@@ -53,15 +53,21 @@ class LanguageFilter : Filter {
|
||||
}
|
||||
|
||||
private fun getPreferredLanguage(request: HttpServletRequest): String {
|
||||
return (request.session.getAttribute("lang") as String?) ?:
|
||||
( langHeaderParser.findAll(request.getHeader("Accept-Language") ?: "").filter {
|
||||
var lang = request.session.getAttribute("lang") as String?
|
||||
if (lang == null) {
|
||||
parseLanguageHeader(request)
|
||||
lang = request.session.getAttribute("lang") as String?
|
||||
}
|
||||
return lang ?: defaultLanguage
|
||||
}
|
||||
|
||||
private fun parseLanguageHeader(request: HttpServletRequest) {
|
||||
langHeaderParser.findAll(request.getHeader("Accept-Language") ?: "").filter {
|
||||
providedLanguages.contains(it.groupValues[1])
|
||||
}.sortedByDescending {
|
||||
it.groupValues[2].toDoubleOrNull() ?: 1.0
|
||||
it.groupValues[3].toDoubleOrNull() ?: 1.0
|
||||
}.firstOrNull()?.let {
|
||||
it.groupValues[1]
|
||||
} ?: defaultLanguage ).also {
|
||||
request.session.setAttribute("lang", it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +75,6 @@ class LanguageFilter : Filter {
|
||||
|
||||
companion object {
|
||||
private val langPattern = Regex("/([a-z]{2})(/.+)")
|
||||
private val langHeaderParser = Regex("(?:\\b(\\*|[a-z]{2})(?:(?:_|-)\\w+)?)(?:;q=([0-9.]+))?")
|
||||
private val langHeaderParser = Regex("(?:\\b(\\*|[a-z]{2})(?:(?:_|-)\\w+)?)(?:;q=([0-9.]+))?") ADD locale group captue
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#foreach($tour in $api.get('tour').entrySet())
|
||||
<div class="section">
|
||||
$tour
|
||||
<a href="tour?tour_id=${tour.key}" class="ui open basic secondary white icon floating button">
|
||||
<a href="tour?id=${tour.key}" class="ui open basic secondary white icon floating button">
|
||||
<i class="fa fa-folder-open-o"></i>
|
||||
Open
|
||||
</a>
|
||||
@@ -68,7 +68,3 @@
|
||||
});
|
||||
// ]]#
|
||||
</script>
|
||||
<!-- date range picker -->
|
||||
<script type="text/javascript" src="/lib/datepicker-1.3.3/datepicker-full.min.js"></script>
|
||||
<script type="text/javascript" src="/lib/datepicker-1.3.3/locales/${request.lang}.js"></script>
|
||||
<link rel="stylesheet" href="/lib/datepicker-1.3.3/datepicker.min.css">
|
||||
|
@@ -106,19 +106,23 @@ Element.prototype.modal = function(show) {
|
||||
|
||||
function formValue(name) {
|
||||
let ctl = $(`[name="${name}"]`)[0];
|
||||
let type = ctl.tagName;
|
||||
if (!ctl) {
|
||||
console.error(`unknown input name: ${name}`)
|
||||
}
|
||||
let tag = ctl.tagName;
|
||||
let type = tag === 'INPUT' ? ctl.attr('type') : undefined;
|
||||
if (
|
||||
(type === 'INPUT' && ['text', 'number'].includes(ctl.attr('type'))) ||
|
||||
type === 'SELECT'
|
||||
(tag === 'INPUT' && ['text', 'number'].includes(ctl.attr('type'))) ||
|
||||
tag === 'SELECT'
|
||||
) {
|
||||
return ctl.value;
|
||||
} else if (type === 'INPUT' && ctl.attr('type') === 'radio') {
|
||||
} else if (tag === 'INPUT' && ctl.attr('type') === 'radio') {
|
||||
ctl = $(`input[name="${name}"]:checked`)[0];
|
||||
if (ctl) return ctl.value;
|
||||
} else if (type === 'INPUT' && ctl.attr('type') === 'radio') {
|
||||
} else if (tag === 'INPUT' && ctl.attr('type') === 'checkbox') {
|
||||
return ctl.checked;
|
||||
}
|
||||
console.error(`unknown input name: ${name}`);
|
||||
console.error(`unhandled input tag or type for input ${name} (tag: ${tag}, type:${type}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -1,17 +1,17 @@
|
||||
#macro(hms $value)#if($value)#set($hh = $value / 3600)#set($mm = ($value - $hh) / 60)#set($ss = $value % 60)$hh:$mm:$ss#end#end
|
||||
#macro(toHMS $value)#if($value)#set($hh = $value / 3600)#set($mm = ($value - $hh) / 60)#set($ss = $value % 60)$hh:$mm:$ss#end#end
|
||||
#macro(levels $sel)
|
||||
#foreach($k in [-30..-1])
|
||||
#set($disp = "${math.abs($k)}k"
|
||||
<option value="$k" #if($sel && $sel == $k)selected#end>$disp</option>
|
||||
#end
|
||||
#foreach($d in [0..9])
|
||||
#foreach($d in [8..0])
|
||||
#set($dan = $d + 1)
|
||||
#set($disp = "${dan}d")
|
||||
<option value="$d" #if($sel && $sel == $d)selected#end>$disp</option>
|
||||
<option value="$d" #if("$!sel" != "" && $sel == $d)selected#end>$disp</option>
|
||||
#end
|
||||
#foreach($k in [-1..-30])
|
||||
#set($disp = "${math.abs($k)}k")
|
||||
<option value="$k" #if($sel && $sel == $k)selected#end>$disp</option>
|
||||
#end
|
||||
#end
|
||||
#if($params.tour)
|
||||
#set($tour = $api.get("tour/${params.tour}"))
|
||||
#if($params.id)
|
||||
#set($tour = $api.get("tour/${params.id}"))
|
||||
#if (!$tour)
|
||||
<div class="section">
|
||||
<h2 class="error">Invalid tournament id</h2>
|
||||
@@ -32,15 +32,17 @@
|
||||
<input type="text" name="shortName" required placeholder="short_name" #if($tour) value="$tour.shortName" #end/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="fields">
|
||||
<div class="ten wide field">
|
||||
<label>Dates</label>
|
||||
<span id="date-range">
|
||||
from
|
||||
<input type="text" name="startDate" required class="date" placeholder="start date" #if($tour)value="$tour.startDate"#end/>
|
||||
<input type="date" name="startDate" required class="date" placeholder="start date" #if($tour) value="$tour.startDate" #end/>
|
||||
to
|
||||
<input type="text" name="endDate" required class="date" placeholder="end date" #if($tour)value="$tour.startDate"#end/>
|
||||
<input type="date" name="endDate" required class="date" placeholder="end date" #if($tour) value="$tour.startDate" #end/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two stackable fields">
|
||||
<div class="seven wide field">
|
||||
<label>Country</label>
|
||||
@@ -65,7 +67,7 @@
|
||||
</div>
|
||||
<div class="roundbox">
|
||||
<div class="two fields">
|
||||
<div class="fourteen wide field">
|
||||
<div class="twelve wide field">
|
||||
<label>Tournament type</label>
|
||||
<select name="type">
|
||||
<option value="INDIVIDUAL" #if(!$tour || $tour.type == 'INDIVIDUAL') checked #end>Standard tournament of individual players</option>
|
||||
@@ -80,28 +82,64 @@
|
||||
</div>
|
||||
<div class="four wide field">
|
||||
<label>Rounds</label>
|
||||
<span><input type="number" name="rounds" required min="1" value="#if($tour)rounds#{else}1#end"/> rounds</span>
|
||||
<span><input type="number" name="rounds" required min="1" value="#if($tour)$tour.rounds#{else}1#end"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="four fields">
|
||||
<div class="four wide field">
|
||||
<label>Pairing</label>
|
||||
<select name="pairing">
|
||||
<option value="SWISS" #if($tour && $tour.pairing.type == 'SWISS')checked#end>Swiss</option>
|
||||
<option value="MAC_MAHON" #if(!$tour || $tour.pairing.type == 'MAC_MAHON') checked #end>Mac Mahon</option>
|
||||
<option value="SWISS" #if($tour && $tour.pairing.type == 'SWISS') checked #end>Swiss</option>
|
||||
<option value="ROUND_ROBIN" #if($tour && $tour.pairing.type == 'ROUND_ROBIN') checked #end>Round-robin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mms four wide field">
|
||||
<label>MMS floor</label>
|
||||
<select name="mmsFloor">
|
||||
#* MM floor parameter not shown on creation page
|
||||
<div class="mms pairing four wide field #if($tour && $tour.pairing.type != 'MAC_MAHON') hidden #end">
|
||||
<label>MM floor</label>
|
||||
<select name="mmFloor">
|
||||
#set($floor = -20)
|
||||
#if($tour) #set($floor = $tour.pairing.
|
||||
#if($tour) #set($floor = $tour.pairing.mmFloor) #end
|
||||
#levels($floor)
|
||||
</select>
|
||||
</div>
|
||||
<div class="mms four wide field">
|
||||
<label>MMS ceiling</label>
|
||||
*#
|
||||
<div class="mms pairing four wide field #if($tour && $tour.pairing.type != 'MAC_MAHON') hidden #end">
|
||||
<label>Hd correction</label>
|
||||
<input name="correction" type="number" min="-9" max="0" value="#if($tour && "$!tour.pairing.handicap.correction" != "")$tour.pairing.handicap.correction#{else}-1#end"/>
|
||||
</div>
|
||||
<div class="mms pairing four wide field #if($tour && $tour.pairing.type != 'MAC_MAHON') hidden #end">
|
||||
<label>MM bar</label>
|
||||
<select name="mmBar">
|
||||
#set($bar = 0)
|
||||
#if($tour && "$!tour.pairing.mmBar" != "") #set($bar = $tour.pairing.mmBar) #end
|
||||
#levels($bar)
|
||||
</select>
|
||||
</div>
|
||||
<div class="mms pairing four wide field #if($tour && $tour.pairing.type != 'MAC_MAHON') hidden #end">
|
||||
<label>Hd treshold</label>
|
||||
<select name="treshold">
|
||||
#set($limit = 0)
|
||||
#if($tour && "$!tour.pairing.handicap.treshold" != "") #set($limit = $tour.pairing.handicap.treshold) #end
|
||||
#levels($limit)
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiss pairing four wide field #if(!$tour || $tour && $tour.pairing.type != 'SWISS') hidden #end">
|
||||
<label>1st round seeding</label>
|
||||
<select name="firstSeed">
|
||||
<option value="SPLIT_AND_FOLD" #if($tour && "$!tour.pairing.main.firstSeed" == "SPLIT_AND_FOLD") selected #end>Split and fold</option>
|
||||
<option value="SPLIT_AND_RANDOM" #if(!$tour || "$!tour.pairing.main.firstSeed" == "SPLIT_AND_RANDOM") selected #end>Split and random</option>
|
||||
<option value="SPLIT_AND_SLIP" #if($tour && "$!tour.pairing.main.firstSeed" == "SPLIT_AND_SLIP") selected #end>Split and slip</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="swiss pairing four wide field #if(!$tour || $tour && $tour.pairing.type != 'SWISS')hidden#end">
|
||||
<label>Next rounds seeding</label>
|
||||
<select name="secondSeed">
|
||||
<option value="SPLIT_AND_FOLD" #if(!$tour || "$!tour.pairing.main.secondSeed" == "SPLIT_AND_FOLD") selected #end>Split and fold</option>
|
||||
<option value="SPLIT_AND_RANDOM" #if($tour && "$!tour.pairing.main.secondSeed" == "SPLIT_AND_RANDOM") selected #end>Split and random</option>
|
||||
<option value="SPLIT_AND_SLIP" #if($tour && "$!tour.pairing.main.secondSeed" == "SPLIT_AND_SLIP") selected #end>Split and slip</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="roundbox">
|
||||
@@ -118,8 +156,8 @@
|
||||
<label>Goban</label>
|
||||
<select name="gobanSize">
|
||||
<option value="9" #if($tour && $tour.gobanSize == 9) selected #end>9x9</option>
|
||||
<option value="13" #if($tour && $tour.gobanSize == 9)selected#end>13x13</option>
|
||||
<option value="19" #if(!$tour || $tour.gobanSize == 9)selected#end>19x19</option>
|
||||
<option value="13" #if($tour && $tour.gobanSize == 13) selected #end>13x13</option>
|
||||
<option value="19" #if(!$tour || $tour.gobanSize == 19) selected #end>19x19</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="three wide field">
|
||||
@@ -139,27 +177,27 @@
|
||||
</div>
|
||||
<div class="three wide field">
|
||||
<label>Main time</label>
|
||||
<input name="mainTime" type="text" class="duration" value="#if($tour)#hms($tour.timeSystem.mainTime)#{else}00:40:00#end"/>
|
||||
<input name="mainTime" type="text" class="duration" value="#if($tour && $tour.timeSystem.mainTime)#toHMS($tour.timeSystem.mainTime)#{else}00:40:00#end"/>
|
||||
</div>
|
||||
<div id="increment" class="three wide field #if($tour && $tour.timeSystem.type != 'FISCHER')hidden#end">
|
||||
<label>Increment</label>
|
||||
<input name="increment" type="text" class="duration" value="#if($tour)#hms($tour.timeSystem.increment)#{else}00:00:20#end"/>
|
||||
<input name="increment" type="text" class="duration" value="#if($tour && "$!tour.timeSystem.increment" != "")#toHMS($tour.timeSystem.increment)#{else}00:00:20#end"/>
|
||||
</div>
|
||||
<div id="maxTime" class="three wide field #if($tour && $tour.timeSystem.type != 'FISCHER')hidden#end">
|
||||
<label>Max time</label>
|
||||
<input name="maxTime" type="text" class="duration" value="#if($tour)#hms($tour.timeSystem.maxTime)#{else}00:40:00#end"/>
|
||||
<input name="maxTime" type="text" class="duration" value="#if($tour && "$!tour.timeSystem.maxTime" != "")#toHMS($tour.timeSystem.maxTime)#{else}00:40:00#end"/>
|
||||
</div>
|
||||
<div id="byoyomi" class="three wide field #if(!$tour || $tour.timeSystem.type != 'CANADIAN' && $tour.timeSystem.type != 'STANDARD')hidden#end">
|
||||
<label>Byo-yomi time</label>
|
||||
<input name="byoyomi" type="text" class="duration" value="#if($tour)#hms($tour.timeSystem.byoyomi)#{else}00:05:00#end"/>
|
||||
<input name="byoyomi" type="text" class="duration" value="#if($tour && "$!tour.timeSystem.byoyomi" != "")#toHMS($tour.timeSystem.byoyomi)#{else}00:05:00#end"/>
|
||||
</div>
|
||||
<div id="periods" class="three wide field #if(!$tour || $tour.timeSystem.type != 'STANDARD')hidden#end">
|
||||
<label>Byo-yomi periods</label>
|
||||
<input name="periods" type="number" min="0" value="#if($tour)$tour.timeSystem.periods#{else}3#end"/>
|
||||
<input name="periods" type="number" min="0" value="#if($tour && "$!tour.timeSystem.periods" != "")$tour.timeSystem.periods#{else}3#end"/>
|
||||
</div>
|
||||
<div id="stones" class="three wide field #if(!$tour || $tour.timeSystem.type != 'CANADIAN')hidden#end">
|
||||
<label>Byo-yomi stones</label>
|
||||
<input name="stones" class="seconds" type="number" min="0" value="#if($tour)$tour.timeSystem.stones#{else}15#end"/>
|
||||
<input name="stones" class="seconds" type="number" min="0" value="#if($tour && "$!tour.timeSystem.stones" != "")$tour.timeSystem.stones#{else}15#end"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,12 +219,21 @@
|
||||
// #[[
|
||||
const safeRegex = /^[-a-zA-Z0-9_.]+$/;
|
||||
function parseDate(value) {
|
||||
return value;
|
||||
/*
|
||||
let locale = Datepicker.locales[lang];
|
||||
if (locale) {
|
||||
let date = Datepicker.parseDate(value, locale.format, locale);
|
||||
return Datepicker.formatDate(date, 'yyyy-mm-dd')
|
||||
}
|
||||
else return undefined;
|
||||
*/
|
||||
}
|
||||
function fromHMS(value) {
|
||||
if (value && /\d+:\d+:\d+/.test(value)) {
|
||||
let parts = value.split(':');
|
||||
return parts[0] * 3600 + parts[1] * 60 + parts[2];
|
||||
}
|
||||
}
|
||||
onLoad(() => {
|
||||
|
||||
@@ -215,6 +262,7 @@
|
||||
valid = false;
|
||||
shortNameCtl.setCustomValidity(msg('invalid_character'));
|
||||
}
|
||||
if (!valid) return;
|
||||
});
|
||||
|
||||
new DateRangePicker($('#date-range')[0], {
|
||||
@@ -222,38 +270,6 @@
|
||||
language: lang
|
||||
});
|
||||
|
||||
$('#tournament-infos').on('submit', e => {
|
||||
e.preventDefault();
|
||||
let tour = {
|
||||
name: formValue('name'),
|
||||
shortName: formValue('shortName'),
|
||||
startDate: parseDate(formValue('startDate')),
|
||||
endDate: parseDate(formValue('endDate')),
|
||||
type: formValue('type'),
|
||||
rounds: formValue('rounds'),
|
||||
country: formValue('country'),
|
||||
online: formValue('online'),
|
||||
location: formValue('online') ? "" : formValue('location'),
|
||||
pairing: {
|
||||
type: formValue('pairing')
|
||||
},
|
||||
timeSystem: {
|
||||
type: formValue('timeSystemType')
|
||||
}
|
||||
}
|
||||
console.log(tour);
|
||||
if (typeof(tour_id) !== 'undefined') {
|
||||
api.putJson(`tour/${tour_id}`, tour);
|
||||
} else {
|
||||
api.postJson('tour', tour)
|
||||
.then((o) => {
|
||||
if (o !== 'error') {
|
||||
console.log("success ==> %o", o);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('input[name="online"]').on('change', e => {
|
||||
$('input[name="location"]')[0].disabled = e.target.checked;
|
||||
});
|
||||
@@ -296,12 +312,62 @@
|
||||
lazy: false,
|
||||
overwrite: true
|
||||
});
|
||||
|
||||
$('#tournament-infos').on('submit', e => {
|
||||
e.preventDefault();
|
||||
let tour = {
|
||||
name: formValue('name'),
|
||||
shortName: formValue('shortName'),
|
||||
startDate: parseDate(formValue('startDate')),
|
||||
endDate: parseDate(formValue('endDate')),
|
||||
type: formValue('type'),
|
||||
rounds: formValue('rounds'),
|
||||
country: formValue('country'),
|
||||
online: formValue('online'),
|
||||
location: formValue('online') ? "" : formValue('location'),
|
||||
pairing: {
|
||||
type: formValue('pairing'),
|
||||
// mmFloor: formValue('mmFloor'),
|
||||
mmBar: formValue('mmBar'),
|
||||
main: {
|
||||
firstSeed: formValue('firstSeed'),
|
||||
secondSeed: formValue('secondSeed')
|
||||
},
|
||||
handicap: {
|
||||
correction: formValue('correction'),
|
||||
treshold: formValue('treshold')
|
||||
}
|
||||
},
|
||||
timeSystem: {
|
||||
type: formValue('timeSystemType'),
|
||||
mainTime: fromHMS(formValue('mainTime')),
|
||||
increment: fromHMS(formValue('increment')),
|
||||
maxTime: fromHMS(formValue('maxTime')),
|
||||
byoyomi: fromHMS(formValue('byoyomi')),
|
||||
periods: formValue('periods'),
|
||||
stones: formValue('stones')
|
||||
}
|
||||
}
|
||||
console.log(tour);
|
||||
if (typeof(tour_id) !== 'undefined') {
|
||||
api.putJson(`tour/${tour_id}`, tour);
|
||||
} else {
|
||||
api.postJson('tour', tour)
|
||||
.then((o) => {
|
||||
if (o !== 'error') {
|
||||
console.log("success ==> %o", o);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
// ]]#
|
||||
</script>
|
||||
<!--
|
||||
<script type="text/javascript" src="/lib/datepicker-1.3.3/datepicker-full.min.js"></script>
|
||||
<script type="text/javascript" src="/lib/datepicker-1.3.3/locales/${request.lang}.js"></script>
|
||||
<link rel="stylesheet" href="/lib/datepicker-1.3.3/datepicker.min.css">
|
||||
-->
|
||||
<style type="text/css">
|
||||
.ui.form input[type=checkbox][name=online] {
|
||||
vertical-align: initial;
|
||||
|
Reference in New Issue
Block a user