add search by winner
parent
478c01ff21
commit
2c5708047c
|
@ -12,7 +12,11 @@ import chess.{ Mode }
|
||||||
final class DataForm {
|
final class DataForm {
|
||||||
|
|
||||||
val search = Form(mapping(
|
val search = Form(mapping(
|
||||||
"usernames" -> optional(nonEmptyText),
|
"players" -> mapping(
|
||||||
|
"a" -> optional(nonEmptyText),
|
||||||
|
"b" -> optional(nonEmptyText),
|
||||||
|
"winner" -> optional(nonEmptyText)
|
||||||
|
)(SearchPlayer.apply)(SearchPlayer.unapply),
|
||||||
"variant" -> numberIn(Query.variants),
|
"variant" -> numberIn(Query.variants),
|
||||||
"mode" -> numberIn(Query.modes),
|
"mode" -> numberIn(Query.modes),
|
||||||
"opening" -> stringIn(Query.openings),
|
"opening" -> stringIn(Query.openings),
|
||||||
|
@ -28,8 +32,10 @@ final class DataForm {
|
||||||
"dateMin" -> stringIn(Query.dates),
|
"dateMin" -> stringIn(Query.dates),
|
||||||
"dateMax" -> stringIn(Query.dates),
|
"dateMax" -> stringIn(Query.dates),
|
||||||
"status" -> numberIn(Query.statuses),
|
"status" -> numberIn(Query.statuses),
|
||||||
"sortField" -> nonEmptyText.verifying(hasKey(Sorting.fields, _)),
|
"sort" -> mapping(
|
||||||
"sortOrder" -> nonEmptyText.verifying(hasKey(Sorting.orders, _))
|
"field" -> nonEmptyText.verifying(hasKey(Sorting.fields, _)),
|
||||||
|
"order" -> nonEmptyText.verifying(hasKey(Sorting.orders, _))
|
||||||
|
)(SearchSort.apply)(SearchSort.unapply)
|
||||||
)(SearchData.apply)(SearchData.unapply))
|
)(SearchData.apply)(SearchData.unapply))
|
||||||
|
|
||||||
private def numberIn(choices: Seq[(Int, String)]) =
|
private def numberIn(choices: Seq[(Int, String)]) =
|
||||||
|
@ -43,7 +49,7 @@ final class DataForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
case class SearchData(
|
case class SearchData(
|
||||||
usernames: Option[String] = None,
|
players: SearchPlayer = SearchPlayer(),
|
||||||
variant: Option[Int] = None,
|
variant: Option[Int] = None,
|
||||||
mode: Option[Int] = None,
|
mode: Option[Int] = None,
|
||||||
opening: Option[String] = None,
|
opening: Option[String] = None,
|
||||||
|
@ -59,11 +65,12 @@ case class SearchData(
|
||||||
dateMin: Option[String] = None,
|
dateMin: Option[String] = None,
|
||||||
dateMax: Option[String] = None,
|
dateMax: Option[String] = None,
|
||||||
status: Option[Int] = None,
|
status: Option[Int] = None,
|
||||||
sortField: String = Sorting.default.field,
|
sort: SearchSort = SearchSort()) {
|
||||||
sortOrder: String = Sorting.default.order) {
|
|
||||||
|
|
||||||
lazy val query = Query(
|
lazy val query = Query(
|
||||||
usernames = (~usernames).split(" ").toList map clean filter (_.nonEmpty),
|
user1 = players.cleanA,
|
||||||
|
user2 = players.cleanB,
|
||||||
|
winner = players.cleanWinner,
|
||||||
variant = variant,
|
variant = variant,
|
||||||
rated = mode flatMap Mode.apply map (_.rated),
|
rated = mode flatMap Mode.apply map (_.rated),
|
||||||
opening = opening map clean,
|
opening = opening map clean,
|
||||||
|
@ -74,7 +81,7 @@ case class SearchData(
|
||||||
duration = Range(durationMin, durationMax),
|
duration = Range(durationMin, durationMax),
|
||||||
date = Range(dateMin flatMap toDate, dateMax flatMap toDate),
|
date = Range(dateMin flatMap toDate, dateMax flatMap toDate),
|
||||||
status = status,
|
status = status,
|
||||||
sorting = Sorting(sortField, sortOrder)
|
sorting = Sorting(sort.field, sort.order)
|
||||||
)
|
)
|
||||||
|
|
||||||
private def clean(s: String) = s.trim.toLowerCase
|
private def clean(s: String) = s.trim.toLowerCase
|
||||||
|
@ -88,3 +95,22 @@ case class SearchData(
|
||||||
case _ ⇒ None
|
case _ ⇒ None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class SearchPlayer(
|
||||||
|
a: Option[String] = None,
|
||||||
|
b: Option[String] = None,
|
||||||
|
winner: Option[String] = None) {
|
||||||
|
|
||||||
|
def cleanA = clean(a)
|
||||||
|
def cleanB = clean(b)
|
||||||
|
def cleanWinner = clean(winner) |> { w ⇒
|
||||||
|
w filter List(a, b).flatten.contains
|
||||||
|
}
|
||||||
|
|
||||||
|
private def clean(s: Option[String]) =
|
||||||
|
s map (_.trim.toLowerCase) filter (_.nonEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class SearchSort(
|
||||||
|
field: String = Sorting.default.field,
|
||||||
|
order: String = Sorting.default.order)
|
||||||
|
|
|
@ -14,6 +14,7 @@ object Game {
|
||||||
val rated = "ra"
|
val rated = "ra"
|
||||||
val variant = "va"
|
val variant = "va"
|
||||||
val uids = "ui"
|
val uids = "ui"
|
||||||
|
val winner = "wi"
|
||||||
val averageElo = "el"
|
val averageElo = "el"
|
||||||
val ai = "ai"
|
val ai = "ai"
|
||||||
val opening = "op"
|
val opening = "op"
|
||||||
|
@ -37,6 +38,7 @@ object Game {
|
||||||
field(rated, "boolean"),
|
field(rated, "boolean"),
|
||||||
field(variant, "short"),
|
field(variant, "short"),
|
||||||
field(uids, "string"),
|
field(uids, "string"),
|
||||||
|
field(winner, "string"),
|
||||||
field(averageElo, "short"),
|
field(averageElo, "short"),
|
||||||
field(ai, "short"),
|
field(ai, "short"),
|
||||||
field(opening, "string"),
|
field(opening, "string"),
|
||||||
|
@ -54,6 +56,7 @@ object Game {
|
||||||
rated -> game.rated.some,
|
rated -> game.rated.some,
|
||||||
variant -> game.variant.id.some,
|
variant -> game.variant.id.some,
|
||||||
uids -> (game.userIds.toNel map (_.list)),
|
uids -> (game.userIds.toNel map (_.list)),
|
||||||
|
winner -> (game.winner flatMap (_.userId)),
|
||||||
averageElo -> game.averageUsersElo,
|
averageElo -> game.averageUsersElo,
|
||||||
ai -> game.aiLevel,
|
ai -> game.aiLevel,
|
||||||
date -> (dateFormatter print createdAt).some,
|
date -> (dateFormatter print createdAt).some,
|
||||||
|
|
|
@ -9,7 +9,9 @@ import org.joda.time.DateTime
|
||||||
import org.scala_tools.time.Imports._
|
import org.scala_tools.time.Imports._
|
||||||
|
|
||||||
case class Query(
|
case class Query(
|
||||||
usernames: List[String] = Nil,
|
user1: Option[String] = None,
|
||||||
|
user2: Option[String] = None,
|
||||||
|
winner: Option[String] = None,
|
||||||
variant: Option[Int] = None,
|
variant: Option[Int] = None,
|
||||||
status: Option[Int] = None,
|
status: Option[Int] = None,
|
||||||
turns: Range[Int] = Range.none,
|
turns: Range[Int] = Range.none,
|
||||||
|
@ -23,7 +25,9 @@ case class Query(
|
||||||
sorting: Sorting = Sorting.default) {
|
sorting: Sorting = Sorting.default) {
|
||||||
|
|
||||||
def nonEmpty =
|
def nonEmpty =
|
||||||
usernames.nonEmpty ||
|
user1.nonEmpty ||
|
||||||
|
user2.nonEmpty ||
|
||||||
|
winner.nonEmpty ||
|
||||||
variant.nonEmpty ||
|
variant.nonEmpty ||
|
||||||
status.nonEmpty ||
|
status.nonEmpty ||
|
||||||
turns.nonEmpty ||
|
turns.nonEmpty ||
|
||||||
|
@ -44,8 +48,11 @@ case class Query(
|
||||||
|
|
||||||
def countRequest = CountRequest(matchAllQuery, filters)
|
def countRequest = CountRequest(matchAllQuery, filters)
|
||||||
|
|
||||||
|
def usernames = List(user1, user2).flatten
|
||||||
|
|
||||||
private def filters = List(
|
private def filters = List(
|
||||||
usernames map { u ⇒ termFilter(fields.uids, u.toLowerCase) },
|
usernames map { termFilter(fields.uids, _) },
|
||||||
|
toFilters(winner, fields.winner),
|
||||||
turns filters fields.turns,
|
turns filters fields.turns,
|
||||||
averageElo filters fields.averageElo,
|
averageElo filters fields.averageElo,
|
||||||
duration map (60 *) filters fields.duration,
|
duration map (60 *) filters fields.duration,
|
||||||
|
@ -108,25 +115,4 @@ object Query {
|
||||||
}
|
}
|
||||||
|
|
||||||
val statuses = Status.finishedNotCheated map { s ⇒ s.id -> s.name }
|
val statuses = Status.finishedNotCheated map { s ⇒ s.id -> s.name }
|
||||||
|
|
||||||
def test = Query(
|
|
||||||
usernames = List("thibault"),
|
|
||||||
duration = Range(1.some, 3.some),
|
|
||||||
sorting = Sorting(fields.averageElo, "desc")
|
|
||||||
)
|
|
||||||
def test2 = Query(
|
|
||||||
opening = "A04".some,
|
|
||||||
sorting = Sorting(fields.turns, "desc")
|
|
||||||
)
|
|
||||||
def test3 = Query(
|
|
||||||
usernames = List("controlaltdelete"),
|
|
||||||
variant = 1.some,
|
|
||||||
turns = Range(20.some, 100.some),
|
|
||||||
averageElo = Range(1100.some, 2000.some),
|
|
||||||
opening = "A00".some,
|
|
||||||
hasAi = true.some,
|
|
||||||
aiLevel = Range.none,
|
|
||||||
date = Range(Some(DateTime.now - 1.year), none),
|
|
||||||
sorting = Sorting(fields.date, "desc")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ object Sorting {
|
||||||
|
|
||||||
def fieldKeys = fields map (_._1)
|
def fieldKeys = fields map (_._1)
|
||||||
|
|
||||||
val orders = List(SortOrder.ASC, SortOrder.DESC) map { s ⇒ s.toString -> s.toString }
|
val orders = List(SortOrder.DESC, SortOrder.ASC) map { s ⇒ s.toString -> s.toString }
|
||||||
|
|
||||||
val default = Sorting(Game.fields.date, "desc")
|
val default = Sorting(Game.fields.date, "desc")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import play.api.templates.Html
|
||||||
|
|
||||||
trait AssetHelper {
|
trait AssetHelper {
|
||||||
|
|
||||||
val assetVersion = 71
|
val assetVersion = 73
|
||||||
|
|
||||||
def cssTag(name: String) = css("stylesheets/" + name)
|
def cssTag(name: String) = css("stylesheets/" + name)
|
||||||
|
|
||||||
|
|
|
@ -26,16 +26,22 @@ moreCss = moreCss) {
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<label for="@form("usernames").id">Players</label>
|
<label>Players</label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td class="usernames">
|
||||||
<input
|
<div class="half">@input(form("players")("a"))</div>
|
||||||
type="text"
|
<div class="half">vs @input(form("players")("b"))</div>
|
||||||
value="@form("usernames").value"
|
</td>
|
||||||
name="@form("usernames").name"
|
</tr>
|
||||||
id="@form("usernames").id" />
|
<tr class="winner none">
|
||||||
|
<th>
|
||||||
|
<label for="@form("players")("winner").id">Winner</label>
|
||||||
|
</th>
|
||||||
|
<td class="single">
|
||||||
|
<select id="@form("players")("winner").id" name="@form("players")("winner").name">
|
||||||
|
<option class="blank" value=""></option>
|
||||||
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<th class="help">Type up to two usernames</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
|
@ -55,7 +61,7 @@ moreCss = moreCss) {
|
||||||
@select(form("hasAi"), Query.hasAis, "Human or Computer".some)
|
@select(form("hasAi"), Query.hasAis, "Human or Computer".some)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="aiLevel">
|
<tr class="aiLevel none">
|
||||||
<th>
|
<th>
|
||||||
<label for="@form("aiLevel").id">Stockfish level</label>
|
<label for="@form("aiLevel").id">Stockfish level</label>
|
||||||
</th>
|
</th>
|
||||||
|
@ -128,8 +134,8 @@ moreCss = moreCss) {
|
||||||
<label>Sort</label>
|
<label>Sort</label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<div class="half">By @select(form("sortField"), Sorting.fields)</div>
|
<div class="half">By @select(form("sort")("field"), Sorting.fields)</div>
|
||||||
<div class="half">Order @select(form("sortOrder"), Sorting.orders)</div>
|
<div class="half">Order @select(form("sort")("order"), Sorting.orders)</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -137,7 +143,7 @@ moreCss = moreCss) {
|
||||||
<div class="search_result">
|
<div class="search_result">
|
||||||
@paginator.map { pager =>
|
@paginator.map { pager =>
|
||||||
@if(pager.nbResults > 0) {
|
@if(pager.nbResults > 0) {
|
||||||
<div class="nb_results">
|
<div class="search_status">
|
||||||
@paginator.map { pager =>
|
@paginator.map { pager =>
|
||||||
@pager.nbResults.localize games found
|
@pager.nbResults.localize games found
|
||||||
}
|
}
|
||||||
|
@ -151,9 +157,11 @@ moreCss = moreCss) {
|
||||||
@game.widgets(pager.currentPageResults)
|
@game.widgets(pager.currentPageResults)
|
||||||
</div>
|
</div>
|
||||||
} else {
|
} else {
|
||||||
<div class="no_result">No game found</div>
|
<div class="search_status">No game found</div>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}.getOrElse {
|
||||||
|
<div class="search_status">Search ready</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
@(field: play.api.data.Field)
|
||||||
|
|
||||||
|
<input type="text" value="@field.value" name="@field.name" id="@field.id" />
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<select id="@field.id" name="@field.name">
|
<select id="@field.id" name="@field.name">
|
||||||
@default.map { d =>
|
@default.map { d =>
|
||||||
<option class="blank" value=""></option>
|
<option value=""></option>
|
||||||
}
|
}
|
||||||
@options.map { v =>
|
@options.map { v =>
|
||||||
<option value="@v._1" @(if(field.value == Some(v._1.toString)) "selected" else "")>@v._2</option>
|
<option value="@v._1" @(if(field.value == Some(v._1.toString)) "selected" else "")>@v._2</option>
|
||||||
|
|
|
@ -38,7 +38,6 @@ object Main {
|
||||||
case "game-per-day" :: days :: Nil ⇒ games.perDay(parseIntOption(days) err "days: Int")
|
case "game-per-day" :: days :: Nil ⇒ games.perDay(parseIntOption(days) err "days: Int")
|
||||||
case "wiki-fetch" :: Nil ⇒ wiki.fetch
|
case "wiki-fetch" :: Nil ⇒ wiki.fetch
|
||||||
case "search-reset" :: Nil ⇒ search.reset
|
case "search-reset" :: Nil ⇒ search.reset
|
||||||
case "search-test" :: Nil ⇒ search.test
|
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
putStrLn("Unknown command: " + args.mkString(" "))
|
putStrLn("Unknown command: " + args.mkString(" "))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,6 @@ case class Search(env: SearchEnv) {
|
||||||
|
|
||||||
def reset: IO[Unit] = env.indexer.rebuildAll
|
def reset: IO[Unit] = env.indexer.rebuildAll
|
||||||
|
|
||||||
def test: IO[Unit] = env.indexer toGames {
|
|
||||||
env.indexer search Query.test.searchRequest(0, 10)
|
|
||||||
} flatMap { games ⇒
|
|
||||||
putStrLn(games map showGame mkString "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def showGame(game: lila.game.DbGame) =
|
private def showGame(game: lila.game.DbGame) =
|
||||||
game.id + " " + game.turns //+ " " + game
|
game.id + " " + game.turns //+ " " + game
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
var $form = $("form.search");
|
var $form = $("form.search");
|
||||||
|
var $usernames = $form.find(".usernames input");
|
||||||
|
var $winnerRow = $form.find(".winner");
|
||||||
|
var $winner = $winnerRow.find("select");
|
||||||
var $result = $(".search_result");
|
var $result = $(".search_result");
|
||||||
|
|
||||||
function realtimeResults() {
|
function realtimeResults() {
|
||||||
|
$("div.search_status").text("Searching...");
|
||||||
$result.load(
|
$result.load(
|
||||||
$form.attr("action") + "?" + $form.serialize() + " .search_result",
|
$form.attr("action") + "?" + $form.serialize() + " .search_result",
|
||||||
function() {
|
function() {
|
||||||
|
@ -11,27 +15,76 @@ $(function() {
|
||||||
$result.find('.search_infinitescroll:has(.pager a)').each(function() {
|
$result.find('.search_infinitescroll:has(.pager a)').each(function() {
|
||||||
var $next = $(this).find(".pager a:last")
|
var $next = $(this).find(".pager a:last")
|
||||||
$next.attr("href", $next.attr("href") + "&" + $form.serialize());
|
$next.attr("href", $next.attr("href") + "&" + $form.serialize());
|
||||||
$(this).infinitescroll({
|
$(this).infinitescroll({
|
||||||
navSelector: ".pager",
|
navSelector: ".pager",
|
||||||
nextSelector: $next,
|
nextSelector: $next,
|
||||||
itemSelector: ".search_infinitescroll .paginated_element",
|
itemSelector: ".search_infinitescroll .paginated_element",
|
||||||
loading: {
|
loading: {
|
||||||
msgText: "",
|
msgText: "",
|
||||||
img: "/assets/images/hloader3.gif",
|
img: "/assets/images/hloader3.gif",
|
||||||
finishedMsg: "---"
|
finishedMsg: "---"
|
||||||
}
|
}
|
||||||
}, function() {
|
}, function() {
|
||||||
$("#infscr-loading").remove();
|
$("#infscr-loading").remove();
|
||||||
$('body').trigger('lichess.content_loaded');
|
$('body').trigger('lichess.content_loaded');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function winnerChoices() {
|
||||||
|
var options = ["<option value=''></option>"];
|
||||||
|
$usernames.each(function() {
|
||||||
|
var user = $.trim($(this).val());
|
||||||
|
if (user.length > 1) {
|
||||||
|
options.push("<option value='"+user+"'>"+user+"</option>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$winner.html(options.join(""));
|
||||||
|
$winnerRow.toggle(options.length > 1);
|
||||||
|
}
|
||||||
|
|
||||||
$form.find("select").change(realtimeResults);
|
$form.find("select").change(realtimeResults);
|
||||||
$form.find("input").change(realtimeResults);
|
$usernames.bind("keyup", winnerChoices).trigger("keyup");
|
||||||
|
$usernames.bindWithDelay("keyup", realtimeResults, 400);
|
||||||
|
|
||||||
$form.find(".opponent select").change(function() {
|
$form.find(".opponent select").change(function() {
|
||||||
$form.find(".aiLevel").toggle($(this).val() == 1);
|
$form.find(".aiLevel").toggle($(this).val() == 1);
|
||||||
}).trigger("change");
|
}).trigger("change");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js
|
||||||
|
$.fn.bindWithDelay = function( type, data, fn, timeout, throttle ) {
|
||||||
|
|
||||||
|
if ( $.isFunction( data ) ) {
|
||||||
|
throttle = timeout;
|
||||||
|
timeout = fn;
|
||||||
|
fn = data;
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow delayed function to be removed with fn in unbind function
|
||||||
|
fn.guid = fn.guid || ($.guid && $.guid++);
|
||||||
|
|
||||||
|
// Bind each separately so that each element has its own delay
|
||||||
|
return this.each(function() {
|
||||||
|
|
||||||
|
var wait = null;
|
||||||
|
|
||||||
|
function cb() {
|
||||||
|
var e = $.extend(true, { }, arguments[0]);
|
||||||
|
var ctx = this;
|
||||||
|
var throttler = function() {
|
||||||
|
wait = null;
|
||||||
|
fn.apply(ctx, [e]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!throttle) { clearTimeout(wait); wait = null; }
|
||||||
|
if (!wait) { wait = setTimeout(throttler, timeout); }
|
||||||
|
}
|
||||||
|
|
||||||
|
cb.guid = fn.guid;
|
||||||
|
|
||||||
|
$(this).bind(type, data, cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -2,14 +2,6 @@ form.search {
|
||||||
padding: 10px 25px;
|
padding: 10px 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.nb_results {
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 10px 25px;
|
|
||||||
background: #f4f4f4;
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.search td {
|
form.search td {
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +31,8 @@ form.search .single select {
|
||||||
width: 99%;
|
width: 99%;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.search .half select {
|
form.search .half select,
|
||||||
|
form.search .half input {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,13 +48,16 @@ form.search th.help {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.no_result {
|
div.search_status {
|
||||||
padding: 10px;
|
margin-top: 10px;
|
||||||
font-size: 1.3em;
|
padding: 10px 25px;
|
||||||
font-style: italic;
|
background: #f4f4f4;
|
||||||
text-align: center
|
border-top: 1px solid #e4e4e4;
|
||||||
|
border-bottom: 1px solid #e4e4e4;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Errors */
|
/* Errors */
|
||||||
form.search .error {
|
form.search .error {
|
||||||
margin-left: 160px;
|
margin-left: 160px;
|
||||||
|
|
Loading…
Reference in New Issue