145 lines
4.1 KiB
TypeScript
145 lines
4.1 KiB
TypeScript
import { h } from 'snabbdom';
|
|
import { VNode } from 'snabbdom/vnode';
|
|
import LobbyController from '../../ctrl';
|
|
import { Hook } from '../../interfaces';
|
|
import { bind, perfIcons } from '../util';
|
|
|
|
function percents(v) {
|
|
return v + '%';
|
|
}
|
|
|
|
function ratingLog(a) {
|
|
return Math.log(a / 150 + 1);
|
|
}
|
|
|
|
function ratingY(e) {
|
|
const rating = Math.max(1000, Math.min(2200, e || 1500));
|
|
let ratio;
|
|
let mid = 2/5;
|
|
if (rating == 1500) {
|
|
ratio = mid;
|
|
} else if (rating > 1500) {
|
|
ratio = mid + (ratingLog(rating - 1500) / ratingLog(1300)) * 2 * mid;
|
|
} else {
|
|
ratio = mid - (ratingLog(1500 - rating) / ratingLog(500)) * mid;
|
|
}
|
|
return Math.round(ratio * 94);
|
|
}
|
|
|
|
const clockMax = 2000;
|
|
|
|
function clockX(dur) {
|
|
function durLog(a) {
|
|
return Math.log((a - 30) / 200 + 1);
|
|
}
|
|
return Math.round(durLog(Math.min(clockMax, dur || clockMax)) / durLog(clockMax) * 100);
|
|
}
|
|
|
|
function renderPlot(ctrl: LobbyController, hook: Hook) {
|
|
const bottom = Math.max(0, ratingY(hook.rating) - 2),
|
|
left = Math.max(0, clockX(hook.t) - 2),
|
|
klass = [
|
|
'plot.new',
|
|
hook.ra ? 'rated' : 'casual',
|
|
hook.action === 'cancel' ? 'cancel' : ''
|
|
].join('.');
|
|
return h('span#' + hook.id + '.' + klass, {
|
|
key: hook.id,
|
|
attrs: {
|
|
'data-icon': perfIcons[hook.perf],
|
|
style: `bottom:${percents(bottom)};left:${percents(left)}`
|
|
},
|
|
hook: {
|
|
insert(vnode) {
|
|
$(vnode.elm as HTMLElement).powerTip({
|
|
placement: hook.rating > 1800 ? 'se' : 'ne',
|
|
closeDelay: 200,
|
|
popupId: 'hook'
|
|
}).data('powertipjq', $(renderHook(ctrl, hook)))
|
|
.on({
|
|
powerTipRender() {
|
|
$('#hook .inner-clickable').on('click', () => ctrl.clickHook(hook.id));
|
|
}
|
|
});
|
|
setTimeout(function() {
|
|
(vnode.elm as HTMLElement).classList.remove('new');
|
|
}, 20);
|
|
},
|
|
destroy(vnode) {
|
|
$(vnode.elm as HTMLElement).data('powertipjq', null);
|
|
$.powerTip.destroy(vnode.elm as HTMLElement);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderHook(ctrl: LobbyController, hook: Hook): string {
|
|
const color = hook.c || 'random';
|
|
let html = '<div class="inner">';
|
|
if (hook.rating) {
|
|
html += '<a class="opponent ulpt is color-icon ' + color + '" href="/@/' + hook.u + '">';
|
|
html += ' ' + hook.u + ' (' + hook.rating + (hook.prov ? '?' : '') + ')';
|
|
html += '</a>';
|
|
} else {
|
|
html += '<span class="opponent anon ' + color + '">' + ctrl.trans('anonymous') + '</span>';
|
|
}
|
|
html += '<div class="inner-clickable">';
|
|
html += `<div>${hook.clock}</div>`;
|
|
html += '<i data-icon="' + perfIcons[hook.perf] + '"> ' + ctrl.trans(hook.ra ? 'rated' : 'casual') + '</i>';
|
|
html += '</div>';
|
|
html += '</div>';
|
|
return html;
|
|
}
|
|
|
|
const xMarks = [1, 2, 3, 5, 7, 10, 15, 20, 30];
|
|
|
|
function renderXAxis() {
|
|
const tags: VNode[] = [];
|
|
xMarks.forEach(v => {
|
|
const l = clockX(v * 60);
|
|
tags.push(h('span.x.label', {
|
|
attrs: { style: 'left:' + percents(l - 1.5) }
|
|
}, '' + v));
|
|
tags.push(h('div.grid.vert', {
|
|
attrs: { style: 'width:' + percents(l) }
|
|
}));
|
|
});
|
|
return tags;
|
|
}
|
|
|
|
const yMarks = [1000, 1200, 1400, 1500, 1600, 1800, 2000];
|
|
|
|
function renderYAxis() {
|
|
const tags: VNode[] = [];
|
|
yMarks.forEach(function(v) {
|
|
const b = ratingY(v);
|
|
tags.push(h('span.y.label', {
|
|
attrs: { style: 'bottom:' + percents(b + 1) }
|
|
}, '' + v));
|
|
tags.push(h('div.grid.horiz', {
|
|
attrs: { style: 'height:' + percents(b + 0.8) }
|
|
}));
|
|
});
|
|
return tags;
|
|
}
|
|
|
|
export function toggle(ctrl: LobbyController) {
|
|
return h('i.toggle', {
|
|
key: 'set-mode-list',
|
|
attrs: { title: ctrl.trans.noarg('list'), 'data-icon': '?' },
|
|
hook: bind('mousedown', _ => ctrl.setMode('list'), ctrl.redraw)
|
|
});
|
|
}
|
|
|
|
export function render(ctrl: LobbyController, hooks: Hook[]) {
|
|
return h('div.hooks__chart', [
|
|
h('div.canvas', {
|
|
hook: bind('click', e => {
|
|
if ((e.target as HTMLElement).classList.contains('plot')) ctrl.clickHook((e.target as HTMLElement).id);
|
|
}, ctrl.redraw)
|
|
}, hooks.map(hook => renderPlot(ctrl, hook))),
|
|
...renderYAxis(),
|
|
...renderXAxis()
|
|
]);
|
|
}
|