ui/site refactor WIP
parent
1c05a29dcc
commit
bf9aff4c43
|
@ -26,7 +26,7 @@ object userAnalysis {
|
|||
moreJs = frag(
|
||||
analyseTag,
|
||||
analyseNvuiTag,
|
||||
embedJsUnsafe(s"""lichess=lichess||{};lichess.user_analysis=${safeJsonValue(
|
||||
embedJsUnsafe(s"""lichess.userAnalysis=${safeJsonValue(
|
||||
Json.obj(
|
||||
"data" -> data,
|
||||
"i18n" -> userAnalysisI18n(withForecast = withForecast),
|
||||
|
|
|
@ -20,7 +20,7 @@ object show {
|
|||
moreJs = frag(
|
||||
analyseTag,
|
||||
analyseNvuiTag,
|
||||
embedJsUnsafe(s"""lichess=window.lichess||{};lichess.practice=${safeJsonValue(
|
||||
embedJsUnsafe(s"""lichess.practice=${safeJsonValue(
|
||||
Json.obj(
|
||||
"practice" -> data.practice,
|
||||
"study" -> data.study,
|
||||
|
|
|
@ -18,9 +18,7 @@ object show {
|
|||
moreJs = frag(
|
||||
jsTag("vendor/sparkline.min.js"),
|
||||
jsModule("puzzle"),
|
||||
embedJsUnsafe(s"""
|
||||
lichess = lichess || {};
|
||||
lichess.puzzle = ${safeJsonValue(
|
||||
embedJsUnsafe(s"""lichess.puzzle=${safeJsonValue(
|
||||
Json.obj(
|
||||
"data" -> data,
|
||||
"pref" -> pref,
|
||||
|
|
|
@ -26,7 +26,7 @@ object show {
|
|||
moreJs = frag(
|
||||
analyseTag,
|
||||
analyseNvuiTag,
|
||||
embedJsUnsafe(s"""lichess=window.lichess||{};lichess.relay=${safeJsonValue(
|
||||
embedJsUnsafe(s"""lichess.relay=${safeJsonValue(
|
||||
Json.obj(
|
||||
"relay" -> data.relay,
|
||||
"study" -> data.study.add("admin" -> isGranted(_.StudyAdmin)),
|
||||
|
|
|
@ -24,7 +24,7 @@ object show {
|
|||
moreJs = frag(
|
||||
analyseTag,
|
||||
analyseNvuiTag,
|
||||
embedJsUnsafe(s"""lichess=window.lichess||{};lichess.study=${safeJsonValue(
|
||||
embedJsUnsafe(s"""lichess.study=${safeJsonValue(
|
||||
Json.obj(
|
||||
"study" -> data.study.add("admin" -> isGranted(_.StudyAdmin)),
|
||||
"data" -> data.analysis,
|
||||
|
|
|
@ -26,7 +26,7 @@ object show {
|
|||
title = s"${tour.name()} #${tour.id}",
|
||||
moreJs = frag(
|
||||
jsModule("tournament"),
|
||||
embedJsUnsafe(s"""lichess=lichess||{};lichess.tournament=${safeJsonValue(
|
||||
embedJsUnsafe(s"""lichess.tournament=${safeJsonValue(
|
||||
Json.obj(
|
||||
"data" -> data,
|
||||
"i18n" -> bits.jsI18n,
|
||||
|
|
|
@ -12,7 +12,6 @@ interface Lichess {
|
|||
powertip: any;
|
||||
widget: any;
|
||||
hoverable?: boolean;
|
||||
isHoverable(): boolean;
|
||||
spinnerHtml: string;
|
||||
assetUrl(url: string, opts?: AssetUrlOpts): string;
|
||||
soundUrl: string;
|
||||
|
@ -27,7 +26,9 @@ interface Lichess {
|
|||
numberFormat(n: number): string;
|
||||
idleTimer(delay: number, onIdle: () => void, onWakeUp: () => void): void;
|
||||
pubsub: Pubsub;
|
||||
hasToReload: boolean;
|
||||
unload: {
|
||||
expected: boolean;
|
||||
};
|
||||
redirect(o: string | { url: string, cookie: Cookie }): void;
|
||||
reload(): void;
|
||||
escapeHtml(str: string): string;
|
||||
|
@ -50,17 +51,11 @@ interface Lichess {
|
|||
update(node: HTMLElement, data: { fen: string, lm: string, wc?: number, bc?: number }): void;
|
||||
finish(node: HTMLElement, win?: Color): void;
|
||||
};
|
||||
challengeApp: any;
|
||||
ab?: any;
|
||||
|
||||
// socket.js
|
||||
StrongSocket: {
|
||||
(url: string, version: number | false, cfg: any): any;
|
||||
defaults: {
|
||||
events: {
|
||||
fen(e: any): void;
|
||||
}
|
||||
},
|
||||
firstConnect: Promise<(tpe: string, data: any) => void>
|
||||
}
|
||||
|
||||
|
@ -70,6 +65,7 @@ interface Lichess {
|
|||
format(date: number | Date): string;
|
||||
absolute(date: number | Date): string;
|
||||
}
|
||||
timeagoLocale(a: number, b: number, c: number): any;
|
||||
|
||||
// misc
|
||||
advantageChart: {
|
||||
|
@ -86,9 +82,6 @@ interface Lichess {
|
|||
playMusic(): any;
|
||||
quietMode?: boolean;
|
||||
keyboardMove?: any;
|
||||
notifyApp: {
|
||||
setMsgRead(user: string): void;
|
||||
};
|
||||
modal: {
|
||||
(html: JQuery | string, cls?: string, onClose?: () => void): void;
|
||||
close(): void;
|
||||
|
|
|
@ -151,7 +151,7 @@ export default class MsgCtrl {
|
|||
setRead = () => {
|
||||
const msg = this.currentContact()?.lastMsg;
|
||||
if (msg && msg.user != this.data.me.id) {
|
||||
window.lichess.notifyApp.setMsgRead(msg.user);
|
||||
window.lichess.pubsub.emit('notify-app.set-read', msg.user);
|
||||
if (msg.read) return false;
|
||||
msg.read = true;
|
||||
network.setRead(msg.user);
|
||||
|
|
|
@ -82,7 +82,7 @@ export default function(opts: RoundOpts): void {
|
|||
.change(round.moveOn.toggle)
|
||||
.prop('checked', round.moveOn.get())
|
||||
.on('click', 'a', () => {
|
||||
li.hasToReload = true;
|
||||
li.unload.expected = true;
|
||||
return true;
|
||||
});
|
||||
if (location.pathname.lastIndexOf('/round-next/', 0) === 0)
|
||||
|
|
|
@ -700,7 +700,7 @@ export default class RoundController {
|
|||
|
||||
window.addEventListener('beforeunload', e => {
|
||||
const d = this.data;
|
||||
if (li.hasToReload ||
|
||||
if (li.unload.expected ||
|
||||
this.nvui ||
|
||||
!game.playable(d) ||
|
||||
!d.clock ||
|
||||
|
|
|
@ -17,7 +17,7 @@ export default class MoveOn {
|
|||
|
||||
private redirect = (href: string) => {
|
||||
this.ctrl.setRedirecting();
|
||||
window.lichess.hasToReload = true;
|
||||
window.lichess.unload.expected = true;
|
||||
window.location.href = href;
|
||||
};
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ export function make(send: SocketSend, ctrl: RoundController): RoundSocket {
|
|||
!isPlayerTurn(ctrl.data)) {
|
||||
ctrl.setRedirecting();
|
||||
sound.move();
|
||||
li.hasToReload = true;
|
||||
li.unload.expected = true;
|
||||
location.href = '/' + gameId;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
lichess.announce = (() => {
|
||||
let timeout;
|
||||
const kill = () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
$('#announce').remove();
|
||||
};
|
||||
const set = d => {
|
||||
if (!d) return;
|
||||
kill();
|
||||
if (d.msg) {
|
||||
$('body').append(
|
||||
'<div id="announce" class="announce">' +
|
||||
lichess.escapeHtml(d.msg) +
|
||||
(d.date ? '<time class="timeago" datetime="' + d.date + '"></time>' : '') +
|
||||
'<div class="actions"><a class="close">X</a></div>' +
|
||||
'</div>'
|
||||
).find('#announce .close').click(kill);
|
||||
timeout = setTimeout(kill, d.date ? new Date(d.date) - Date.now() : 5000);
|
||||
if (d.date) lichess.pubsub.emit('content_loaded');
|
||||
}
|
||||
};
|
||||
set($('body').data('announce'));
|
||||
return set;
|
||||
})();
|
||||
import { escapeHtml } from './functions';
|
||||
import pubsub from './pubsub';
|
||||
|
||||
let timeout;
|
||||
|
||||
const kill = () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
$('#announce').remove();
|
||||
};
|
||||
|
||||
const announce = (d?: { msg: string, date: Date }) => {
|
||||
if (!d) return;
|
||||
kill();
|
||||
if (d.msg) {
|
||||
$('body').append(
|
||||
'<div id="announce" class="announce">' +
|
||||
escapeHtml(d.msg) +
|
||||
(d.date ? '<time class="timeago" datetime="' + d.date + '"></time>' : '') +
|
||||
'<div class="actions"><a class="close">X</a></div>' +
|
||||
'</div>'
|
||||
).find('#announce .close').click(kill);
|
||||
timeout = setTimeout(kill, d.date ? new Date(d.date).getTime() - Date.now() : 5000);
|
||||
if (d.date) pubsub.emit('content_loaded');
|
||||
}
|
||||
};
|
||||
|
||||
announce($('body').data('announce'));
|
||||
|
||||
export default announce;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import widget from './widget';
|
||||
import trans from './trans';
|
||||
import pubsub from './pubsub';
|
||||
|
||||
widget("friends", (() => {
|
||||
const getId = function(titleName) {
|
||||
|
@ -45,6 +46,14 @@ widget("friends", (() => {
|
|||
};
|
||||
self.trans = trans(data.i18n);
|
||||
self.set(data);
|
||||
|
||||
pubsub.on('socket.in.following_onlines', d => {
|
||||
d.users = d.d;
|
||||
self.set(d);
|
||||
});
|
||||
['enters', 'leaves', 'playing', 'stopped_playing'].forEach(k =>
|
||||
pubsub.on('socket.in.following_' + k, self[k])
|
||||
);
|
||||
},
|
||||
repaint: function() {
|
||||
const self: any = this;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import StrongSocket from './socket';
|
||||
import { makeChat } from './functions';
|
||||
import trans from './trans';
|
||||
|
||||
export default function moduleLaunchers() {
|
||||
const li: any = window.lichess;
|
||||
if (li.analyse) window.LichessAnalyse.boot(li.analyse);
|
||||
else if (li.user_analysis) startUserAnalysis(li.user_analysis);
|
||||
else if (li.userAnalysis) startUserAnalysis(li.userAnalysis);
|
||||
else if (li.study) startStudy(li.study);
|
||||
else if (li.practice) startPractice(li.practice);
|
||||
else if (li.relay) startRelay(li.relay);
|
||||
|
@ -11,42 +15,25 @@ export default function moduleLaunchers() {
|
|||
}
|
||||
|
||||
function startTournament(cfg) {
|
||||
var element = document.querySelector('main.tour');
|
||||
const element = document.querySelector('main.tour') as HTMLElement;
|
||||
$('body').data('tournament-id', cfg.data.id);
|
||||
let tournament;
|
||||
lichess.socket = lichess.StrongSocket(
|
||||
'/tournament/' + cfg.data.id + '/socket/v5', cfg.data.socketVersion, {
|
||||
window.lichess.socket = StrongSocket(
|
||||
`/tournament/${cfg.data.id}/socket/v5`, cfg.data.socketVersion, {
|
||||
receive: (t, d) => tournament.socketReceive(t, d)
|
||||
});
|
||||
cfg.socketSend = lichess.socket.send;
|
||||
cfg.socketSend = window.lichess.socket.send;
|
||||
cfg.element = element;
|
||||
tournament = LichessTournament.start(cfg);
|
||||
}
|
||||
|
||||
function startSimul(cfg) {
|
||||
cfg.element = document.querySelector('main.simul');
|
||||
$('body').data('simul-id', cfg.data.id);
|
||||
var simul;
|
||||
lichess.socket = lichess.StrongSocket(
|
||||
'/simul/' + cfg.data.id + '/socket/v5', cfg.socketVersion, {
|
||||
receive: function(t, d) {
|
||||
simul.socketReceive(t, d);
|
||||
}
|
||||
});
|
||||
cfg.socketSend = lichess.socket.send;
|
||||
cfg.$side = $('.simul__side').clone();
|
||||
simul = LichessSimul(cfg);
|
||||
tournament = window.LichessTournament.start(cfg);
|
||||
}
|
||||
|
||||
function startTeam(cfg) {
|
||||
lichess.socket = lichess.StrongSocket('/team/' + cfg.id, cfg.socketVersion);
|
||||
cfg.chat && lichess.makeChat(cfg.chat);
|
||||
$('#team-subscribe').on('change', function() {
|
||||
window.lichess.socket = StrongSocket('/team/' + cfg.id, cfg.socketVersion);
|
||||
cfg.chat && makeChat(cfg.chat);
|
||||
$('#team-subscribe').on('change', function(this: HTMLInputElement) {
|
||||
const v = this.checked;
|
||||
$(this).parents('form').each(function() {
|
||||
$.post($(this).attr('action'), {
|
||||
v: v
|
||||
});
|
||||
$(this).parents('form').each(function(this: HTMLElement) {
|
||||
$.post($(this).attr('action'), { v });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -54,56 +41,48 @@ function startTeam(cfg) {
|
|||
function startUserAnalysis(cfg) {
|
||||
var analyse;
|
||||
cfg.initialPly = 'url';
|
||||
cfg.trans = lichess.trans(cfg.i18n);
|
||||
lichess.socket = lichess.StrongSocket('/analysis/socket/v5', false, {
|
||||
receive: function(t, d) {
|
||||
analyse.socketReceive(t, d);
|
||||
}
|
||||
cfg.trans = trans(cfg.i18n);
|
||||
window.lichess.socket = StrongSocket('/analysis/socket/v5', false, {
|
||||
receive: (t, d) => analyse.socketReceive(t, d)
|
||||
});
|
||||
cfg.socketSend = lichess.socket.send;
|
||||
cfg.socketSend = window.lichess.socket.send;
|
||||
cfg.$side = $('.analyse__side').clone();
|
||||
analyse = LichessAnalyse.start(cfg);
|
||||
analyse = window.LichessAnalyse.start(cfg);
|
||||
}
|
||||
|
||||
function startStudy(cfg) {
|
||||
var analyse;
|
||||
cfg.initialPly = 'url';
|
||||
lichess.socket = lichess.StrongSocket(cfg.socketUrl, cfg.socketVersion, {
|
||||
receive: function(t, d) {
|
||||
analyse.socketReceive(t, d);
|
||||
}
|
||||
window.lichess.socket = StrongSocket(cfg.socketUrl, cfg.socketVersion, {
|
||||
receive: (t, d) => analyse.socketReceive(t, d)
|
||||
});
|
||||
cfg.socketSend = lichess.socket.send;
|
||||
cfg.trans = lichess.trans(cfg.i18n);
|
||||
analyse = LichessAnalyse.start(cfg);
|
||||
cfg.socketSend = window.lichess.socket.send;
|
||||
cfg.trans = trans(cfg.i18n);
|
||||
analyse = window.LichessAnalyse.start(cfg);
|
||||
}
|
||||
|
||||
function startPractice(cfg) {
|
||||
var analyse;
|
||||
cfg.trans = lichess.trans(cfg.i18n);
|
||||
lichess.socket = lichess.StrongSocket('/analysis/socket/v5', false, {
|
||||
receive: function(t, d) {
|
||||
analyse.socketReceive(t, d);
|
||||
}
|
||||
cfg.trans = trans(cfg.i18n);
|
||||
window.lichess.socket = StrongSocket('/analysis/socket/v5', false, {
|
||||
receive: (t, d) => analyse.socketReceive(t, d)
|
||||
});
|
||||
cfg.socketSend = lichess.socket.send;
|
||||
analyse = LichessAnalyse.start(cfg);
|
||||
cfg.socketSend = window.lichess.socket.send;
|
||||
analyse = window.LichessAnalyse.start(cfg);
|
||||
}
|
||||
|
||||
function startRelay(cfg) {
|
||||
var analyse;
|
||||
cfg.initialPly = 'url';
|
||||
lichess.socket = lichess.StrongSocket(cfg.socketUrl, cfg.socketVersion, {
|
||||
receive: function(t, d) {
|
||||
analyse.socketReceive(t, d);
|
||||
}
|
||||
window.lichess.socket = StrongSocket(cfg.socketUrl, cfg.socketVersion, {
|
||||
receive: (t, d) => analyse.socketReceive(t, d)
|
||||
});
|
||||
cfg.socketSend = lichess.socket.send;
|
||||
cfg.trans = lichess.trans(cfg.i18n);
|
||||
analyse = LichessAnalyse.start(cfg);
|
||||
cfg.socketSend = window.lichess.socket.send;
|
||||
cfg.trans = trans(cfg.i18n);
|
||||
analyse = window.LichessAnalyse.start(cfg);
|
||||
}
|
||||
|
||||
function startPuzzle(cfg) {
|
||||
cfg.element = document.querySelector('main.puzzle');
|
||||
LichessPuzzle(cfg);
|
||||
window.LichessPuzzle(cfg);
|
||||
}
|
||||
|
|
|
@ -1,92 +1,96 @@
|
|||
{
|
||||
let hoverable;
|
||||
function isHoverable() {
|
||||
if (typeof hoverable === 'undefined')
|
||||
hoverable = !lichess.hasTouchEvents /* Firefox <= 63 */ || !!getComputedStyle(document.body).getPropertyValue('--hoverable');
|
||||
return hoverable;
|
||||
}
|
||||
import { hasTouchEvents, spinnerHtml } from './intro';
|
||||
import pubsub from './pubsub';
|
||||
import { requestIdleCallback } from './functions';
|
||||
|
||||
const containedIn = (el, container) => container && container.contains(el);
|
||||
let hoverable: boolean;
|
||||
function isHoverable() {
|
||||
if (typeof hoverable === 'undefined')
|
||||
hoverable = !hasTouchEvents /* Firefox <= 63 */ || !!getComputedStyle(document.body).getPropertyValue('--hoverable');
|
||||
return hoverable;
|
||||
}
|
||||
|
||||
const inCrosstable = el => containedIn(el, document.querySelector('.crosstable'));
|
||||
const inCrosstable = (el: HTMLElement) =>
|
||||
document.querySelector('.crosstable')?.contains(el);
|
||||
|
||||
function onPowertipPreRender(id, preload) {
|
||||
return function() {
|
||||
const url = ($(this).data('href') || $(this).attr('href')).replace(/\?.+$/, '');
|
||||
if (preload) preload(url);
|
||||
$.ajax({
|
||||
url: url + '/mini',
|
||||
success: function(html) {
|
||||
$('#' + id).html(html);
|
||||
lichess.pubsub.emit('content_loaded');
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const uptA = (url, icon) => '<a class="btn-rack__btn" href="' + url + '" data-icon="' + icon + '"></a>';
|
||||
|
||||
const userPowertip = (el, pos) => {
|
||||
pos = pos || el.getAttribute('data-pt-pos') || (
|
||||
inCrosstable(el) ? 'n' : 's'
|
||||
);
|
||||
$(el).removeClass('ulpt').powerTip({
|
||||
intentPollInterval: 200,
|
||||
placement: pos,
|
||||
smartPlacement: true,
|
||||
closeDelay: 200
|
||||
}).data('powertip', ' ').on({
|
||||
powerTipRender: onPowertipPreRender('powerTip', (url) => {
|
||||
const u = url.substr(3);
|
||||
const name = $(el).data('name') || $(el).html();
|
||||
$('#powerTip').html('<div class="upt__info"><div class="upt__info__top"><span class="user-link offline">' + name + '</span></div></div><div class="upt__actions btn-rack">' +
|
||||
uptA('/@/' + u + '/tv', '1') +
|
||||
uptA('/inbox/new?user=' + u, 'c') +
|
||||
uptA('/?user=' + u + '#friend', 'U') +
|
||||
'<a class="btn-rack__btn relation-button" disabled></a></div>');
|
||||
})
|
||||
function onPowertipPreRender(id: string, preload?: (url: string) => void) {
|
||||
return function(this: HTMLElement) {
|
||||
const url = ($(this).data('href') || $(this).attr('href')).replace(/\?.+$/, '');
|
||||
if (preload) preload(url);
|
||||
$.ajax({
|
||||
url: url + '/mini',
|
||||
success(html) {
|
||||
$('#' + id).html(html);
|
||||
pubsub.emit('content_loaded');
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function gamePowertip(el) {
|
||||
$(el).removeClass('glpt').powerTip({
|
||||
intentPollInterval: 200,
|
||||
placement: inCrosstable(el) ? 'n' : 'w',
|
||||
smartPlacement: true,
|
||||
closeDelay: 200,
|
||||
popupId: 'miniGame'
|
||||
}).on({
|
||||
powerTipPreRender: onPowertipPreRender('miniGame')
|
||||
}).data('powertip', lichess.spinnerHtml);
|
||||
};
|
||||
const uptA = (url: string, icon: string) =>
|
||||
`<a class="btn-rack__btn" href="${url}" data-icon="${icon}"></a>`;
|
||||
|
||||
function powerTipWith(el, ev, f) {
|
||||
if (lichess.isHoverable()) {
|
||||
f(el);
|
||||
$.powerTip.show(el, ev);
|
||||
}
|
||||
};
|
||||
const userPowertip = (el: HTMLElement, pos?: PowerTip.Placement) => {
|
||||
pos = pos || (el.getAttribute('data-pt-pos') as PowerTip.Placement) || (
|
||||
inCrosstable(el) ? 'n' : 's'
|
||||
);
|
||||
$(el).removeClass('ulpt').powerTip({
|
||||
intentPollInterval: 200,
|
||||
placement: pos,
|
||||
smartPlacement: true,
|
||||
closeDelay: 200
|
||||
}).data('powertip', ' ').on({
|
||||
powerTipRender: onPowertipPreRender('powerTip', (url: string) => {
|
||||
const u = url.substr(3);
|
||||
const name = $(el).data('name') || $(el).html();
|
||||
$('#powerTip').html('<div class="upt__info"><div class="upt__info__top"><span class="user-link offline">' + name + '</span></div></div><div class="upt__actions btn-rack">' +
|
||||
uptA('/@/' + u + '/tv', '1') +
|
||||
uptA('/inbox/new?user=' + u, 'c') +
|
||||
uptA('/?user=' + u + '#friend', 'U') +
|
||||
'<a class="btn-rack__btn relation-button" disabled></a></div>');
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
function onIdleForAll(par, sel, fun) {
|
||||
lichess.requestIdleCallback(() =>
|
||||
Array.prototype.forEach.call(par.querySelectorAll(sel), el => fun(el)) // do not codegolf to `fun`
|
||||
)
|
||||
function gamePowertip(el: HTMLElement) {
|
||||
$(el).removeClass('glpt').powerTip({
|
||||
intentPollInterval: 200,
|
||||
placement: inCrosstable(el) ? 'n' : 'w',
|
||||
smartPlacement: true,
|
||||
closeDelay: 200,
|
||||
popupId: 'miniGame'
|
||||
}).on({
|
||||
powerTipPreRender: onPowertipPreRender('miniGame')
|
||||
}).data('powertip', spinnerHtml);
|
||||
};
|
||||
|
||||
function powerTipWith(el, ev, f) {
|
||||
if (isHoverable()) {
|
||||
f(el);
|
||||
$.powerTip.show(el, ev);
|
||||
}
|
||||
};
|
||||
|
||||
lichess.powertip = {
|
||||
mouseover(e) {
|
||||
var t = e.target,
|
||||
cl = t.classList;
|
||||
if (cl.contains('ulpt')) powerTipWith(t, e, userPowertip);
|
||||
else if (cl.contains('glpt')) powerTipWith(t, e, gamePowertip);
|
||||
},
|
||||
manualGameIn(parent) {
|
||||
onIdleForAll(parent, '.glpt', gamePowertip);
|
||||
},
|
||||
manualGame: gamePowertip,
|
||||
manualUser: userPowertip,
|
||||
manualUserIn(parent) {
|
||||
onIdleForAll(parent, '.ulpt', userPowertip);
|
||||
}
|
||||
};
|
||||
function onIdleForAll(par, sel, fun) {
|
||||
requestIdleCallback(() =>
|
||||
Array.prototype.forEach.call(par.querySelectorAll(sel), el => fun(el)) // do not codegolf to `fun`
|
||||
)
|
||||
}
|
||||
|
||||
const powertip = {
|
||||
mouseover(e) {
|
||||
var t = e.target,
|
||||
cl = t.classList;
|
||||
if (cl.contains('ulpt')) powerTipWith(t, e, userPowertip);
|
||||
else if (cl.contains('glpt')) powerTipWith(t, e, gamePowertip);
|
||||
},
|
||||
manualGameIn(parent) {
|
||||
onIdleForAll(parent, '.glpt', gamePowertip);
|
||||
},
|
||||
manualGame: gamePowertip,
|
||||
manualUser: userPowertip,
|
||||
manualUserIn(parent) {
|
||||
onIdleForAll(parent, '.ulpt', userPowertip);
|
||||
}
|
||||
};
|
||||
|
||||
export default powertip;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export let hasToReload = false;
|
||||
export let redirectInProgress: false | string = false;
|
||||
|
||||
export const redirect = obj => {
|
||||
|
@ -22,9 +21,13 @@ export const redirect = obj => {
|
|||
location.href = href;
|
||||
};
|
||||
|
||||
export const unload = {
|
||||
expected: false
|
||||
}
|
||||
|
||||
export const reload = () => {
|
||||
if (redirectInProgress) return;
|
||||
hasToReload = true;
|
||||
unload.expected = true;
|
||||
window.lichess.socket?.disconnect();
|
||||
if (location.hash) location.reload();
|
||||
else location.href = location.href;
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
lichess.serviceWorker = () => {
|
||||
import {assetUrl, jsModule} from "./assets";
|
||||
import {storage} from "./storage";
|
||||
|
||||
export default function() {
|
||||
if ('serviceWorker' in navigator && 'Notification' in window && 'PushManager' in window) {
|
||||
const workerUrl = new URL(lichess.assetUrl(lichess.jsModule('serviceWorker'), {
|
||||
const workerUrl = new URL(assetUrl(jsModule('serviceWorker'), {
|
||||
sameDomain: true
|
||||
}), self.location.href);
|
||||
workerUrl.searchParams.set('asset-url', document.body.getAttribute('data-asset-url'));
|
||||
workerUrl.searchParams.set('asset-url', document.body.getAttribute('data-asset-url')!);
|
||||
if (document.body.getAttribute('data-dev')) workerUrl.searchParams.set('dev', '1');
|
||||
const updateViaCache = document.body.getAttribute('data-dev') ? 'none' : 'all';
|
||||
navigator.serviceWorker.register(workerUrl.href, {
|
||||
scope: '/',
|
||||
updateViaCache
|
||||
}).then(reg => {
|
||||
const storage = lichess.storage.make('push-subscribed');
|
||||
const store = storage.make('push-subscribed');
|
||||
const vapid = document.body.getAttribute('data-vapid');
|
||||
if (vapid && Notification.permission == 'granted') return reg.pushManager.getSubscription().then(sub => {
|
||||
const resub = parseInt(storage.get() || '0', 10) + 43200000 < Date.now(); // 12 hours
|
||||
const resub = parseInt(store.get() || '0', 10) + 43200000 < Date.now(); // 12 hours
|
||||
const applicationServerKey = Uint8Array.from(atob(vapid), c => c.charCodeAt(0));
|
||||
if (!sub || resub) {
|
||||
return reg.pushManager.subscribe({
|
||||
|
@ -26,15 +29,15 @@ lichess.serviceWorker = () => {
|
|||
},
|
||||
body: JSON.stringify(sub)
|
||||
}).then(res => {
|
||||
if (res.ok) storage.set('' + Date.now());
|
||||
else console.log('submitting push subscription failed', response.statusText);
|
||||
if (res.ok) store.set('' + Date.now());
|
||||
else console.log('submitting push subscription failed', res.statusText);
|
||||
}), err => {
|
||||
console.log('push subscribe failed', err.message);
|
||||
if (sub) sub.unsubscribe();
|
||||
});
|
||||
}
|
||||
});
|
||||
else storage.remove();
|
||||
else store.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
lichess.setSocketDefaults = $friendsBox =>
|
||||
$.extend(true, lichess.StrongSocket.defaults, {
|
||||
events: {
|
||||
following_onlines(_, d) {
|
||||
d.users = d.d;
|
||||
$friendsBox.friends("set", d);
|
||||
},
|
||||
following_enters(_, d) {
|
||||
$friendsBox.friends('enters', d);
|
||||
},
|
||||
following_leaves(name) {
|
||||
$friendsBox.friends('leaves', name);
|
||||
},
|
||||
following_playing(name) {
|
||||
$friendsBox.friends('playing', name);
|
||||
},
|
||||
following_stopped_playing(name) {
|
||||
$friendsBox.friends('stopped_playing', name);
|
||||
},
|
||||
redirect(o) {
|
||||
setTimeout(() => {
|
||||
lichess.hasToReload = true;
|
||||
lichess.redirect(o);
|
||||
}, 200);
|
||||
},
|
||||
tournamentReminder(data) {
|
||||
if ($('#announce').length || $('body').data("tournament-id") == data.id) return;
|
||||
const url = '/tournament/' + data.id;
|
||||
$('body').append(
|
||||
'<div id="announce">' +
|
||||
'<a data-icon="g" class="text" href="' + url + '">' + data.name + '</a>' +
|
||||
'<div class="actions">' +
|
||||
'<a class="withdraw text" href="' + url + '/withdraw" data-icon="Z">Pause</a>' +
|
||||
'<a class="text" href="' + url + '" data-icon="G">Resume</a>' +
|
||||
'</div></div>'
|
||||
).find('#announce .withdraw').click(function() {
|
||||
$.post($(this).attr("href"));
|
||||
$('#announce').remove();
|
||||
return false;
|
||||
});
|
||||
},
|
||||
announce: lichess.announce
|
||||
},
|
||||
params: {},
|
||||
options: {
|
||||
isAuth: !!$('body').data('user')
|
||||
}
|
||||
});
|
|
@ -44,7 +44,7 @@ function makeAckable(send) {
|
|||
}
|
||||
|
||||
// versioned events, acks, retries, resync
|
||||
const StrongSocket = function(url, version, settings) {
|
||||
const StrongSocket = function(url: string, version: number | false, settings?: any) {
|
||||
|
||||
var settings = $.extend(true, {}, StrongSocket.defaults, settings);
|
||||
var options = settings.options;
|
||||
|
@ -175,12 +175,12 @@ const StrongSocket = function(url, version, settings) {
|
|||
};
|
||||
|
||||
var handle = function(m) {
|
||||
if (m.v) {
|
||||
if (m.v && version !== false) {
|
||||
if (m.v <= version) {
|
||||
debug("already has event " + m.v);
|
||||
return;
|
||||
}
|
||||
// it's impossible but according to previous login, it happens nonetheless
|
||||
// it's impossible but according to previous logging, it happens nonetheless
|
||||
if (m.v > version + 1) return reload();
|
||||
version = m.v;
|
||||
}
|
||||
|
@ -281,7 +281,8 @@ StrongSocket.defaults = {
|
|||
pingMaxLag: 9000, // time to wait for pong before reseting the connection
|
||||
pingDelay: 2500, // time between pong and ping
|
||||
autoReconnectDelay: 3500,
|
||||
protocol: location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
protocol: location.protocol === 'https:' ? 'wss:' : 'ws:',
|
||||
isAuth: document.body.hasAttribute('user')
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { storage } from './storage';
|
||||
import pubsub from './pubsub';
|
||||
import soundBox from './soundbox';
|
||||
import { soundUrl } from './assets';
|
||||
|
||||
const api: any = {};
|
||||
|
||||
|
@ -46,14 +49,14 @@ const volumes = {
|
|||
explode: 0.35,
|
||||
confirmation: 0.5
|
||||
};
|
||||
api.collection = new memoize(k => {
|
||||
api.collection = memoize(k => {
|
||||
let set = soundSet;
|
||||
if (set === 'music' || speechStorage.get()) {
|
||||
if (['move', 'capture', 'check'].includes(k)) return $.noop;
|
||||
set = 'standard';
|
||||
}
|
||||
lichess.soundBox.loadOggOrMp3(k, `${lichess.soundUrl}/${set}/${names[k]}`);
|
||||
return () => lichess.soundBox.play(k, volumes[k] || 1);
|
||||
soundBox.loadOggOrMp3(k, `${soundUrl}/${set}/${names[k]}`);
|
||||
return () => soundBox.play(k, volumes[k] || 1);
|
||||
});
|
||||
const enabled = () => soundSet !== 'silent';
|
||||
api.load = (name, file) => {
|
||||
|
@ -66,7 +69,7 @@ Object.keys(names).forEach(api.load);
|
|||
api.say = (text, cut, force) => {
|
||||
if (!speechStorage.get() && !force) return false;
|
||||
const msg = text.text ? text : new SpeechSynthesisUtterance(text);
|
||||
msg.volume = lichess.soundBox.getVolume();
|
||||
msg.volume = soundBox.getVolume();
|
||||
msg.lang = 'en-US';
|
||||
if (cut) speechSynthesis.cancel();
|
||||
speechSynthesis.speak(msg);
|
||||
|
@ -74,7 +77,8 @@ api.say = (text, cut, force) => {
|
|||
return true;
|
||||
};
|
||||
|
||||
const publish = () => lichess.pubsub.emit('sound_set', soundSet);
|
||||
const publish = () => pubsub.emit('sound_set', soundSet);
|
||||
|
||||
if (soundSet == 'music') setTimeout(publish, 500);
|
||||
|
||||
api.changeSet = s => {
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
import { storage } from './storage';
|
||||
|
||||
class SoundBox {
|
||||
|
||||
sounds = {}; // The loaded sounds and their instances
|
||||
sounds = new Map(); // The loaded sounds and their instances
|
||||
|
||||
volume = lichess.storage.make('sound-volume');
|
||||
volume = storage.make('sound-volume');
|
||||
|
||||
loadOggOrMp3 = (name, path) =>
|
||||
this.sounds[name] = new Howl({
|
||||
loadOggOrMp3 = (name: string, path: string) =>
|
||||
this.sounds.set(name, new window.Howl({
|
||||
src: ['ogg', 'mp3'].map(ext => `${path}.${ext}`)
|
||||
});
|
||||
}));
|
||||
|
||||
play(name, volume = 1) {
|
||||
play(name: string, volume: number = 1) {
|
||||
const doPlay = () => {
|
||||
this.sounds[name].volume(volume * this.getVolume());
|
||||
this.sounds[name].play();
|
||||
this.sounds.get(name).volume(volume * this.getVolume());
|
||||
this.sounds.get(name).play();
|
||||
};
|
||||
if (Howler.ctx.state == "suspended") Howler.ctx.resume().then(doPlay);
|
||||
if (window.Howler.ctx.state == "suspended") window.Howler.ctx.resume().then(doPlay);
|
||||
else doPlay();
|
||||
};
|
||||
|
||||
|
@ -26,4 +28,7 @@ class SoundBox {
|
|||
return v >= 0 ? v : 0.7;
|
||||
}
|
||||
}
|
||||
lichess.soundBox = new SoundBox
|
||||
|
||||
const soundBox = new SoundBox;
|
||||
|
||||
export default soundBox;
|
||||
|
|
|
@ -1,86 +1,85 @@
|
|||
/** based on https://github.com/hustcc/timeago.js Copyright (c) 2016 hustcc License: MIT **/
|
||||
lichess.timeago = (function() {
|
||||
// divisors for minutes, hours, days, weeks, months, years
|
||||
const DIVS = [60,
|
||||
60 * 60,
|
||||
60 * 60 * 24,
|
||||
60 * 60 * 24 * 7,
|
||||
60 * 60 * 2 * 365, // 24/12 = 2
|
||||
60 * 60 * 24 * 365];
|
||||
|
||||
const LIMITS = [...DIVS];
|
||||
LIMITS[2] *= 2; // Show hours up to 2 days.
|
||||
|
||||
// divisors for minutes, hours, days, weeks, months, years
|
||||
const DIVS = [60,
|
||||
60 * 60,
|
||||
60 * 60 * 24,
|
||||
60 * 60 * 24 * 7,
|
||||
60 * 60 * 2 * 365, // 24/12 = 2
|
||||
60 * 60 * 24 * 365];
|
||||
// format Date / string / timestamp to Date instance.
|
||||
function toDate(input: any): Date {
|
||||
return input instanceof Date ? input : (
|
||||
new Date(isNaN(input) ? input : parseInt(input))
|
||||
);
|
||||
}
|
||||
|
||||
const LIMITS = [...DIVS];
|
||||
LIMITS[2] *= 2; // Show hours up to 2 days.
|
||||
|
||||
// format Date / string / timestamp to Date instance.
|
||||
function toDate(input) {
|
||||
return input instanceof Date ? input : (
|
||||
new Date(isNaN(input) ? input : parseInt(input))
|
||||
);
|
||||
// format the diff second to *** time ago
|
||||
function formatDiff(diff) {
|
||||
let agoin = 0;
|
||||
if (diff < 0) {
|
||||
agoin = 1;
|
||||
diff = -diff;
|
||||
}
|
||||
const totalSec = diff;
|
||||
|
||||
// format the diff second to *** time ago
|
||||
function formatDiff(diff) {
|
||||
let agoin = 0;
|
||||
if (diff < 0) {
|
||||
agoin = 1;
|
||||
diff = -diff;
|
||||
}
|
||||
var total_sec = diff;
|
||||
let i = 0;
|
||||
for (; i < 6 && diff >= LIMITS[i]; i++);
|
||||
if (i > 0) diff /= DIVS[i - 1];
|
||||
|
||||
let i = 0;
|
||||
for (;i < 6 && diff >= LIMITS[i]; i++);
|
||||
if (i > 0) diff /= DIVS[i-1];
|
||||
diff = Math.floor(diff);
|
||||
i *= 2;
|
||||
|
||||
diff = Math.floor(diff);
|
||||
i *= 2;
|
||||
if (diff > (i === 0 ? 9 : 1)) i += 1;
|
||||
return window.lichess.timeagoLocale(diff, i, totalSec)[agoin].replace('%s', diff);
|
||||
}
|
||||
|
||||
if (diff > (i === 0 ? 9 : 1)) i += 1;
|
||||
return lichess.timeagoLocale(diff, i, total_sec)[agoin].replace('%s', diff);
|
||||
}
|
||||
let formatterInst;
|
||||
|
||||
var formatterInst;
|
||||
|
||||
function formatter() {
|
||||
return formatterInst = formatterInst || (window.Intl && Intl.DateTimeFormat ?
|
||||
function formatter() {
|
||||
return formatterInst = formatterInst || (
|
||||
window.Intl && Intl.DateTimeFormat ?
|
||||
new Intl.DateTimeFormat(document.documentElement.lang, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
}).format : function(d) { return d.toLocaleString(); })
|
||||
}
|
||||
}).format : d => d.toLocaleString()
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
render: function(nodes) {
|
||||
var cl, abs, set, str, diff, now = Date.now();
|
||||
nodes.forEach(function(node) {
|
||||
cl = node.classList,
|
||||
const timeago = {
|
||||
render: function(nodes) {
|
||||
var cl, abs, set, str, diff, now = Date.now();
|
||||
nodes.forEach(function(node) {
|
||||
cl = node.classList,
|
||||
abs = cl.contains('abs'),
|
||||
set = cl.contains('set');
|
||||
node.date = node.date || toDate(node.getAttribute('datetime'));
|
||||
if (!set) {
|
||||
str = formatter()(node.date);
|
||||
if (abs) node.textContent = str;
|
||||
else node.setAttribute('title', str);
|
||||
cl.add('set');
|
||||
if (abs || cl.contains('once')) cl.remove('timeago');
|
||||
}
|
||||
if (!abs) {
|
||||
diff = (now - node.date) / 1000;
|
||||
node.textContent = formatDiff(diff);
|
||||
if (Math.abs(diff) > 9999) cl.remove('timeago'); // ~3h
|
||||
}
|
||||
});
|
||||
},
|
||||
// relative
|
||||
format: function(date) {
|
||||
return formatDiff((Date.now() - toDate(date)) / 1000);
|
||||
},
|
||||
absolute: function(date) {
|
||||
return formatter()(toDate(date));
|
||||
}
|
||||
};
|
||||
})();
|
||||
node.date = node.date || toDate(node.getAttribute('datetime'));
|
||||
if (!set) {
|
||||
str = formatter()(node.date);
|
||||
if (abs) node.textContent = str;
|
||||
else node.setAttribute('title', str);
|
||||
cl.add('set');
|
||||
if (abs || cl.contains('once')) cl.remove('timeago');
|
||||
}
|
||||
if (!abs) {
|
||||
diff = (now - node.date) / 1000;
|
||||
node.textContent = formatDiff(diff);
|
||||
if (Math.abs(diff) > 9999) cl.remove('timeago'); // ~3h
|
||||
}
|
||||
});
|
||||
},
|
||||
// relative
|
||||
format: function(date) {
|
||||
return formatDiff((Date.now() - toDate(date).getTime()) / 1000);
|
||||
},
|
||||
absolute: function(date) {
|
||||
return formatter()(toDate(date));
|
||||
}
|
||||
};
|
||||
|
||||
export default timeago;
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import pubsub from './pubsub';
|
||||
import { spinnerHtml } from './intro';
|
||||
import { loadCssPath, loadScript, jsModule } from './assets';
|
||||
|
||||
lichess.topBar = () => {
|
||||
export default function() {
|
||||
|
||||
const initiatingHtml = '<div class="initiating">' + lichess.spinnerHtml + '</div>';
|
||||
const initiatingHtml = '<div class="initiating">' + spinnerHtml + '</div>';
|
||||
|
||||
$('#topnav-toggle').on('change', e => {
|
||||
document.body.classList.toggle('masked', e.target.checked);
|
||||
document.body.classList.toggle('masked', (e.target as HTMLInputElement).checked);
|
||||
});
|
||||
|
||||
$('#top').on('click', 'a.toggle', function() {
|
||||
$('#top').on('click', 'a.toggle', function(this: HTMLElement) {
|
||||
var $p = $(this).parent();
|
||||
$p.toggleClass('shown');
|
||||
$p.siblings('.shown').removeClass('shown');
|
||||
lichess.pubsub.emit('top.toggle.' + $(this).attr('id'));
|
||||
setTimeout(function() {
|
||||
pubsub.emit('top.toggle.' + $(this).attr('id'));
|
||||
setTimeout(() => {
|
||||
const handler = function(e) {
|
||||
if ($.contains($p[0], e.target)) return;
|
||||
$p.removeClass('shown');
|
||||
|
@ -28,13 +30,13 @@ lichess.topBar = () => {
|
|||
let instance, booted;
|
||||
const $toggle = $('#challenge-toggle');
|
||||
$toggle.one('mouseover click', () => load());
|
||||
const load = function(data) {
|
||||
const load = function(data?: any) {
|
||||
if (booted) return;
|
||||
booted = true;
|
||||
const $el = $('#challenge-app').html(initiatingHtml);
|
||||
lichess.loadCssPath('challenge');
|
||||
lichess.loadScript(lichess.jsModule('challenge')).done(function() {
|
||||
instance = LichessChallenge($el[0], {
|
||||
loadCssPath('challenge');
|
||||
loadScript(jsModule('challenge')).done(function() {
|
||||
instance = window.LichessChallenge($el[0], {
|
||||
data: data,
|
||||
show() {
|
||||
if (!$('#challenge-app').is(':visible')) $toggle.click();
|
||||
|
@ -60,13 +62,13 @@ lichess.topBar = () => {
|
|||
const $toggle = $('#notify-toggle'),
|
||||
isVisible = () => $('#notify-app').is(':visible');
|
||||
|
||||
const load = function(data, incoming) {
|
||||
const load = (data?: any, incoming = false) => {
|
||||
if (booted) return;
|
||||
booted = true;
|
||||
var $el = $('#notify-app').html(initiatingHtml);
|
||||
lichess.loadCssPath('notify');
|
||||
lichess.loadScript(lichess.jsModule('notify')).done(() => {
|
||||
instance = LichessNotify($el.empty()[0], {
|
||||
loadCssPath('notify');
|
||||
loadScript(jsModule('notify')).done(() => {
|
||||
instance = window.LichessNotify($el.empty()[0], {
|
||||
data: data,
|
||||
incoming: incoming,
|
||||
isVisible: isVisible,
|
||||
|
@ -77,7 +79,7 @@ lichess.topBar = () => {
|
|||
if (!isVisible()) $toggle.click();
|
||||
},
|
||||
setNotified() {
|
||||
lichess.socket.send('notified');
|
||||
window.lichess.socket.send('notified');
|
||||
},
|
||||
pulse() {
|
||||
$toggle.addClass('pulse');
|
||||
|
@ -97,12 +99,10 @@ lichess.topBar = () => {
|
|||
if (!instance) load(data, true);
|
||||
else instance.update(data, true);
|
||||
});
|
||||
lichess.notifyApp = {
|
||||
setMsgRead(user) {
|
||||
if (!instance) load();
|
||||
else instance.setMsgRead(user);
|
||||
}
|
||||
};
|
||||
pubsub.on('notify-app.set-read', user => {
|
||||
if (!instance) load();
|
||||
else instance.setMsgRead(user);
|
||||
});
|
||||
}
|
||||
|
||||
{ // dasher
|
||||
|
@ -112,9 +112,9 @@ lichess.topBar = () => {
|
|||
booted = true;
|
||||
const $el = $('#dasher_app').html(initiatingHtml),
|
||||
playing = $('body').hasClass('playing');
|
||||
lichess.loadCssPath('dasher');
|
||||
lichess.loadScript(lichess.jsModule('dasher')).done(() =>
|
||||
LichessDasher($el.empty()[0], {
|
||||
loadCssPath('dasher');
|
||||
loadScript(jsModule('dasher')).done(() =>
|
||||
window.LichessDasher($el.empty()[0], {
|
||||
playing
|
||||
})
|
||||
);
|
||||
|
@ -129,8 +129,8 @@ lichess.topBar = () => {
|
|||
const boot = () => {
|
||||
if (booted) return;
|
||||
booted = true;
|
||||
lichess.loadScript(lichess.jsModule('cli')).done(() =>
|
||||
LichessCli.app($wrap, toggle)
|
||||
loadScript(jsModule('cli')).done(() =>
|
||||
window.LichessCli.app($wrap, toggle)
|
||||
);
|
||||
};
|
||||
const toggle = () => {
|
||||
|
@ -139,12 +139,12 @@ lichess.topBar = () => {
|
|||
if ($('body').hasClass('clinput')) $input.focus();
|
||||
};
|
||||
$wrap.find('a').on('mouseover click', e => (e.type === 'mouseover' ? boot : toggle)());
|
||||
Mousetrap.bind('/', () => {
|
||||
window.Mousetrap.bind('/', () => {
|
||||
$input.val('/');
|
||||
requestAnimationFrame(() => toggle());
|
||||
return false;
|
||||
});
|
||||
Mousetrap.bind('s', () => requestAnimationFrame(() => toggle()));
|
||||
window.Mousetrap.bind('s', () => requestAnimationFrame(() => toggle()));
|
||||
if ($('body').hasClass('blind-mode')) $input.one('focus', () => toggle());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import './component/trans';
|
|||
import './component/user-autocomplete';
|
||||
import './component/infinite-scroll';
|
||||
import './component/socket';
|
||||
import './component/socket-defaults';
|
||||
import './component/top-bar';
|
||||
import './component/service-worker';
|
||||
import './component/module-launchers';
|
||||
|
|
|
@ -1,160 +1,190 @@
|
|||
const $friendsBox = $('#friend_box');
|
||||
import StrongSocket from "./component/socket";
|
||||
import { unload, redirect } from "./component/reload";
|
||||
import announce from './component/announce';
|
||||
import moduleLaunchers from "./component/module-launchers";
|
||||
import pubsub from "./component/pubsub";
|
||||
import miniBoard from "./component/mini-board";
|
||||
import miniGame from "./component/mini-game";
|
||||
import {requestIdleCallback} from "./component/functions";
|
||||
|
||||
lichess.setSocketDefaults($friendsBox);
|
||||
StrongSocket.defaults.events = {
|
||||
redirect(o) {
|
||||
setTimeout(() => {
|
||||
unload.expected = true;
|
||||
redirect(o);
|
||||
}, 200);
|
||||
},
|
||||
tournamentReminder(data) {
|
||||
if ($('#announce').length || $('body').data("tournament-id") == data.id) return;
|
||||
const url = '/tournament/' + data.id;
|
||||
$('body').append(
|
||||
'<div id="announce">' +
|
||||
'<a data-icon="g" class="text" href="' + url + '">' + data.name + '</a>' +
|
||||
'<div class="actions">' +
|
||||
'<a class="withdraw text" href="' + url + '/withdraw" data-icon="Z">Pause</a>' +
|
||||
'<a class="text" href="' + url + '" data-icon="G">Resume</a>' +
|
||||
'</div></div>'
|
||||
).find('#announce .withdraw').click(function(this: HTMLElement) {
|
||||
$.post($(this).attr("href"));
|
||||
$('#announce').remove();
|
||||
return false;
|
||||
});
|
||||
},
|
||||
announce
|
||||
};
|
||||
|
||||
$(() => {
|
||||
|
||||
lichess.moduleLaunchers();
|
||||
moduleLaunchers();
|
||||
|
||||
lichess.pubsub.on('content_loaded', lichess.miniBoard.initAll);
|
||||
lichess.pubsub.on('content_loaded', lichess.miniGame.initAll);
|
||||
pubsub.on('content_loaded', miniBoard.initAll);
|
||||
pubsub.on('content_loaded', miniGame.initAll);
|
||||
|
||||
lichess.pubsub.on('socket.in.fen', e =>
|
||||
document.querySelectorAll('.mini-game-' + e.id).forEach((el: HTMLElement) => lichess.miniGame.update(el, e))
|
||||
pubsub.on('socket.in.fen', e =>
|
||||
document.querySelectorAll('.mini-game-' + e.id).forEach((el: HTMLElement) => miniGame.update(el, e))
|
||||
);
|
||||
lichess.pubsub.on('socket.in.finish', e =>
|
||||
pubsub.on('socket.in.finish', e =>
|
||||
document.querySelectorAll('.mini-game-' + e.id).forEach((el: HTMLElement) => miniGame.finish(el, e.win))
|
||||
);
|
||||
}
|
||||
|
||||
lichess.requestIdleCallback(() => {
|
||||
requestIdleCallback(() => {
|
||||
|
||||
$friendsBox.friends();
|
||||
$('#friend_box').friends();
|
||||
|
||||
$('#main-wrap')
|
||||
.on('click', '.autoselect', function() {
|
||||
$(this).select();
|
||||
})
|
||||
.on('click', 'button.copy', function() {
|
||||
$('#' + $(this).data('rel')).select();
|
||||
document.execCommand('copy');
|
||||
$(this).attr('data-icon', 'E');
|
||||
$('#main-wrap')
|
||||
.on('click', '.autoselect', function() {
|
||||
$(this).select();
|
||||
})
|
||||
.on('click', 'button.copy', function() {
|
||||
$('#' + $(this).data('rel')).select();
|
||||
document.execCommand('copy');
|
||||
$(this).attr('data-icon', 'E');
|
||||
});
|
||||
|
||||
$('body').on('click', 'a.relation-button', function() {
|
||||
var $a = $(this).addClass('processing').css('opacity', 0.3);
|
||||
$.ajax({
|
||||
url: $a.attr('href'),
|
||||
type: 'post',
|
||||
success: function(html) {
|
||||
if (html.includes('relation-actions')) $a.parent().replaceWith(html);
|
||||
else $a.replaceWith(html);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('body').on('click', 'a.relation-button', function() {
|
||||
var $a = $(this).addClass('processing').css('opacity', 0.3);
|
||||
$.ajax({
|
||||
url: $a.attr('href'),
|
||||
type: 'post',
|
||||
success: function(html) {
|
||||
if (html.includes('relation-actions')) $a.parent().replaceWith(html);
|
||||
else $a.replaceWith(html);
|
||||
$('.mselect .button').on('click', function() {
|
||||
const $p = $(this).parent();
|
||||
$p.toggleClass('shown');
|
||||
setTimeout(function() {
|
||||
const handler = function(e) {
|
||||
if ($.contains($p[0], e.target)) return;
|
||||
$p.removeClass('shown');
|
||||
$('html').off('click', handler);
|
||||
};
|
||||
$('html').on('click', handler);
|
||||
}, 10);
|
||||
});
|
||||
|
||||
document.body.addEventListener('mouseover', lichess.powertip.mouseover);
|
||||
|
||||
{ // timeago
|
||||
const renderTimeago = () =>
|
||||
requestAnimationFrame(() =>
|
||||
lichess.timeago.render([].slice.call(document.getElementsByClassName('timeago'), 0, 99))
|
||||
);
|
||||
|
||||
const setTimeago = interval => {
|
||||
renderTimeago();
|
||||
setTimeout(() => setTimeago(interval * 1.1), interval);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
setTimeago(1200);
|
||||
lichess.pubsub.on('content_loaded', renderTimeago);
|
||||
}
|
||||
|
||||
$('.mselect .button').on('click', function() {
|
||||
const $p = $(this).parent();
|
||||
$p.toggleClass('shown');
|
||||
setTimeout(function() {
|
||||
const handler = function(e) {
|
||||
if ($.contains($p[0], e.target)) return;
|
||||
$p.removeClass('shown');
|
||||
$('html').off('click', handler);
|
||||
if (!window.customWS) setTimeout(() => {
|
||||
if (!lichess.socket)
|
||||
lichess.socket = lichess.StrongSocket("/socket/v5", false);
|
||||
}, 300);
|
||||
|
||||
lichess.topBar();
|
||||
|
||||
window.addEventListener('resize', () => lichess.dispatchEvent(document.body, 'chessground.resize'));
|
||||
|
||||
$('.user-autocomplete').each(function() {
|
||||
const opts = {
|
||||
focus: 1,
|
||||
friend: $(this).data('friend'),
|
||||
tag: $(this).data('tag')
|
||||
};
|
||||
$('html').on('click', handler);
|
||||
}, 10);
|
||||
});
|
||||
|
||||
document.body.addEventListener('mouseover', lichess.powertip.mouseover);
|
||||
|
||||
{ // timeago
|
||||
const renderTimeago = () =>
|
||||
requestAnimationFrame(() =>
|
||||
lichess.timeago.render([].slice.call(document.getElementsByClassName('timeago'), 0, 99))
|
||||
);
|
||||
|
||||
const setTimeago = interval => {
|
||||
renderTimeago();
|
||||
setTimeout(() => setTimeago(interval * 1.1), interval);
|
||||
}
|
||||
setTimeago(1200);
|
||||
lichess.pubsub.on('content_loaded', renderTimeago);
|
||||
}
|
||||
|
||||
if (!window.customWS) setTimeout(() => {
|
||||
if (!lichess.socket)
|
||||
lichess.socket = lichess.StrongSocket("/socket/v5", false);
|
||||
}, 300);
|
||||
|
||||
lichess.topBar();
|
||||
|
||||
window.addEventListener('resize', () => lichess.dispatchEvent(document.body, 'chessground.resize'));
|
||||
|
||||
$('.user-autocomplete').each(function() {
|
||||
const opts = {
|
||||
focus: 1,
|
||||
friend: $(this).data('friend'),
|
||||
tag: $(this).data('tag')
|
||||
};
|
||||
if ($(this).attr('autofocus')) lichess.userAutocomplete($(this), opts);
|
||||
else $(this).one('focus', function() {
|
||||
lichess.userAutocomplete($(this), opts);
|
||||
if ($(this).attr('autofocus')) lichess.userAutocomplete($(this), opts);
|
||||
else $(this).one('focus', function() {
|
||||
lichess.userAutocomplete($(this), opts);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
lichess.loadInfiniteScroll('.infinitescroll');
|
||||
lichess.loadInfiniteScroll('.infinitescroll');
|
||||
|
||||
$('a.delete, input.delete').click(() => confirm('Delete?'));
|
||||
$('input.confirm, button.confirm').click(function() {
|
||||
return confirm($(this).attr('title') || 'Confirm this action?');
|
||||
});
|
||||
$('a.delete, input.delete').click(() => confirm('Delete?'));
|
||||
$('input.confirm, button.confirm').click(function() {
|
||||
return confirm($(this).attr('title') || 'Confirm this action?');
|
||||
});
|
||||
|
||||
$('#main-wrap').on('click', 'a.bookmark', function() {
|
||||
const t = $(this).toggleClass("bookmarked");
|
||||
$.post(t.attr("href"));
|
||||
const count = (parseInt(t.text(), 10) || 0) + (t.hasClass("bookmarked") ? 1 : -1);
|
||||
t.find('span').html(count > 0 ? count : "");
|
||||
return false;
|
||||
});
|
||||
$('#main-wrap').on('click', 'a.bookmark', function() {
|
||||
const t = $(this).toggleClass("bookmarked");
|
||||
$.post(t.attr("href"));
|
||||
const count = (parseInt(t.text(), 10) || 0) + (t.hasClass("bookmarked") ? 1 : -1);
|
||||
t.find('span').html(count > 0 ? count : "");
|
||||
return false;
|
||||
});
|
||||
|
||||
// still bind esc even in form fields
|
||||
Mousetrap.prototype.stopCallback = function(e, el, combo) {
|
||||
return combo != 'esc' && (el.isContentEditable || el.tagName == 'INPUT' || el.tagName == 'SELECT' || el.tagName == 'TEXTAREA');
|
||||
};
|
||||
Mousetrap.bind('esc', function() {
|
||||
var $oc = $('#modal-wrap .close');
|
||||
if ($oc.length) $oc.trigger('click');
|
||||
else {
|
||||
var $input = $(':focus');
|
||||
if ($input.length) $input.trigger('blur');
|
||||
// still bind esc even in form fields
|
||||
Mousetrap.prototype.stopCallback = function(e, el, combo) {
|
||||
return combo != 'esc' && (el.isContentEditable || el.tagName == 'INPUT' || el.tagName == 'SELECT' || el.tagName == 'TEXTAREA');
|
||||
};
|
||||
Mousetrap.bind('esc', function() {
|
||||
var $oc = $('#modal-wrap .close');
|
||||
if ($oc.length) $oc.trigger('click');
|
||||
else {
|
||||
var $input = $(':focus');
|
||||
if ($input.length) $input.trigger('blur');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!lichess.storage.get('grid')) setTimeout(() => {
|
||||
if (getComputedStyle(document.body).getPropertyValue('--grid'))
|
||||
lichess.storage.set('grid', 1);
|
||||
else
|
||||
$.get(lichess.assetUrl('oops/browser.html'), html => $('body').prepend(html))
|
||||
}, 3000);
|
||||
|
||||
/* A disgusting hack for a disgusting browser
|
||||
* Edge randomly fails to rasterize SVG on page load
|
||||
* A different SVG must be loaded so a new image can be rasterized */
|
||||
if (navigator.userAgent.includes('Edge/')) setTimeout(() => {
|
||||
const sprite = $('#piece-sprite');
|
||||
sprite.attr('href', sprite.attr('href').replace('.css', '.external.css'));
|
||||
}, 1000);
|
||||
|
||||
// prevent zoom when keyboard shows on iOS
|
||||
if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
|
||||
const el = document.querySelector('meta[name=viewport]');
|
||||
el.setAttribute('content', el.getAttribute('content') + ',maximum-scale=1.0');
|
||||
}
|
||||
return false;
|
||||
|
||||
lichess.miniBoard.initAll();
|
||||
lichess.miniGame.initAll();
|
||||
|
||||
$('.chat__members').watchers();
|
||||
|
||||
if (location.hash === '#blind' && !$('body').hasClass('blind-mode'))
|
||||
$.post('/toggle-blind-mode', {
|
||||
enable: 1,
|
||||
redirect: '/'
|
||||
}, lichess.reload);
|
||||
|
||||
lichess.serviceWorker();
|
||||
});
|
||||
|
||||
if (!lichess.storage.get('grid')) setTimeout(() => {
|
||||
if (getComputedStyle(document.body).getPropertyValue('--grid'))
|
||||
lichess.storage.set('grid', 1);
|
||||
else
|
||||
$.get(lichess.assetUrl('oops/browser.html'), html => $('body').prepend(html))
|
||||
}, 3000);
|
||||
|
||||
/* A disgusting hack for a disgusting browser
|
||||
* Edge randomly fails to rasterize SVG on page load
|
||||
* A different SVG must be loaded so a new image can be rasterized */
|
||||
if (navigator.userAgent.includes('Edge/')) setTimeout(() => {
|
||||
const sprite = $('#piece-sprite');
|
||||
sprite.attr('href', sprite.attr('href').replace('.css', '.external.css'));
|
||||
}, 1000);
|
||||
|
||||
// prevent zoom when keyboard shows on iOS
|
||||
if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
|
||||
const el = document.querySelector('meta[name=viewport]');
|
||||
el.setAttribute('content', el.getAttribute('content') + ',maximum-scale=1.0');
|
||||
}
|
||||
|
||||
lichess.miniBoard.initAll();
|
||||
lichess.miniGame.initAll();
|
||||
|
||||
$('.chat__members').watchers();
|
||||
|
||||
if (location.hash === '#blind' && !$('body').hasClass('blind-mode'))
|
||||
$.post('/toggle-blind-mode', {
|
||||
enable: 1,
|
||||
redirect: '/'
|
||||
}, lichess.reload);
|
||||
|
||||
lichess.serviceWorker();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"include": ["src/*.ts", "src/*.js"],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"noImplicitAny": false
|
||||
"noImplicitAny": false,
|
||||
"noImplicitReturns": false
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue