workspace: update eslint rule (#1027)

This commit is contained in:
undefined authored and GitHub committed 2025-08-06 18:51:25 +01:00
1 parent 05c3b8f2bd
commit cee3c00ddb
150 files changed
+1069 -1043

No files matched your search

+10 -10
View File
@@ -1,23 +1,23 @@
/* eslint-disable import/first */
export * from './utils';
export { default as Notification } from './components/notification';
export * from './components/dialog';
export { Context, Service, ctx } from './context';
export { AnsiUp } from 'ansi_up';
export { default as loadMonaco } from './components/monaco/loader';
export { default as Notification } from './components/notification';
export { default as Socket } from './components/socket/index';
export { default as uploadFiles } from './components/upload';
export * as redux from 'react-redux';
export * from './components/zipDownloader';
export * from '@hydrooj/utils/lib/common';
export { default as download } from './components/zipDownloader';
export { Context, ctx, Service } from './context';
export { initPageLoader } from './hydro';
export * from './lazyload';
export * from './misc/Page';
export * from './utils';
export * from '@hydrooj/utils/lib/common';
export { AnsiUp } from 'ansi_up';
export { default as $ } from 'jquery';
export { default as _ } from 'lodash';
export { default as React } from 'react';
export { default as ReactDOM } from 'react-dom/client';
export * from './misc/Page';
export { initPageLoader } from './hydro';
export * from './lazyload';
export * as redux from 'react-redux';
import { load } from './lazyload';
export default load;
@@ -30,7 +30,7 @@ import ProblemSelectAutoComplete from './components/autocomplete/ProblemSelectAu
import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete';
export {
AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete,
AutoComplete, CustomSelectAutoComplete, DomainSelectAutoComplete, ProblemSelectAutoComplete, UserSelectAutoComplete,
};
export function addPage(page: import('./misc/Page').Page | (() => Promise<void> | void)) {
window.Hydro.extraPages.push(page);
+2 -2
View File
@@ -10,7 +10,7 @@ import esbuild from 'esbuild';
declare module 'hydrooj' {
interface UI {
esbuildPlugins?: esbuild.Plugin[]
esbuildPlugins?: esbuild.Plugin[];
}
interface SystemKeys {
'ui-default.nav_logo_dark': string;
@@ -91,7 +91,7 @@ export async function buildUI() {
const entryPoints: string[] = [];
const lazyModules: string[] = [];
const newFiles = ['entry.js'];
for (const addon of Object.values(global.addons)) {
for (const addon of Object.values(global.addons) as string[]) {
let publicPath = resolve(addon, 'frontend');
if (!fs.existsSync(publicPath)) publicPath = resolve(addon, 'public');
if (!fs.existsSync(publicPath)) continue;
@@ -8,7 +8,7 @@ function parseNextNumber(str, pos, max) {
value: '',
};
let code = str.charCodeAt(pos);
while (pos < max && (code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) || code === 0x25 /* % */) {
while (pos < max && ((code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) || code === 0x25 /* % */)) {
code = str.charCodeAt(++pos);
}
result.ok = true;
@@ -24,9 +24,9 @@ function parseImageSize(str, pos, max) {
width: '',
height: '',
};
if (pos >= max) { return result; }
if (pos >= max) return result;
let code = str.charCodeAt(pos);
if (code !== 0x3d /* = */) { return result; }
if (code !== 0x3D /* = */) return result;
pos++;
code = str.charCodeAt(pos);
if (code !== 0x78 /* x */ && (code < 0x30 || code > 0x39) /* [0-9] */) {
@@ -67,14 +67,14 @@ export default function plugin(md) {
const oldPos = state.pos;
const max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) return false;
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) return false;
const labelStart = state.pos + 2;
const labelEnd = md.helpers.parseLinkLabel(state, state.pos + 1, false);
// parser failed to find ']', so it's not a valid link
if (labelEnd < 0) { return false; }
if (labelEnd < 0) return false;
pos = labelEnd + 1;
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
@@ -89,7 +89,7 @@ export default function plugin(md) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
if (pos >= max) { return false; }
if (pos >= max) return false;
// [link]( <href> "title" )
// ^^^^^^ parsing link destination
@@ -162,7 +162,7 @@ export default function plugin(md) {
//
// Link reference
//
if (typeof state.env.references === 'undefined') { return false; }
if (typeof state.env.references === 'undefined') return false;
// [foo] [bar]
// ^^ optional whitespace (can include newlines)
@@ -112,7 +112,7 @@ export default function plugin(md) {
const render = (latex: string, displayMode = false) => {
options.displayMode = displayMode;
try {
latex = latex.replace(/\\def{\\([a-zA-Z0-9]+)}/g, '\\def\\$1');
latex = latex.replace(/\\def\{\\([a-zA-Z0-9]+)\}/g, '\\def\\$1');
return katex.renderToString(latex, options);
} catch (error) {
if (options.throwOnError) console.error(error);
@@ -1,4 +1,7 @@
/* eslint-disable max-len */
/* eslint-disable regexp/prefer-question-quantifier */
/* eslint-disable regexp/no-useless-non-capturing-group */
/* eslint-disable regexp/optimal-quantifier-concatenation */
import type MarkdownIt from 'markdown-it';
import { v4 as uuid } from 'uuid';
@@ -10,6 +13,7 @@ function youtubeParser(url: string) {
const match = url.match(ytRegex);
return match && match[7].length === 11 ? match[7] : url;
}
// eslint-disable-next-line regexp/no-empty-alternative
const vimeoRegex = /https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|)(\d+)(?:$|\/|\?)/;
function vimeoParser(url: string) {
const match = url.match(vimeoRegex);
@@ -25,7 +29,7 @@ function preziParser(url: string) {
const match = url.match(preziRegex);
return match ? match[1] : url;
}
const EMBED_REGEX = /@\[([a-zA-Z].+?)]\((.*?)[)]/im;
const EMBED_REGEX = /@\[([a-zA-Z].+?)\]\((.*?)\)/;
function extractVideoParameters(url: string) {
const parameterMap = new Map();
const params = url.replace(/&amp;/gi, '&').split(/[#?&]/);
@@ -83,7 +87,7 @@ declare module 'hydrooj' {
interface ModuleInterfaces {
richmedia: {
get: (service: string, src: string, md: MarkdownIt) => string | null;
}
};
}
}
@@ -19,7 +19,7 @@ const depedentTags = {
dt: ['dl'],
dd: ['dl'],
};
const whitelistClasses = ['row', 'columns', 'typo', 'note', 'warn'].concat(new Array(12).fill(0).map((_, i) => `medium-${i + 1}`));
const whitelistClasses = ['row', 'columns', 'typo', 'note', 'warn'].concat(Array.from({ length: 12 }).fill(0).map((_, i) => `medium-${i + 1}`));
const tagCheck = new FilterXSS({
css: false,
+1 -1
View File
@@ -1,7 +1,7 @@
import { moment, ObjectId } from 'hydrooj';
import { AnsiUp } from 'ansi_up';
export { size, formatSeconds } from 'hydrooj';
export { formatSeconds, size } from 'hydrooj';
const AU = new AnsiUp();
+3 -3
View File
@@ -30,7 +30,7 @@ async function getFiles(folder: string, base = ''): Promise<string[]> {
files.push(...await getFiles(path.join(folder, i), path.join(base, i)));
} else files.push(path.join(base, i));
}
return files.map((item) => item.replace(/\\/gmi, '/'));
return files.map((item) => item.replace(/\\/g, '/'));
}
function locateFile(basePath: string, filenames: string[]) {
@@ -47,7 +47,7 @@ nunjucks.runtime.memberLookup = function memberLookup(obj, val) {
if (obj === undefined || obj === null) return undefined;
if (typeof obj[val] === 'function') {
const fn = function (...args) {
return obj[val].call(obj, ...args);
return obj[val].call(obj, ...args); // eslint-disable-line no-useless-call
};
fn._original = obj[val];
return fn;
@@ -255,7 +255,7 @@ export class TemplateService extends Service {
const pending = Object.values(global.addons);
const logger = this.ctx.logger('template');
for (const i of pending) {
const p = locateFile(i, ['template', 'templates']);
const p = locateFile(i as string, ['template', 'templates']);
if (p && (await fs.stat(p)).isDirectory()) {
try {
const files = await getFiles(p);
+1 -1
View File
@@ -108,7 +108,7 @@ export default async function (env: { watch?: boolean, production?: boolean, mea
devtool: env.production ? 'source-map' : false,
entry: {
[`hydro-${version}`]: './entry.js',
'sentry': './sentry.ts',
sentry: './sentry.ts',
'default.theme': './theme/default.js',
'service-worker': './service-worker.ts',
},
+1 -2
View File
@@ -1,4 +1,3 @@
/* eslint-disable import/no-import-module-exports */
import path from 'path';
import { size } from '@hydrooj/utils/lib/utils';
import cac from 'cac';
@@ -106,7 +105,7 @@ async function main() {
classNamePrefix: 'icon',
fontName: 'hydro-icons',
css: true,
startUnicode: 0xea01,
startUnicode: 0xEA01,
svg2ttf: {
timestamp: 1577836800, // 2020-1-1
},
@@ -118,7 +118,6 @@ export default class DOMAttachedObject {
}
constructor($dom: JQuery<HTMLElement>, monitorDetach = false) {
// eslint-disable-next-line no-constructor-return
if ($dom == null) return null;
this.$dom = $dom;
this.id = ++DOMAttachedObject.uniqueIdCounter;
@@ -2,7 +2,7 @@ import { CustomSelectAutoComplete as CustomSelectAutoCompleteFC } from '@hydrooj
import AutoComplete, { AutoCompleteOptions } from '.';
interface CustomSelectOptions {
data: any[]
data: any[];
}
export default class CustomSelectAutoComplete<Multi extends boolean> extends AutoComplete<CustomSelectOptions> {
@@ -19,7 +19,7 @@ export interface AutoCompleteOptions<Multi extends boolean = boolean> {
text?: () => string;
}
export default class AutoComplete<Options extends Record<string, any> = {}, Multi extends boolean = boolean> extends DOMAttachedObject {
export default class AutoComplete<Options extends Record<string, any> = object, Multi extends boolean = boolean> extends DOMAttachedObject {
static DOMAttachKey = 'ucwAutoCompleteInstance';
ref = null;
container = document.createElement('div');
@@ -215,7 +215,7 @@ export default class Calendar {
const daysByWeek = _.chunk(days, 7);
const numberOfWeeks = days.length / 7;
const bannersByWeek = _.fill(new Array(numberOfWeeks), 1).map(() => []);
const bannersByWeek = _.fill(Array.from({ length: numberOfWeeks }), 1).map(() => []);
const beginDate = days[0].date.clone();
const endDate = _.last(days).date.clone();
@@ -268,7 +268,7 @@ export default class Calendar {
]))
.map((banners) => {
const dayBitmap = _
.fill(new Array(7), 1)
.fill(Array.from({ length: 7 }), 1)
.map(() => []);
banners.forEach((banner) => {
const beginDay = banner.beginAt.day();
@@ -292,7 +292,7 @@ export default class Calendar {
// merge adjacent cells and arrange banners by vertical index
const vMaxLength = _.max(_.range(0, 7).map((day) => dayBitmap[day].length));
const weekBanners = _
.fill(new Array(vMaxLength), 1)
.fill(Array.from({ length: vMaxLength }), 1)
.map(() => []);
for (let vIndex = 0; vIndex < vMaxLength; ++vIndex) {
let last = { span: 1, banner: dayBitmap[0][vIndex] };
@@ -27,12 +27,14 @@ export default new AutoloadPage('customFont', (pageName) => {
if (!dialog) {
const fonts = await getAvailableFonts();
dialog = new ActionDialog({
$body: $(tpl(<CustomSelectAutoComplete
ref={(el) => { handle = el; }}
allowEmptyQuery={false}
data={fonts}
renderItem={(item) => <div style={{ fontFamily: item.id }}>{item.name}</div>}
/>, true)),
$body: $(tpl(
<CustomSelectAutoComplete
ref={(el) => { handle = el; }}
allowEmptyQuery={false}
data={fonts}
renderItem={(item) => <div style={{ fontFamily: item.id }}>{item.name}</div>}
/>, true,
)),
onDispatch: (action) => {
selection = handle.getSelectedItems()?.[0]?.id;
return action !== 'ok' || selection;
@@ -144,24 +144,29 @@ export async function prompt<T extends string>(title: string, fields: Record<T,
<div className="textbox-container">
{field.type === 'text'
? <input
type="text" className="textbox" data-autofocus={field.autofocus}
type="text"
className="textbox"
data-autofocus={field.autofocus}
defaultValue={field.default}
onChange={(e) => setValues({ ...values, [name]: e.target.value })}
/>
: field.type === 'user'
? <UserSelectAutoComplete
data-autofocus={field.autofocus} selectedKeys={values[name] ? [values[name].toString()] : []}
data-autofocus={field.autofocus}
selectedKeys={values[name] ? [values[name].toString()] : []}
onChange={(e) => setValues({ ...values, [name]: +e })}
byId
/>
: <DomainSelectAutoComplete
data-autofocus={field.autofocus} selectedKeys={values[name] ? [values[name]] : []}
data-autofocus={field.autofocus}
selectedKeys={values[name] ? [values[name]] : []}
onChange={(e) => setValues({ ...values, [name]: e })}
/>}
</div>
</label>}
{field.type === 'checkbox' && <label className="checkbox">
<input type="checkbox"
<input
type="checkbox"
defaultChecked={field.default === 'true'}
onChange={(e) => setValues({ ...values, [name]: !!e.target.checked })}
/>
@@ -62,8 +62,8 @@ function Reaction({ payload, ele }) {
<input name="emojiSuggest" onFocus={() => updateFocus(true)} onBlur={() => updateFocus(false)}></input>
</div>
</div>
</div>
}>
</div>}
>
<span className="icon icon-emoji"></span>
</Popover>
);
@@ -169,9 +169,9 @@ export default class Editor extends DOMAttachedObject {
const [val, setVal] = React.useState(value);
that.setMarkdownEditorValue = setVal;
return <MdEditor
className='textbox'
className="textbox"
autoFocus={hasFocus}
codeTheme='github'
codeTheme="github"
codeStyleReverse={false}
ref={renderCallback}
value={val}
@@ -73,7 +73,7 @@ function addInvisibles(grammar) {
for (const name in grammar) {
if (Object.prototype.hasOwnProperty.call(grammar, name) && !invisibles[name]) {
if (name === 'rest') addInvisibles(grammar.rest);
else handlerInvisiblesToken(grammar, name); // eslint-disable-line @typescript-eslint/no-use-before-define
else handlerInvisiblesToken(grammar, name); // eslint-disable-line ts/no-use-before-define
}
}
}
@@ -23,7 +23,6 @@ class Marker {
}
constructor() {
// eslint-disable-next-line no-constructor-return
if (Marker.exists()) return markerInstance;
if (markerInstance) {
markerInstance.destroy();
@@ -1,4 +1,3 @@
/* eslint-disable no-restricted-globals */
/// <reference no-default-lib="true" />
/// <reference lib="webworker" />
import ReconnectingWebsocket from 'reconnecting-websocket';
@@ -64,10 +63,10 @@ function initConn(path: string, port: MessagePort, cookie: any) {
console.log('Connected');
broadcastMsg({ type: 'open' });
conn.send(JSON.stringify({
'operation': 'subscribe',
'request_id': Math.random().toString(16).substring(2),
'credential': lcookie,
'channels': ['message'],
operation: 'subscribe',
request_id: Math.random().toString(16).substring(2),
credential: lcookie,
channels: ['message'],
}));
};
conn.onerror = () => broadcastMsg({ type: 'error' });
@@ -94,11 +94,11 @@ export default connect(mapStateToProps)(class MessagePadDialogueContentContainer
<>
<div className="messagepad__header">
{ this.props.item
&& (
<a className="messagepad__content__header__title" href={`/user/${this.props.item.udoc._id}`}>
{`${this.props.item.udoc.uname}(UID: ${this.props.item.udoc._id})`}
</a>
)}
&& (
<a className="messagepad__content__header__title" href={`/user/${this.props.item.udoc._id}`}>
{`${this.props.item.udoc.uname}(UID: ${this.props.item.udoc._id})`}
</a>
)}
</div>
<ol className="messagepad__content" ref="list">
{this.renderInner()}
@@ -51,7 +51,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MessagePadInpu
componentDidUpdate(prevProps) {
this.focusInput = (
this.props.activeId !== prevProps.activeId
|| prevProps.isPosting !== this.props.isPosting && this.props.isPosting === false
|| (prevProps.isPosting !== this.props.isPosting && this.props.isPosting === false)
);
if (this.focusInput) {
const { scrollX, scrollY } = window;
+1 -1
View File
@@ -12,7 +12,7 @@ function format(message, args) {
return result;
}
let CURRENT_LOCALE_DATA = {}; // eslint-disable-line @typescript-eslint/naming-convention
let CURRENT_LOCALE_DATA = {}; // eslint-disable-line ts/naming-convention
export function localize(path, message, ...args) {
return format(CURRENT_LOCALE_DATA[path.key || path] || CURRENT_LOCALE_DATA[message] || message, args);
@@ -159,7 +159,7 @@ export async function previewFile(ev?, type = '') {
Notification.info(i18n('Loading file...'));
try {
const { url } = await request.get(link, {}, { headers: { Pragma: 'no-cache' } });
if (/^(doc|xls|ppt)x?$/.test(ext)) return previewOffice(link, url);
if (/^(?:doc|xls|ppt)x?$/.test(ext)) return previewOffice(link, url);
content = await request.get(url, undefined, { dataType: 'text' });
} catch (e) {
Notification.error(i18n('Failed to load file: {0}', e.message));
@@ -24,7 +24,7 @@ const mapDispatchToProps = (dispatch) => ({
interface Props {
config: any;
handleUpdateCode: Function;
handleUpdateCode: (val: string) => void;
}
const configKey = [
@@ -10,7 +10,7 @@ import { ProblemConfigTree } from './ProblemConfigTree';
import { RootState } from './reducer';
interface Props {
onSave: () => void
onSave: () => void;
}
export default function ProblemConfig(props: Props) {
@@ -31,8 +31,12 @@ export default function ProblemConfig(props: Props) {
<Tabs onChange={(t) => (t !== 'errors' && setSelected(t.toString()))} selectedTabId={valid ? selected : 'errors'}>
<Tab id="basic" disabled={!valid} title={i18n('Basic')} panel={<ProblemConfigForm />} />
<Tab id="subtasks" disabled={!valid} title={i18n('Subtasks')} panel={<ProblemConfigTree />} />
<Tab id="errors" disabled={valid} title={errors.length ? `Errors(${errors.length})` : 'No Errors'} panel={
<div>{errors.map((i) => (<pre key={i}>{i}</pre>))}</div>} />
<Tab
id="errors"
disabled={valid}
title={errors.length ? `Errors(${errors.length})` : 'No Errors'}
panel={<div>{errors.map((i) => (<pre key={i}>{i}</pre>))}</div>}
/>
</Tabs>
</div>
</div>
@@ -42,9 +42,9 @@ export function SelectionManager(props: SelectionManagerProps) {
for (const el of selected) {
if (collide(el, { ...pos, width: 1, height: 1 })) return;
}
// eslint-disable-next-line @typescript-eslint/no-use-before-define
// eslint-disable-next-line ts/no-use-before-define
document.body.addEventListener('mousemove', handleMouseMove);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
// eslint-disable-next-line ts/no-use-before-define
document.body.addEventListener('mouseup', handleMouseUp);
$('body').css('cursor', 'crosshair')
.append('<div id="divSelectArea" style="position:absolute;background-color:#e073d4;"></div>');
@@ -30,7 +30,7 @@ export function TestcaseNode(props: TestcaseNodeProps) {
data-selected={selected}
content={
<Menu>
<MenuItem icon="drawer-left" text={i18n('Move to subtask')} >
<MenuItem icon="drawer-left" text={i18n('Move to subtask')}>
{subtaskIds.filter((i) => i !== subtaskId).map((i) => (
<MenuItem key={i} text={i18n('Subtask {0}', i)} />
))}
@@ -56,7 +56,7 @@ export default class Rotator extends DOMAttachedObject {
return;
}
let fromPosition;
if (parseFloat(value) > parseFloat(this.value)) {
if (Number.parseFloat(value) > Number.parseFloat(this.value)) {
fromPosition = POS_BELOW;
} else {
fromPosition = POS_ABOVE;
+12 -6
View File
@@ -20,15 +20,21 @@ interface UploadOptions {
export default async function uploadFiles(endpoint = '', files: File[] | FileList = [], options: UploadOptions = {}) {
const dialog = new Dialog({
$body: $(tpl(<>
<div className="file-label" style={{
textAlign: 'center', marginBottom: '5px', color: 'gray', fontSize: 'small',
}}></div>
<div
className="file-label"
style={{
textAlign: 'center', marginBottom: '5px', color: 'gray', fontSize: 'small',
}}
></div>
<div className={`${Classes.PROGRESS_BAR} ${Classes.INTENT_PRIMARY} bp5-no-stripes`}>
<div className={`file-progress ${Classes.PROGRESS_METER}`} style={{ width: 0 }}></div>
</div>
<div className="upload-label" style={{
textAlign: 'center', margin: '5px 0', color: 'gray', fontSize: 'small',
}}></div>
<div
className="upload-label"
style={{
textAlign: 'center', margin: '5px 0', color: 'gray', fontSize: 'small',
}}
></div>
<div className={`${Classes.PROGRESS_BAR} ${Classes.INTENT_PRIMARY} bp5-no-stripes`}>
<div className={`upload-progress ${Classes.PROGRESS_METER}`} style={{ width: 0 }}></div>
</div>
@@ -84,13 +84,13 @@ export default async function download(filename, targets) {
declare module '../../api' {
interface EventMap {
'problemset/download': (pids: number[], name: string, targets: { filename: string; url?: string; content?: string }[]) => void;
'problemset/download': (pids: number[], name: string, targets: { filename: string, url?: string, content?: string }[]) => void;
}
}
export async function downloadProblemSet(pids, name = 'Export') {
Notification.info(i18n('Downloading...'));
const targets: { filename: string; url?: string; content?: string }[] = [];
const targets: { filename: string, url?: string, content?: string }[] = [];
try {
await ctx.serial('problemset/download', pids, name, targets);
for (const pid of pids) {
@@ -122,7 +122,7 @@ export async function downloadProblemSet(pids, name = 'Export') {
});
try {
const c = JSON.parse(pdoc.content);
if (c instanceof Array || typeof c === 'string') throw new Error();
if (c instanceof Array || typeof c === 'string') throw new Error('Invalid content');
for (const key of Object.keys(c)) {
targets.push({
filename: `${pid}/problem_${key}.md`,
+2
View File
@@ -1,3 +1,5 @@
/* eslint-disable ts/no-unsafe-declaration-merging */
import * as cordis from 'cordis';
import type { EventMap } from './api';
+3 -1
View File
@@ -29,7 +29,9 @@ console.log(
window.UiContext = JSON.parse(window.UiContext);
window.UserContext = JSON.parse(window.UserContext);
try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { }
try {
__webpack_public_path__ = UiContext.cdn_prefix;
} catch (e) { }
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then((registration) => {
console.log('SW registered: ', registration);
+1 -1
View File
@@ -21,7 +21,7 @@ const start = new Date();
function buildSequence(pages, type) {
if (process.env.NODE_ENV !== 'production') {
if (['before', 'after'].indexOf(type) === -1) {
if (!['before', 'after'].includes(type)) {
throw new Error("'type' should be one of 'before' or 'after'");
}
}
+2 -1
View File
@@ -22,7 +22,7 @@ class WikiAboutHandler extends Handler {
async get() {
let raw = SystemModel.get('ui-default.about') || '';
// TODO template engine
raw = raw.replace(/{{ name }}/g, this.domain.ui?.name || SystemModel.get('server.name')).trim();
raw = raw.replace(/\{\{ name \}\}/g, this.domain.ui?.name || SystemModel.get('server.name')).trim();
const lines = raw.split('\n');
const sections: { id: string, title: string, content: string }[] = [];
for (const line of lines) {
@@ -131,6 +131,7 @@ class RichMediaHandler extends Handler {
}
}
/* eslint-disable style/quote-props */
const fontRange = {
'Open Sans': 'Open Sans',
'Seravek': 'Seravek',
+1 -1
View File
@@ -1102,4 +1102,4 @@ Your account has two factor authentication enabled. Please choose an authenticat
Your browser does not support WebAuthn or you are not in secure context.: 您的浏览器不支持WebAuthn或是您不在安全环境中。
Your Device: 你的设备
Your permission: 您的权限
Your role: 您的角色
Your role: 您的角色
+1 -1
View File
@@ -16,7 +16,7 @@ export class Page {
}
if (process.env.NODE_ENV !== 'production') {
if (typeof name !== 'string' && !(name instanceof Array)) {
throw new Error("'name' should be a string or string[]");
throw new TypeError("'name' should be a string or string[]");
}
if (typeof this.afterLoading !== 'function' && this.afterLoading != null) {
throw new Error("'afterLoading' should be a function");
+7 -7
View File
@@ -21,7 +21,7 @@
"@fontsource/source-code-pro": "^5.2.6",
"@fontsource/ubuntu-mono": "^5.2.6",
"@hydrooj/utils": "workspace:^",
"@sentry/browser": "^10.0.0",
"@sentry/browser": "^10.2.0",
"@sentry/webpack-plugin": "^4.0.2",
"@simplewebauthn/browser": "13.1.2",
"@svgr/webpack": "^8.1.0",
@@ -44,14 +44,14 @@
"allotment": "^1.20.4",
"autoprefixer": "^10.4.21",
"browser-update": "^3.3.60",
"chalk": "^5.4.1",
"chalk": "^5.5.0",
"classnames": "^2.5.1",
"clean-webpack-plugin": "^4.0.0",
"clipboard": "^2.0.11",
"copy-webpack-plugin": "^13.0.0",
"cordis": "4.0.0-beta.5",
"core-js": "^3.44.0",
"core-js-compat": "^3.44.0",
"core-js": "^3.45.0",
"core-js-compat": "^3.45.0",
"css-loader": "^7.1.2",
"diff": "^8.0.2",
"diff-dom": "^5.2.0",
@@ -67,7 +67,7 @@
"jquery.transit": "^0.9.12",
"matchmedia-polyfill": "^0.3.2",
"md-editor-rt": "^5.8.4",
"mini-css-extract-plugin": "^2.9.2",
"mini-css-extract-plugin": "^2.9.3",
"moment": "^2.30.1",
"monaco-editor": "0.52.2",
"monaco-editor-nls": "^3.1.0",
@@ -98,7 +98,7 @@
"redux-promise-middleware": "^6.2.0",
"redux-thunk": "^3.1.0",
"rupture": "^0.7.1",
"sass": "^1.89.2",
"sass": "^1.90.0",
"sass-loader": "^16.0.5",
"schemastery": "^3.16.1",
"shorty.js": "^1.0.1",
@@ -125,7 +125,7 @@
"ansi_up": "^6.0.6",
"cssfilter": "^0.0.11",
"esbuild": "0.25.2",
"fs-extra": "^11.3.0",
"fs-extra": "^11.3.1",
"html-to-text": "^9.0.5",
"js-yaml": "^4.1.0",
"jsesc": "^3.1.0",
+4 -4
View File
@@ -24,10 +24,10 @@ export default new NamedPage(['contest_detail', 'contest_problemlist', 'contest_
addSpeculationRules({
prerender: [{
'where': {
'or': [
{ 'href_matches': '/p/*' },
{ 'href_matches': '/d/*/p/*' },
where: {
or: [
{ href_matches: '/p/*' },
{ href_matches: '/d/*/p/*' },
],
},
}],
@@ -37,7 +37,7 @@ function Balloon({ tdoc, val }) {
</td>
<td>
<HexColorInput
className='textbox'
className="textbox"
color={c}
onFocus={() => { setNow(pid); setColor(c); }}
onChange={(e) => { val[+pid].color = e; setColor(e); }}
@@ -194,15 +194,15 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
const ans = {};
const pids = [];
let cnt = 0;
const reg = /{{ (input|select|multiselect|textarea)\(\d+(-\d+)?\) }}/g;
const reg = /\{\{ (input|select|multiselect|textarea)\(\d+(-\d+)?\) \}\}/g;
$('.problem-content .typo').children().each((i, e) => {
if (e.tagName === 'PRE' && !e.children[0].className.includes('#input')) return;
const questions = [];
let q;
while (q = reg.exec(e.innerText)) questions.push(q); // eslint-disable-line no-cond-assign
while (q = reg.exec(e.textContent)) questions.push(q); // eslint-disable-line no-cond-assign
for (const [info, type] of questions) {
cnt++;
const id = info.replace(/{{ (input|select|multiselect|textarea)\((\d+(-\d+)?)\) }}/, '$2');
const id = info.replace(/\{\{ (input|select|multiselect|textarea)\((\d+(-\d+)?)\) \}\}/, '$2');
pids.push(id);
if (type === 'input') {
$(e).html($(e).html().replace(info, tpl`
@@ -37,7 +37,7 @@ async function updateSelection() {
}
}
const requestCategoryTags = _.uniq(selections
.filter((s) => s.indexOf(',') !== -1)
.filter((s) => s.includes(','))
.map((s) => s.split(',')[0]));
// drop the category if its subcategory is selected
const requestTags = _.uniq(_.pullAll(selections, requestCategoryTags));
@@ -267,7 +267,7 @@ export default new NamedPage(['problem_create', 'problem_edit'], () => {
val = JSON.parse(val);
if (!(val instanceof Array)) val = JSON.stringify(val);
} catch { }
const empty = /^\s*$/g.test(val);
const empty = /^\s*$/.test(val);
if (empty) delete content[activeTab];
else content[activeTab] = val;
if (!Object.keys(content).length) $field.text('');
@@ -175,14 +175,21 @@ const page = new NamedPage('problem_files', () => {
style={{ ...style, ...(highlight ? { color: 'transparent', background: 'transparent', caretColor: 'black' } : {}) }}
value={original}
onChange={(e) => setOriginal(e.currentTarget.value)}
></input>
/>
</div>
<div className="textbox-container" style={{
position: 'absolute', top: 0, left: 0, zIndex: 0,
}}>
{highlight && <span className="textbox" style={{
...style, border: 'none', display: 'inline-flex', alignItems: 'center',
}} dangerouslySetInnerHTML={{ __html: highlight }} />}
<div
className="textbox-container"
style={{
position: 'absolute', top: 0, left: 0, zIndex: 0,
}}
>
{highlight && <span
className="textbox"
style={{
...style, border: 'none', display: 'inline-flex', alignItems: 'center',
}}
dangerouslySetInnerHTML={{ __html: highlight }}
/>}
</div>
</div>
</label>
@@ -237,7 +244,7 @@ const page = new NamedPage('problem_files', () => {
</tbody>
</table>
</div>}
</div >;
</div>;
}
const promise = new ActionDialog({
@@ -456,19 +456,20 @@ const page = new NamedPage(['problem_main'], () => {
const selection = document.getElementById('problem_selection');
if (selection) {
ReactDOM.createRoot(selection)
.render(<ProblemSelectionDisplay
ReactDOM.createRoot(selection).render(
<ProblemSelectionDisplay
onChange={(pids) => { selectedPids = pids; }}
onClear={(handler) => { clearSelectionHandler = handler; }}
/>);
/>,
);
}
addSpeculationRules({
prerender: [{
'where': {
'or': [
{ 'href_matches': '/p/*' },
{ 'href_matches': '/d/*/p/*' },
where: {
or: [
{ href_matches: '/p/*' },
{ href_matches: '/d/*/p/*' },
],
},
}],
@@ -49,7 +49,8 @@ const page = new NamedPage(['problem_submit', 'contest_detail_problem_submit', '
root.render(<blockquote className="note">
<p>{i18n('This page is only for pasting code from other sources.')}</p>
<p>{i18n("To get a better editing experience, with code highlighting and test runs, \
please go back to the problem detail page and use 'Open Scratchpad' button.")}</p>
please go back to the problem detail page and use 'Open Scratchpad' button.")}
</p>
<a onClick={() => root.unmount()}>{i18n('Dismiss')}</a> / <a onClick={ignore}>{i18n("Don't show again")}</a>
</blockquote>);
});
+2 -1
View File
@@ -57,7 +57,8 @@ const page = new NamedPage('manage_config', async () => {
.omnibar-toggle {
display: none;
}
`}</style>
`}
</style>
</ComponentsProvider>,
);
});
+1 -2
View File
@@ -1,4 +1,3 @@
/* eslint-disable no-restricted-globals */
/// <reference no-default-lib="true" />
/// <reference lib="webworker" />
export { }; // make it a module so that declare self works
@@ -27,7 +26,7 @@ self.onmessage = (event) => {
const data = event.data;
const downloadUrl = data.url || `${self.registration.scope + Math.random()}/${typeof data === 'string' ? data : data.filename}`;
const port = event.ports[0];
const metadata = new Array(3); // [stream, data, port]
const metadata = Array.from({ length: 3 }); // [stream, data, port]
metadata[1] = data;
metadata[2] = port;
if (event.data.readableStream) {
+2 -2
View File
@@ -23,8 +23,8 @@ const Base64 = {
if (Number.isNaN(chr2)) enc3 = enc4 = 64;
else if (Number.isNaN(chr3)) enc4 = 64;
output = output
+ this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2)
+ this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
+ this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2)
+ this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
+6 -7
View File
@@ -36,7 +36,6 @@ export async function pipeStream(read, write, abort) {
const writer = write.getWriter();
if (abort) abort.abort = writer.abort.bind(writer);
const reader = read.getReader();
// eslint-disable-next-line no-constant-condition
while (1) {
const readResult = await reader.read();
if (readResult.done) {
@@ -51,10 +50,10 @@ export async function pipeStream(read, write, abort) {
export function mongoId(idstring: string) {
if (typeof idstring !== 'string') idstring = String(idstring);
return {
timestamp: parseInt(idstring.slice(0, 0 + 8), 16),
machineid: parseInt(idstring.slice(8, 8 + 6), 16),
pid: parseInt(idstring.slice(14, 14 + 4), 16),
sequence: parseInt(idstring.slice(18, 18 + 6), 16),
timestamp: Number.parseInt(idstring.slice(0, 0 + 8), 16),
machineid: Number.parseInt(idstring.slice(8, 8 + 6), 16),
pid: Number.parseInt(idstring.slice(14, 14 + 4), 16),
sequence: Number.parseInt(idstring.slice(18, 18 + 6), 16),
};
}
@@ -66,12 +65,12 @@ export function emulateAnchorClick(ev: KeyboardEvent, targetUrl: string, alwaysO
else window.location.href = targetUrl;
}
export { default as pjax } from './pjax';
export * from './base';
export { default as base64 } from './base64';
export { default as loadReactRedux } from './loadReactRedux';
export * as mediaQuery from './mediaQuery';
export { default as pjax } from './pjax';
export * from './slide';
export * from './base';
const zip = { createZipStream, createZipBlob };
Object.assign(window.Hydro.utils, {