mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
workspace: update eslint rule (#1027)
This commit is contained in:
1 parent
05c3b8f2bd
commit
cee3c00ddb
150 files changed
+1069
-1043
No files matched your search
+10
-10
@@ -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);
|
||||
|
||||
@@ -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(/&/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,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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable ts/no-unsafe-declaration-merging */
|
||||
|
||||
import * as cordis from 'cordis';
|
||||
import type { EventMap } from './api';
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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: 您的角色
|
||||
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>);
|
||||
});
|
||||
|
||||
@@ -57,7 +57,8 @@ const page = new NamedPage('manage_config', async () => {
|
||||
.omnibar-toggle {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
`}
|
||||
</style>
|
||||
</ComponentsProvider>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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, {
|
||||
|
||||
Reference in new issue
Block a user