show user timeline on homepage
parent
b6a09ab149
commit
f691230d2c
|
@ -1,3 +1,6 @@
|
|||
[submodule "scalachess"]
|
||||
path = modules/chess
|
||||
url = git://github.com/ornicar/scalachess.git
|
||||
[submodule "public/vendor/timeago"]
|
||||
path = public/vendor/timeago
|
||||
url = https://github.com/rmm5t/jquery-timeago
|
||||
|
|
|
@ -3,17 +3,26 @@ package lila.app
|
|||
import akka.actor._
|
||||
import com.typesafe.config.Config
|
||||
|
||||
final class Env(config: Config, system: ActorSystem) {
|
||||
final class Env(
|
||||
config: Config,
|
||||
system: ActorSystem,
|
||||
appPath: String) {
|
||||
|
||||
val CliUsername = config getString "cli.username"
|
||||
|
||||
private val RendererName = config getString "app.renderer.name"
|
||||
private val RouterName = config getString "app.router.name"
|
||||
private val WebPath = config getString "app.web_path"
|
||||
private val TimeagoLocalesPath = config getString "app.timeago_locales_path"
|
||||
|
||||
def timeagoLocalesPath = appPath + "/" + TimeagoLocalesPath
|
||||
|
||||
lazy val preloader = new mashup.Preload(
|
||||
lobby = Env.lobby.lobby,
|
||||
history = Env.lobby.history,
|
||||
featured = Env.game.featured)
|
||||
featured = Env.game.featured,
|
||||
recentGames = () ⇒ Env.timeline.getter.recentGames,
|
||||
timelineEntries = Env.timeline.getter.userEntries _)
|
||||
|
||||
lazy val userInfo = mashup.UserInfo(
|
||||
countUsers = () ⇒ Env.user.countEnabled,
|
||||
|
@ -55,7 +64,8 @@ object Env {
|
|||
|
||||
lazy val current = "[boot] app" describes new Env(
|
||||
config = lila.common.PlayApp.loadConfig,
|
||||
system = lila.common.PlayApp.system)
|
||||
system = lila.common.PlayApp.system,
|
||||
appPath = lila.common.PlayApp withApp (_.path.getCanonicalPath))
|
||||
|
||||
def api = lila.api.Env.current
|
||||
def db = lila.db.Env.current
|
||||
|
|
|
@ -23,9 +23,6 @@ private[app] final class Renderer extends Actor {
|
|||
sender ! V.tournament.createdTable(tours)
|
||||
|
||||
case entry: lila.timeline.GameEntry =>
|
||||
sender ! V.lobby.gameTimelineEntry(entry)
|
||||
|
||||
case entry: lila.timeline.Entry =>
|
||||
sender ! Html(entry.toString) // V.lobby.timelineEntry(entry)
|
||||
sender ! V.timeline.gameEntry(entry)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,13 +26,12 @@ object Lobby extends LilaController with Results {
|
|||
|
||||
private def renderHome[A](status: Status)(implicit ctx: Context): Fu[Result] =
|
||||
Env.current.preloader(
|
||||
timeline = Env.timeline.recentGames,
|
||||
posts = Env.forum.recent(ctx.me, Env.team.cached.teamIds.apply),
|
||||
tours = TournamentRepo.created,
|
||||
filter = Env.setup.filter
|
||||
).map(_.fold(Redirect(_), {
|
||||
case (preload, entries, posts, tours, featured) ⇒ status(html.lobby.home(
|
||||
Json stringify preload, entries, posts, tours, featured)) |> { response ⇒
|
||||
case (preload, entries, gameEntries, posts, tours, featured) ⇒ status(html.lobby.home(
|
||||
Json stringify preload, entries, gameEntries, posts, tours, featured)) |> { response ⇒
|
||||
ctx.req.session.data.contains(LilaCookie.sessionId).fold(
|
||||
response,
|
||||
response withCookies LilaCookie.makeSessionId(ctx.req)
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
package lila.app
|
||||
package mashup
|
||||
|
||||
import lila.lobby.{ Hook, HookRepo }
|
||||
import lila.lobby.actorApi.lobby._
|
||||
import lila.timeline.GameEntry
|
||||
import lila.game.{ Game, GameRepo, Featured }
|
||||
import lila.forum.PostLiteView
|
||||
import lila.socket.History
|
||||
import lila.tournament.Created
|
||||
import lila.setup.FilterConfig
|
||||
import lila.user.{ User, Context }
|
||||
import controllers.routes
|
||||
import makeTimeout.large
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.pattern.ask
|
||||
import play.api.mvc.Call
|
||||
import play.api.libs.json.{ Json, JsObject, JsArray }
|
||||
import play.api.mvc.Call
|
||||
|
||||
import controllers.routes
|
||||
import lila.forum.PostLiteView
|
||||
import lila.game.{ Game, GameRepo, Featured }
|
||||
import lila.lobby.actorApi.lobby._
|
||||
import lila.lobby.{ Hook, HookRepo }
|
||||
import lila.setup.FilterConfig
|
||||
import lila.socket.History
|
||||
import lila.timeline.{ Entry, GameEntry }
|
||||
import lila.tournament.Created
|
||||
import lila.user.{ User, Context }
|
||||
import makeTimeout.large
|
||||
|
||||
final class Preload(
|
||||
lobby: ActorRef,
|
||||
history: History,
|
||||
featured: Featured) {
|
||||
featured: Featured,
|
||||
recentGames: () ⇒ Fu[List[GameEntry]],
|
||||
timelineEntries: String ⇒ Fu[List[Entry]]) {
|
||||
|
||||
private type RightResponse = (JsObject, List[GameEntry], List[PostLiteView], List[Created], Option[Game])
|
||||
private type RightResponse = (JsObject, List[Entry], List[GameEntry], List[PostLiteView], List[Created], Option[Game])
|
||||
private type Response = Either[Call, RightResponse]
|
||||
|
||||
def apply(
|
||||
timeline: Fu[List[GameEntry]],
|
||||
posts: Fu[List[PostLiteView]],
|
||||
tours: Fu[List[Created]],
|
||||
filter: Fu[FilterConfig])(implicit ctx: Context): Fu[Response] =
|
||||
ctx.isAuth.fold(lobby ? GetOpen, lobby ? GetOpenCasual).mapTo[List[Hook]] zip
|
||||
timeline zip
|
||||
posts zip
|
||||
tours zip
|
||||
featured.one zip
|
||||
filter map {
|
||||
case (((((hooks, entries), posts), tours), feat), filter) ⇒
|
||||
recentGames() zip
|
||||
posts zip
|
||||
tours zip
|
||||
featured.one zip
|
||||
(ctx.userId ?? timelineEntries) zip
|
||||
filter map {
|
||||
case ((((((hooks, gameEntries), posts), tours), feat), entries), filter) ⇒
|
||||
(Right((Json.obj(
|
||||
"version" -> history.version,
|
||||
"pool" -> JsArray(hooks map (_.render)),
|
||||
"filter" -> filter.render
|
||||
), entries, posts, tours, feat)))
|
||||
), entries, gameEntries, posts, tours, feat)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
package lila.app
|
||||
package templating
|
||||
|
||||
import lila.user.Context
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.{ DateTimeFormat, DateTimeFormatter }
|
||||
import java.util.Locale
|
||||
import scala.collection.mutable
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format._
|
||||
import play.api.templates.Html
|
||||
|
||||
import lila.user.Context
|
||||
|
||||
trait DateHelper { self: I18nHelper ⇒
|
||||
|
||||
private val style = "MS"
|
||||
|
||||
private val formatters = mutable.Map[String, DateTimeFormatter]()
|
||||
|
||||
private val isoFormatter = ISODateTimeFormat.dateTime
|
||||
|
||||
private def formatter(ctx: Context): DateTimeFormatter =
|
||||
formatters.getOrElseUpdate(
|
||||
lang(ctx).language,
|
||||
|
@ -21,4 +25,25 @@ trait DateHelper { self: I18nHelper ⇒
|
|||
|
||||
def showDate(date: DateTime)(implicit ctx: Context): String =
|
||||
formatter(ctx) print date
|
||||
|
||||
def timeago(date: DateTime)(implicit ctx: Context): Html = Html(
|
||||
"""<time class="timeago" datetime="%s">%s</time>"""
|
||||
.format(isoFormatter print date, showDate(date))
|
||||
)
|
||||
|
||||
def timeagoLocale(implicit ctx: Context): Option[String] =
|
||||
lang(ctx).language match {
|
||||
case "en" ⇒ none
|
||||
case "pt" ⇒ "pt-br".some
|
||||
case "zh" ⇒ "zh-CN".some
|
||||
case l ⇒ timeagoLocales(l) option l
|
||||
}
|
||||
|
||||
private lazy val timeagoLocales: Set[String] = {
|
||||
import java.io.File
|
||||
val Regex = """^jquery\.timeago\.(\w{2})\.js$""".r
|
||||
(new File(Env.current.timeagoLocalesPath).listFiles map (_.getName) collect {
|
||||
case Regex(l) ⇒ l
|
||||
}).toSet: Set[String]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@ package templating
|
|||
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.regex.Matcher.quoteReplacement
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils.escapeXml
|
||||
import play.api.templates.Html
|
||||
import java.util.regex.Matcher.quoteReplacement
|
||||
|
||||
trait StringHelper {
|
||||
|
||||
|
|
|
@ -141,5 +141,8 @@ moreJs: Html = Html(""))(body: Html)(implicit ctx: Context)
|
|||
@if(lang.language != "en") {
|
||||
<script src="@routes.Assets.at("trans/" + lang.language + ".js")?v=@assetVersion"></script>
|
||||
}
|
||||
@timeagoLocale.map { l =>
|
||||
<script src="@routes.Assets.at("vendor/timeago/locales/jquery.timeago." + l + ".js")"></script>
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(preload: String, gameTimeline: List[lila.timeline.GameEntry], forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created], featured: Option[Game])(implicit ctx: Context)
|
||||
@(preload: String, userTimeline: List[lila.timeline.Entry], gameTimeline: List[lila.timeline.GameEntry], forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created], featured: Option[Game])(implicit ctx: Context)
|
||||
|
||||
@underchat = {
|
||||
<div id="featured_game">
|
||||
|
@ -13,6 +13,11 @@
|
|||
}
|
||||
|
||||
@goodies = {
|
||||
<div id="timeline">
|
||||
@userTimeline.map { entry =>
|
||||
@timeline.entry(entry)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@base.layout(
|
||||
|
@ -44,39 +49,7 @@ underchat = underchat.some) {
|
|||
</div>
|
||||
</div>
|
||||
@lobby.buttons()
|
||||
<div class="open_tournaments undertable">
|
||||
<div class="undertable_top">
|
||||
<a class="more" title="See all tournaments" href="@routes.Tournament.home()">More »</a>
|
||||
<span class="title">Open tournaments</span>
|
||||
</div>
|
||||
<div class="undertable_inner">
|
||||
<table class="tournaments">
|
||||
@tournament.createdTable(tours)
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lichess_bot undertable">
|
||||
<div class="undertable_top">
|
||||
<a class="more" title="@trans.seeTheGamesBeingPlayedInRealTime()" href="@routes.Game.realtime()">@trans.games() »</a>
|
||||
<span class="title">@trans.gamesBeingPlayedRightNow()</span>
|
||||
</div>
|
||||
<div class="undertable_inner">
|
||||
<div class="content">
|
||||
<table class="lichess_messages">@gameTimelineEntries(gameTimeline)</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new_posts undertable" data-url="@routes.ForumPost.recent">
|
||||
<div class="undertable_top">
|
||||
<a class="more" title="@trans.talkAboutChessAndDiscussLichessFeaturesInTheForum()" href="@routes.ForumCateg.index">@trans.forum() »</a>
|
||||
<span class="title">@trans.forum()</span>
|
||||
</div>
|
||||
<div class="undertable_inner">
|
||||
<div class="content">
|
||||
<ol>@forum.post.recent(forumRecent)</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@lobby.undertable(gameTimeline, forumRecent, tours)
|
||||
</div>
|
||||
@embedJs("var lichess_preload = " + preload)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
@(gameTimeline: List[lila.timeline.GameEntry], forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created])(implicit ctx: Context)
|
||||
|
||||
<div class="open_tournaments undertable">
|
||||
<div class="undertable_top">
|
||||
<a class="more" title="See all tournaments" href="@routes.Tournament.home()">More »</a>
|
||||
<span class="title">Open tournaments</span>
|
||||
</div>
|
||||
<div class="undertable_inner">
|
||||
<table class="tournaments">
|
||||
@tournament.createdTable(tours)
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lichess_bot undertable">
|
||||
<div class="undertable_top">
|
||||
<a class="more" title="@trans.seeTheGamesBeingPlayedInRealTime()" href="@routes.Game.realtime()">@trans.games() »</a>
|
||||
<span class="title">@trans.gamesBeingPlayedRightNow()</span>
|
||||
</div>
|
||||
<div class="undertable_inner">
|
||||
<div class="content">
|
||||
<table>@timeline.gameEntries(gameTimeline)</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new_posts undertable" data-url="@routes.ForumPost.recent">
|
||||
<div class="undertable_top">
|
||||
<a class="more" title="@trans.talkAboutChessAndDiscussLichessFeaturesInTheForum()" href="@routes.ForumCateg.index">@trans.forum() »</a>
|
||||
<span class="title">@trans.forum()</span>
|
||||
</div>
|
||||
<div class="undertable_inner">
|
||||
<div class="content">
|
||||
<ol>@forum.post.recent(forumRecent)</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
@(e: lila.timeline.Entry)(implicit ctx: Context)
|
||||
|
||||
@import lila.timeline.Entry._
|
||||
@e.decode.map { decoded =>
|
||||
<div class="entry">
|
||||
@decoded match {
|
||||
case Follow(userId) => {
|
||||
@timeline.follow(userId) @timeago(e.date)
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
@(userId: String)(implicit ctx: Context)
|
||||
|
||||
@userIdLink(userId.some) @trans.followsYou()
|
|
@ -1,5 +1,5 @@
|
|||
@(entries: List[lila.timeline.GameEntry])
|
||||
|
||||
@entries.map { e =>
|
||||
<tr>@gameTimelineEntry(e)</tr>
|
||||
<tr>@timeline.gameEntry(e)</tr>
|
||||
}
|
|
@ -17,12 +17,12 @@ object PlayApp {
|
|||
}
|
||||
|
||||
private def enableScheduler = loadConfig getBoolean "app.scheduler.enabled"
|
||||
|
||||
def scheduler = new Scheduler(system, enabled = enableScheduler && isServer)
|
||||
|
||||
def isDev = isMode(_.Dev)
|
||||
def isTest = isMode(_.Test)
|
||||
def isProd = isMode(_.Prod)
|
||||
// def isServer = !(isDev || isTest)
|
||||
def isServer = !isTest
|
||||
def isMode(f: Mode.type ⇒ Mode.Mode) = withApp { _.mode == f(Mode) }
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ package timeline {
|
|||
def apply[A: Writes](user: String, typ: MakeEntry.type ⇒ String, data: A): MakeEntry =
|
||||
MakeEntry(user, typ(MakeEntry), Json toJson data)
|
||||
}
|
||||
case class EntryView(user: String, rendered: String)
|
||||
case class ReloadTimeline(user: String)
|
||||
case class GameEntryView(rendered: String)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
package lila.lobby
|
||||
|
||||
import actorApi._
|
||||
import lila.socket.{ SocketActor, History, Historical }
|
||||
import lila.socket.actorApi.{ Connected ⇒ _, _ }
|
||||
import lila.game.actorApi._
|
||||
import lila.hub.actorApi.lobby._
|
||||
import lila.hub.actorApi.timeline._
|
||||
import lila.hub.actorApi.router.{ Homepage, Player }
|
||||
import makeTimeout.short
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import actorApi._
|
||||
import akka.actor._
|
||||
import akka.pattern.ask
|
||||
import play.api.libs.json._
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.json._
|
||||
import play.api.templates.Html
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.game.actorApi._
|
||||
import lila.hub.actorApi.lobby._
|
||||
import lila.hub.actorApi.router.{ Homepage, Player }
|
||||
import lila.hub.actorApi.timeline._
|
||||
import lila.socket.actorApi.{ Connected ⇒ _, _ }
|
||||
import lila.socket.{ SocketActor, History, Historical }
|
||||
import makeTimeout.short
|
||||
|
||||
private[lobby] final class Socket(
|
||||
val history: History,
|
||||
|
@ -37,15 +38,15 @@ private[lobby] final class Socket(
|
|||
sender ! Connected(enumerator, member)
|
||||
}
|
||||
|
||||
case ReloadTournaments(html) ⇒ notifyTournaments(html)
|
||||
case ReloadTournaments(html) ⇒ notifyTournaments(html)
|
||||
|
||||
case GameEntryView(rendered) ⇒ notifyVersion("game_entry", rendered)
|
||||
case GameEntryView(rendered) ⇒ notifyVersion("game_entry", rendered)
|
||||
|
||||
case EntryView(user, rendered) ⇒ sendTo(user, makeMessage("entry", rendered))
|
||||
case ReloadTimeline(user) ⇒ sendTo(user, makeMessage("reload_timeline", JsNull))
|
||||
|
||||
case AddHook(hook) ⇒ notifyVersion("hook_add", hook.render)
|
||||
case AddHook(hook) ⇒ notifyVersion("hook_add", hook.render)
|
||||
|
||||
case RemoveHook(hookId) ⇒ notifyVersion("hook_remove", hookId)
|
||||
case RemoveHook(hookId) ⇒ notifyVersion("hook_remove", hookId)
|
||||
|
||||
case JoinHook(uid, hook, game) ⇒
|
||||
playerUrl(game fullIdOf game.creatorColor) zip
|
||||
|
|
|
@ -3,6 +3,8 @@ package lila.timeline
|
|||
import org.joda.time.DateTime
|
||||
import play.api.libs.json._
|
||||
|
||||
import lila.common.PimpedJson._
|
||||
|
||||
case class Entry(
|
||||
user: String,
|
||||
typ: String,
|
||||
|
@ -13,11 +15,20 @@ case class Entry(
|
|||
(user == other.user) &&
|
||||
(typ == other.typ) &&
|
||||
(data == other.data)
|
||||
|
||||
def decode: Option[Entry.Decoded] = typ match {
|
||||
case "follow" ⇒ data str "user" map { Entry.Follow(_) }
|
||||
case _ ⇒ none
|
||||
}
|
||||
}
|
||||
|
||||
object Entry {
|
||||
|
||||
def make(user: String, typ: String, data: JsValue): Option[Entry] =
|
||||
sealed trait Decoded
|
||||
|
||||
case class Follow(userId: String) extends Decoded
|
||||
|
||||
private[timeline] def make(user: String, typ: String, data: JsValue): Option[Entry] =
|
||||
data.asOpt[JsObject] map { Entry(user, typ, _, DateTime.now) }
|
||||
|
||||
import lila.db.Tube
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package lila.timeline
|
||||
|
||||
import tube.gameEntryTube
|
||||
import lila.db.api._
|
||||
import lila.db.Implicits._
|
||||
|
||||
import akka.actor._
|
||||
import com.typesafe.config.Config
|
||||
|
||||
|
@ -22,8 +18,9 @@ final class Env(
|
|||
private val UserDisplayMax = config getInt "user.display_max"
|
||||
private val UserActorName = config getString "user.actor.name"
|
||||
|
||||
def recentGames: Fu[List[GameEntry]] =
|
||||
$query[GameEntry]($select.all) sort $sort.naturalOrder toListFlatten GameDisplayMax.some
|
||||
lazy val getter = new Getter(
|
||||
gameMax = GameDisplayMax,
|
||||
userMax = UserDisplayMax)
|
||||
|
||||
system.actorOf(Props(new GamePush(
|
||||
lobbySocket = lobbySocket,
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package lila.timeline
|
||||
|
||||
import play.api.libs.json.Json
|
||||
|
||||
import lila.db.api._
|
||||
import lila.db.Implicits._
|
||||
import tube.{ entryTube, gameEntryTube }
|
||||
|
||||
private[timeline] final class Getter(
|
||||
gameMax: Int,
|
||||
userMax: Int) {
|
||||
|
||||
def recentGames: Fu[List[GameEntry]] =
|
||||
$find[GameEntry](
|
||||
$query[GameEntry]($select.all) sort $sort.naturalOrder,
|
||||
gameMax)
|
||||
|
||||
def userEntries(userId: String): Fu[List[Entry]] =
|
||||
$find[Entry](
|
||||
$query[Entry](Json.obj("user" -> userId)) sort $sort.desc("date"),
|
||||
userMax)
|
||||
}
|
|
@ -18,9 +18,7 @@ private[timeline] final class Push(
|
|||
|
||||
def receive = {
|
||||
case maker @ MakeEntry(user, typ, data) ⇒ makeEntry(user, typ, data) foreach { entry ⇒
|
||||
renderer ? entry map {
|
||||
case view: Html ⇒ EntryView(user, view.body)
|
||||
} pipeTo lobbySocket.ref
|
||||
lobbySocket.ref ! ReloadTimeline(user)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -341,6 +341,12 @@ var lichess_sri = Math.random().toString(36).substring(5); // 8 chars
|
|||
setTimeout(userPowertips, 600);
|
||||
$('body').on('lichess.content_loaded', userPowertips);
|
||||
|
||||
function setTimeAgo() {
|
||||
$("time:not(.jsed)").addClass('.jsed').timeago();
|
||||
}
|
||||
setTimeAgo();
|
||||
$('body').on('lichess.content_loaded', setTimeAgo);
|
||||
|
||||
// Start game
|
||||
var $game = $('div.lichess_game').orNot();
|
||||
if ($game) $game.game(_ld_);
|
||||
|
@ -1836,8 +1842,9 @@ var lichess_sri = Math.random().toString(36).substring(5); // 8 chars
|
|||
game_entry: function(e) {
|
||||
renderTimeline([e]);
|
||||
},
|
||||
entry: function(e) {
|
||||
console.debug(e);
|
||||
reload_timeline: function() {
|
||||
// TODO
|
||||
console.debug("reload timeline");
|
||||
},
|
||||
hook_add: addHook,
|
||||
hook_remove: removeHook,
|
||||
|
|
|
@ -19,3 +19,6 @@ if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).
|
|||
|
||||
// jQuery.cookie
|
||||
(function(e,h,j){function k(b){return b}function l(b){return decodeURIComponent(b.replace(m," "))}var m=/\+/g,d=e.cookie=function(b,c,a){if(c!==j){a=e.extend({},d.defaults,a);null===c&&(a.expires=-1);if("number"===typeof a.expires){var f=a.expires,g=a.expires=new Date;g.setDate(g.getDate()+f)}c=d.json?JSON.stringify(c):String(c);return h.cookie=[encodeURIComponent(b),"=",d.raw?c:encodeURIComponent(c),a.expires?"; expires="+a.expires.toUTCString():"",a.path?"; path="+a.path:"",a.domain?"; domain="+ a.domain:"",a.secure?"; secure":""].join("")}c=d.raw?k:l;a=h.cookie.split("; ");for(f=0;g=a[f]&&a[f].split("=");f++)if(c(g.shift())===b)return b=c(g.join("=")),d.json?JSON.parse(b):b;return null};d.defaults={};e.removeCookie=function(b,c){return null!==e.cookie(b)?(e.cookie(b,null,c),!0):!1}})(jQuery,document);
|
||||
|
||||
// jquery.timeago 1.2.0 https://github.com/rmm5t/jquery-timeago
|
||||
(function(d){"function"===typeof define&&define.amd?define(["jquery"],d):d(jQuery)})(function(d){function l(){var a;a=d(this);if(!a.data("timeago")){a.data("timeago",{datetime:e.datetime(a)});var b=d.trim(a.text());e.settings.localeTitle?a.attr("title",a.data("timeago").datetime.toLocaleString()):0<b.length&&(!e.isTime(a)||!a.attr("title"))&&a.attr("title",b)}a=a.data("timeago");b=e.settings;isNaN(a.datetime)||(0==b.cutoff||(new Date).getTime()-a.datetime.getTime()<b.cutoff)&&d(this).text(f(a.datetime));return this}function f(a){return e.inWords((new Date).getTime()-a.getTime())}d.timeago=function(a){return a instanceof Date?f(a):"string"===typeof a?f(d.timeago.parse(a)):"number"===typeof a?f(new Date(a)):f(d.timeago.datetime(a))};var e=d.timeago;d.extend(d.timeago,{settings:{refreshMillis:6E4,allowFuture:!1,localeTitle:!1,cutoff:0,strings:{prefixAgo:null,prefixFromNow:null,suffixAgo:"ago",suffixFromNow:"from now",seconds:"less than a minute",minute:"about a minute",minutes:"%d minutes",hour:"about an hour",hours:"about %d hours",day:"a day",days:"%d days",month:"about a month",months:"%d months",year:"about a year",years:"%d years",wordSeparator:" ",numbers:[]}},inWords:function(a){function b(b,e){return(d.isFunction(b)?b(e,a):b).replace(/%d/i,c.numbers&&c.numbers[e]||e)}var c=this.settings.strings,e=c.prefixAgo,f=c.suffixAgo;this.settings.allowFuture&&0>a&&(e=c.prefixFromNow,f=c.suffixFromNow);var h=Math.abs(a)/1E3,g=h/60,m=g/60,k=m/24,l=k/365,h=45>h&&b(c.seconds,Math.round(h))||90>h&&b(c.minute,1)||45>g&&b(c.minutes,Math.round(g))||90>g&&b(c.hour,1)||24>m&&b(c.hours,Math.round(m))||42>m&&b(c.day,1)||30>k&&b(c.days,Math.round(k))||45>k&&b(c.month,1)||365>k&&b(c.months,Math.round(k/30))||1.5>l&&b(c.year,1)||b(c.years,Math.round(l)),g=c.wordSeparator||"";void 0===c.wordSeparator&&(g=" ");return d.trim([e,h,f].join(g))},parse:function(a){a=d.trim(a);a=a.replace(/\.\d+/,"");a=a.replace(/-/,"/").replace(/-/,"/");a=a.replace(/T/," ").replace(/Z/," UTC");a=a.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2");return new Date(a)},datetime:function(a){a=e.isTime(a)?d(a).attr("datetime"):d(a).attr("title");return e.parse(a)},isTime:function(a){return"time"===d(a).get(0).tagName.toLowerCase()}});var n={init:function(){var a=d.proxy(l,this);a();var b=e.settings;0<b.refreshMillis&&setInterval(a,b.refreshMillis)},update:function(a){d(this).data("timeago",{datetime:e.parse(a)});l.apply(this)}};d.fn.timeago=function(a,b){var c=a?n[a]:n.init;if(!c)throw Error("Unknown function name '"+a+"' for timeago");this.each(function(){c.call(this,b)});return this};document.createElement("abbr");document.createElement("time")});
|
||||
|
|
|
@ -515,9 +515,6 @@ div.lichess_chat {
|
|||
div.lichess_chat.small_chat {
|
||||
left: 0px;
|
||||
}
|
||||
div.lichess_chat.lobby_chat {
|
||||
top: 126px;
|
||||
}
|
||||
div.lichess_chat_top {
|
||||
border-radius: 4px 4px 0 0;
|
||||
border: 1px solid #ccc;
|
||||
|
@ -552,9 +549,6 @@ div.lichess_chat:hover .lichess_messages {
|
|||
div.lichess_chat.small_chat .lichess_messages {
|
||||
width: 186px;
|
||||
}
|
||||
div.lichess_chat.lobby_chat .lichess_messages {
|
||||
height: 341px;
|
||||
}
|
||||
|
||||
div.lichess_chat form {
|
||||
position: relative;
|
||||
|
|
|
@ -825,6 +825,18 @@ div.game_extra div.bookmarkers {
|
|||
float: right;
|
||||
max-width: 48%;
|
||||
}
|
||||
#timeline {
|
||||
margin-top: 2em;
|
||||
}
|
||||
#timeline > .entry {
|
||||
padding-bottom: 1em;
|
||||
border-bottom: 1px solid #c0c0c0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
#timeline time {
|
||||
font-size: 0.8em;
|
||||
color: #afafaf;
|
||||
}
|
||||
span.bookmark {
|
||||
position: relative;
|
||||
float: right;
|
||||
|
|
|
@ -134,7 +134,8 @@ body.dark div.lichess_chat a.user_link,
|
|||
body.dark div.lichess_chat a.user_link,
|
||||
body.dark div.new_posts li span,
|
||||
body.dark #team .forum a.user_link,
|
||||
body.dark span.board_mark
|
||||
body.dark span.board_mark,
|
||||
body.dark #timeline time
|
||||
{
|
||||
color: #808080;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d52af890182a0779598d7001834a3708a0ea5746
|
Loading…
Reference in New Issue