ui: frontend api (#470) [skip-cache]

pull/472/head
undefined 2 years ago committed by GitHub
parent 6d69e88739
commit 96fa6cc7b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,4 @@
dist dist
*.d.ts *.d.ts
node_modules node_modules
public public/**/*.js

@ -3,7 +3,7 @@ extends:
- airbnb-base - airbnb-base
- airbnb-typescript/base - airbnb-typescript/base
env: env:
jquery: true es6: true
globals: globals:
Atomics: readonly Atomics: readonly
SharedArrayBuffer: readonly SharedArrayBuffer: readonly
@ -14,8 +14,26 @@ plugins:
- simple-import-sort - simple-import-sort
- eslint-plugin-import - eslint-plugin-import
ignorePatterns: ignorePatterns:
- public/ - public/**/*.js
- '*.spec.ts' - packages/ui-default
overrides:
- files:
- '**/public/**/*.ts'
- '**/public/**/*.page.js'
rules:
'@typescript-eslint/indent':
- warn
- 2
env:
browser: true
es6: true
jquery: true
globals:
UiContext: true
UserContext: true
externalModules: true
LOCALES: true
LANGS: true
rules: rules:
'@typescript-eslint/no-shadow': 1 '@typescript-eslint/no-shadow': 1

@ -1,5 +1,10 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const child = require('child_process');
if (fs.existsSync('plugins/patch-package/package.json') && fs.existsSync('node_modules/patch-package/package.json')) {
child.execSync('npx patch-package --patch-dir=plugins/patch-package/patches', { stdio: 'inherit' });
}
const dir = path.dirname(path.dirname(require.resolve('@types/node/package.json'))); const dir = path.dirname(path.dirname(require.resolve('@types/node/package.json')));
const types = fs.readdirSync(dir).filter((i) => !['sharedworker', 'serviceworker'].includes(i)); const types = fs.readdirSync(dir).filter((i) => !['sharedworker', 'serviceworker'].includes(i));
@ -51,6 +56,8 @@ const configFlat = (name) => ({
if (!fs.existsSync(path.resolve(process.cwd(), 'plugins'))) { if (!fs.existsSync(path.resolve(process.cwd(), 'plugins'))) {
fs.mkdirSync(path.resolve(process.cwd(), 'plugins')); fs.mkdirSync(path.resolve(process.cwd(), 'plugins'));
// Write an empty file to make eslint happy
fs.writeFileSync(path.resolve(process.cwd(), 'plugins/eslint.ts'), '');
} }
const modules = [ const modules = [

@ -17,8 +17,8 @@
"build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production", "build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production",
"test": "mocha", "test": "mocha",
"benchmark": "cross-env BENCHMARK=true mocha", "benchmark": "cross-env BENCHMARK=true mocha",
"lint": "eslint packages --ext ts --fix", "lint": "eslint packages plugins --ext ts --fix",
"lint:ci": "eslint packages --ext ts", "lint:ci": "eslint packages plugins --ext ts",
"lint:ui": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --fix", "lint:ui": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --fix",
"lint:ui:ci": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx .", "lint:ui:ci": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx .",
"debug": "node --trace-warnings --async-stack-traces --trace-deprecation node_modules/hydrooj/bin/hydrooj --debug --template", "debug": "node --trace-warnings --async-stack-traces --trace-deprecation node_modules/hydrooj/bin/hydrooj --debug --template",

@ -1,82 +1,81 @@
(() => { import { $ } from '@hydrooj/ui-default';
const { AutoloadPage } = window.Hydro;
const { $ } = window.node_modules;
let loaded = false; const { AutoloadPage } = window.Hydro;
async function load() {
if (loaded) return Promise.resolve();
return new Promise((resolve, reject) => {
const scriptElement = document.createElement('script');
scriptElement.src = UiContext.onlyofficeApi;
scriptElement.async = true;
document.head.appendChild(scriptElement);
scriptElement.onload = resolve;
scriptElement.onerror = reject;
loaded = true;
});
}
const loader = mode => async (element) => { let loaded = false;
const id = `${mode}-${Math.random().toString()}`; async function load() {
$(element).attr('id', id); if (loaded) return Promise.resolve();
const url = $(element).text(); return new Promise((resolve, reject) => {
const t = new URL(url, window.location.href).pathname.split('.'); const scriptElement = document.createElement('script');
const n = new URL(url, window.location.href).pathname.split('/'); scriptElement.src = UiContext.onlyofficeApi;
const lang = UserContext.viewLang.includes('_') ? UserContext.viewLang.split('_')[0] : UserContext.viewLang; scriptElement.async = true;
// eslint-disable-next-line no-undef document.head.appendChild(scriptElement);
window.editor = new DocsAPI.DocEditor(id, { scriptElement.onload = resolve;
document: { scriptElement.onerror = reject;
fileType: t[t.length - 1], loaded = true;
key: Math.random().toString(16), });
title: decodeURIComponent(n[n.length - 1]), }
url: new URL(url, window.location.href),
permissions: {
comment: false,
copy: true,
download: true,
edit: false,
fillForms: false,
modifyContentControl: false,
modifyFilter: false,
print: true,
protect: false,
review: false,
},
},
editorConfig: {
lang,
mode: 'view',
user: {
group: "Hydro",
id: UserContext._id.toString(),
name: UserContext.uname,
},
customization: {
chat: false,
comments: false,
help: false,
hideRulers: true,
plugins: false,
}
},
documentType: mode,
height: mode === 'slide' ? '560px' : '900px',
});
}
const getEles = types => { const loader = (mode) => async (element) => {
const eles = []; const id = `${mode}-${Math.random().toString()}`;
for (const type of types) eles.push(...$('div[data-' + type + ']').get()); $(element).attr('id', id);
return eles; const url = $(element).text();
} const t = new URL(url, window.location.href).pathname.split('.');
const n = new URL(url, window.location.href).pathname.split('/');
const lang = UserContext.viewLang.includes('_') ? UserContext.viewLang.split('_')[0] : UserContext.viewLang;
// eslint-disable-next-line no-undef
window.editor = new DocsAPI.DocEditor(id, {
document: {
fileType: t[t.length - 1],
key: Math.random().toString(16),
title: decodeURIComponent(n[n.length - 1]),
url: new URL(url, window.location.href),
permissions: {
comment: false,
copy: true,
download: true,
edit: false,
fillForms: false,
modifyContentControl: false,
modifyFilter: false,
print: true,
protect: false,
review: false,
},
},
editorConfig: {
lang,
mode: 'view',
user: {
group: 'Hydro',
id: UserContext._id.toString(),
name: UserContext.uname,
},
customization: {
chat: false,
comments: false,
help: false,
hideRulers: true,
plugins: false,
},
},
documentType: mode,
height: mode === 'slide' ? '560px' : '900px',
});
};
const page = new AutoloadPage('onlyoffice', async () => { const getEles = (types) => {
let all = getEles(['doc', 'docx', 'cell', 'xls', 'xlsx', 'slide', 'ppt', 'pptx']); const eles = [];
if (all.length) await load(); for (const type of types) eles.push(...$(`div[data-${type}]`).get());
getEles(['doc', 'docx']).forEach(loader('word')); return eles;
getEles(['cell', 'xls', 'xlsx']).forEach(loader('cell')) };
getEles(['slide', 'ppt', 'pptx']).forEach(loader('slide'));
});
window.Hydro.extraPages.push(page); const page = new AutoloadPage('onlyoffice', async () => {
})(); const all = getEles(['doc', 'docx', 'cell', 'xls', 'xlsx', 'slide', 'ppt', 'pptx']);
if (all.length) await load();
getEles(['doc', 'docx']).forEach(loader('word'));
getEles(['cell', 'xls', 'xlsx']).forEach(loader('cell'));
getEles(['slide', 'ppt', 'pptx']).forEach(loader('slide'));
});
window.Hydro.extraPages.push(page);

@ -0,0 +1,62 @@
export * from './utils';
export { default as Notification } from './components/notification';
export * from './components/dialog';
export * as bus from './bus';
export { default as loadMonaco } from './components/monaco/loader';
export * as redux from 'react-redux';
export * from './components/zipDownloader';
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 default async function load(name: string) {
if (window.node_modules[name]) return window.node_modules[name];
if (name === 'echarts') return import('echarts');
if (name === 'moment') return import('moment');
throw new Error(`Module ${name} not found`);
}
import AutoComplete from './components/autocomplete';
import CustomSelectAutoComplete from './components/autocomplete/CustomSelectAutoComplete';
import DomainSelectAutoComplete from './components/autocomplete/DomainSelectAutoComplete';
import ProblemSelectAutoComplete from './components/autocomplete/ProblemSelectAutoComplete';
import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete';
export {
load, AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete,
};
export const { UserContext, UiContext } = window;
export function addPage(page: import('./misc/Page').Page | (() => Promise<void> | void)) {
window.Hydro.extraPages.push(page);
}
declare global {
interface Window {
LANGS: Record<string, any>;
}
let UserContext: Record<string, any>;
let UiContext: Record<string, any>;
}
// Below are old version api compat
/* eslint-disable import/order */
import $ from 'jquery';
import _ from 'lodash';
import React from 'react';
import ReactDOM from 'react-dom/client';
import * as redux from 'react-redux';
const modules = {
_, $, React, redux, ReactDOM, load,
};
declare global {
interface Window {
node_modules: typeof modules;
}
}
Object.assign(window, { node_modules: modules, $, jQuery: $ });

@ -1,4 +1,3 @@
/* eslint-disable */
import path from 'path'; import path from 'path';
export default function root(fn = '.') { export default function root(fn = '.') {

@ -1,4 +1,5 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import type * as monaco from 'monaco-editor';
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
const _hooks: Record<keyof any, Array<(...args: any[]) => any>> = {}; const _hooks: Record<keyof any, Array<(...args: any[]) => any>> = {};
@ -11,7 +12,7 @@ export type Disposable = () => void;
export type VoidReturn = Promise<any> | any; export type VoidReturn = Promise<any> | any;
export interface EventMap extends Record<string, any> { export interface EventMap extends Record<string, any> {
'scratchpadEditorCreate': (editor, monaco) => any; 'scratchpadEditorCreate': (editor: monaco.editor.IStandaloneCodeEditor, monaco: typeof import('monaco-editor')) => any;
} }
function getHooks<K extends keyof EventMap>(name: K) { function getHooks<K extends keyof EventMap>(name: K) {

@ -23,7 +23,7 @@ function monitorResource(resource) {
} }
} }
export default class DOMAttachedObject { class DOMAttachedObject {
static uniqueIdCounter = 0; static uniqueIdCounter = 0;
static DOMAttachKey: string; static DOMAttachKey: string;
static DOMAttachSelector: string; static DOMAttachSelector: string;
@ -127,3 +127,5 @@ export default class DOMAttachedObject {
if (monitorDetach) monitorResource(this); if (monitorDetach) monitorResource(this);
} }
} }
export default DOMAttachedObject as typeof DOMAttachedObject & DOMAttachedObject;

@ -1,7 +1,7 @@
import type { DomainDoc } from 'hydrooj/src/interface'; import type { DomainDoc } from 'hydrooj/src/interface';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete'; import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete';
const DomainSelectAutoComplete = forwardRef<AutoCompleteHandle<DomainDoc>, AutoCompleteProps<DomainDoc>>((props, ref) => ( const DomainSelectAutoComplete = forwardRef<AutoCompleteHandle<DomainDoc>, AutoCompleteProps<DomainDoc>>((props, ref) => (

@ -1,8 +1,7 @@
import type { ProblemDoc } from 'hydrooj/src/interface'; import type { ProblemDoc } from 'hydrooj/src/interface';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import api, { gql } from 'vj/utils/api'; import { api, gql, request } from 'vj/utils';
import request from 'vj/utils/request';
import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete'; import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete';
const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, AutoCompleteProps<ProblemDoc>>((props, ref) => ( const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, AutoCompleteProps<ProblemDoc>>((props, ref) => (

@ -1,7 +1,7 @@
import type { Udoc } from 'hydrooj/src/interface'; import type { Udoc } from 'hydrooj/src/interface';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import api, { gql } from 'vj/utils/api'; import { api, gql } from 'vj/utils';
import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete'; import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete';
// eslint-disable-next-line prefer-arrow-callback // eslint-disable-next-line prefer-arrow-callback

@ -1,6 +1,5 @@
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
import { InfoDialog } from './dialog'; import { InfoDialog } from './dialog';
function isSupported() { function isSupported() {

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import tpl from 'vj/utils/tpl'; import { tpl } from 'vj/utils';
export default class Calendar { export default class Calendar {
constructor(events) { constructor(events) {

@ -1,9 +1,7 @@
import Clipboard from 'clipboard'; import Clipboard from 'clipboard';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import base64 from 'vj/utils/base64'; import { base64, i18n, substitute } from 'vj/utils';
import i18n from 'vj/utils/i18n';
import substitute from 'vj/utils/substitute';
export default new AutoloadPage('clipboard', () => { export default new AutoloadPage('clipboard', () => {
$('[data-copy]').get().forEach((el) => { $('[data-copy]').get().forEach((el) => {

@ -1,9 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import { delay, i18n, request } from 'vj/utils';
import i18n from 'vj/utils/i18n';
import request from 'vj/utils/request';
const contestPage = new AutoloadPage('contestPage', () => { const contestPage = new AutoloadPage('contestPage', () => {
$('[data-contest-code]').on('click', (ev) => { $('[data-contest-code]').on('click', (ev) => {

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import zIndexManager from 'vj/utils/zIndexManager'; import { zIndexManager } from 'vj/utils';
export interface DialogOptions { export interface DialogOptions {
classes: string; classes: string;

@ -1,6 +1,5 @@
import $ from 'jquery'; import $ from 'jquery';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
import DomDialog, { DialogOptions } from './DomDialog'; import DomDialog, { DialogOptions } from './DomDialog';
export class Dialog { export class Dialog {

@ -3,7 +3,7 @@ import _ from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import TextareaHandler from 'vj/components/editor/textareaHandler'; import TextareaHandler from 'vj/components/editor/textareaHandler';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
let initialized = false; let initialized = false;
const $template = $('.dczcomments__box').eq(0).clone(); const $template = $('.dczcomments__box').eq(0).clone();

@ -4,11 +4,9 @@ import $ from 'jquery';
import { ConfirmDialog } from 'vj/components/dialog'; import { ConfirmDialog } from 'vj/components/dialog';
import CommentBox from 'vj/components/discussion/CommentBox'; import CommentBox from 'vj/components/discussion/CommentBox';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import {
import i18n from 'vj/utils/i18n'; delay, i18n, request, slideDown, slideUp, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import { slideDown, slideUp } from 'vj/utils/slide';
import tpl from 'vj/utils/tpl';
const $replyTemplate = $('.commentbox-container').eq(0).clone(); const $replyTemplate = $('.commentbox-container').eq(0).clone();

@ -5,9 +5,7 @@ import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import TimeAgo from 'timeago-react'; import TimeAgo from 'timeago-react';
import { InfoDialog } from 'vj/components/dialog'; import { InfoDialog } from 'vj/components/dialog';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, request, tpl } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
async function historyDialog(payload, time, uid) { async function historyDialog(payload, time, uid) {
const ts = new Date(time).getTime(); const ts = new Date(time).getTime();

@ -6,7 +6,7 @@ import { chunk } from 'lodash';
import * as React from 'react'; import * as React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
function renderReactions(reactions, self, rootEle) { function renderReactions(reactions, self, rootEle) {
let html = ''; let html = '';

@ -2,8 +2,7 @@ import _ from 'lodash';
import Drop from 'tether-drop'; import Drop from 'tether-drop';
import responsiveCutoff from 'vj/breakpoints.json'; import responsiveCutoff from 'vj/breakpoints.json';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import { isBelow } from 'vj/utils/mediaQuery'; import { mediaQuery, zIndexManager } from 'vj/utils';
import zIndexManager from 'vj/utils/zIndexManager';
export default class Dropdown extends DOMAttachedObject { export default class Dropdown extends DOMAttachedObject {
static DOMAttachKey = 'vjDropdownInstance'; static DOMAttachKey = 'vjDropdownInstance';
@ -12,7 +11,7 @@ export default class Dropdown extends DOMAttachedObject {
constructor($dom, options = {}) { constructor($dom, options = {}) {
if ($dom.attr('data-dropdown-trigger-desktop-only') !== undefined) { if ($dom.attr('data-dropdown-trigger-desktop-only') !== undefined) {
if (isBelow(responsiveCutoff.mobile)) { if (mediaQuery.isBelow(responsiveCutoff.mobile)) {
super(null); super(null);
return; return;
} }

@ -1,5 +1,5 @@
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import { delay } from 'vj/utils';
import CmEditor from '.'; import CmEditor from '.';
function runSubstitute($container: JQuery<Document | HTMLElement>) { function runSubstitute($container: JQuery<Document | HTMLElement>) {

@ -3,8 +3,7 @@ import _ from 'lodash';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import i18n from 'vj/utils/i18n'; import { i18n, request } from 'vj/utils';
import request from 'vj/utils/request';
export const config = { export const config = {
toolbar: [ toolbar: [

@ -1,6 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import { delay } from 'vj/utils';
const formPage = new AutoloadPage('formPage', () => { const formPage = new AutoloadPage('formPage', () => {
$(document).on('vjFormDisableUpdate', 'input, select, textarea', (ev) => { $(document).on('vjFormDisableUpdate', 'input, select, textarea', (ev) => {

@ -1,7 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const highlighterPage = new AutoloadPage('highlighterPage', () => { const highlighterPage = new AutoloadPage('highlighterPage', () => {
import('./prismjs').then(({ default: prismjs }) => { import('./prismjs').then(({ default: prismjs }) => {

@ -11,7 +11,7 @@ import $ from 'jquery';
import components from 'prismjs/components'; import components from 'prismjs/components';
import getLoader from 'prismjs/dependencies'; import getLoader from 'prismjs/dependencies';
import Notification from 'vj/components/notification/index'; import Notification from 'vj/components/notification/index';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import languageMeta from './meta'; import languageMeta from './meta';
const files = require.context('prismjs/components/', true, /prism-[a-z0-9-]+\.js/); const files = require.context('prismjs/components/', true, /prism-[a-z0-9-]+\.js/);

@ -1,7 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { InfoDialog } from 'vj/components/dialog'; import { InfoDialog } from 'vj/components/dialog';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
export default function createHint(message: string, element?: any) { export default function createHint(message: string, element?: any) {
if (i18n(message) === message || !element) return; if (i18n(message) === message || !element) return;

@ -1,8 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, request, tpl } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
export default new NamedPage('homepage', () => { export default new NamedPage('homepage', () => {
function getHitokoto($containers) { function getHitokoto($containers) {

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
export default function LanguageSelect({ export default function LanguageSelect({
fieldSelector, firstLoadMain, firstLoadSub, availableLangs, mainLangs, fieldSelector, firstLoadMain, firstLoadSub, availableLangs, mainLangs,

@ -1,8 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import Tooltip from 'vj/components/tooltip/Tooltip'; import Tooltip from 'vj/components/tooltip/Tooltip';
import delay from 'vj/utils/delay'; import { delay, i18n } from 'vj/utils';
import i18n from 'vj/utils/i18n';
const MARKER_ID = `marker_${Math.floor(Math.random() * 0xFFFFFF).toString(16)}`; const MARKER_ID = `marker_${Math.floor(Math.random() * 0xFFFFFF).toString(16)}`;
const MARKER_OFFSET = 20; const MARKER_OFFSET = 20;

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import delay from 'vj/utils/delay'; import { delay } from 'vj/utils';
import Marker from './Marker'; import Marker from './Marker';
export default class MarkerReactive extends DOMAttachedObject { export default class MarkerReactive extends DOMAttachedObject {

@ -1,6 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
export default new AutoloadPage('media', async () => { export default new AutoloadPage('media', async () => {
async function parseMedia($dom = $(document.body)) { async function parseMedia($dom = $(document.body)) {

@ -1,6 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import tpl from 'vj/utils/tpl'; import { tpl } from 'vj/utils';
const menuHeadingPage = new AutoloadPage('menuHeadingPage', null, () => { const menuHeadingPage = new AutoloadPage('menuHeadingPage', null, () => {
$('[data-heading-extract-to]').get().forEach((container) => { $('[data-heading-extract-to]').get().forEach((container) => {

@ -1,7 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import { delay, slideDown } from 'vj/utils';
import { slideDown } from 'vj/utils/slide';
function expandMenu($menu) { function expandMenu($menu) {
slideDown($menu, 500, { opacity: 0 }, { opacity: 1 }); slideDown($menu, 500, { opacity: 0 }, { opacity: 1 });

@ -4,8 +4,7 @@ import { InfoDialog } from 'vj/components/dialog';
import VjNotification from 'vj/components/notification/index'; import VjNotification from 'vj/components/notification/index';
import { FLAG_ALERT } from 'vj/constant/message'; import { FLAG_ALERT } from 'vj/constant/message';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const onmessage = (msg) => { const onmessage = (msg) => {
console.log('Received message', msg); console.log('Received message', msg);

@ -6,8 +6,7 @@ import moment from 'moment';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import TimeAgo from 'timeago-react'; import TimeAgo from 'timeago-react';
import i18n from 'vj/utils/i18n'; import { i18n, mongoId } from 'vj/utils';
import { parse as parseMongoId } from 'vj/utils/mongoId';
import Message from './MessageComponent'; import Message from './MessageComponent';
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
@ -70,7 +69,7 @@ export default connect(mapStateToProps)(class MessagePadDialogueContentContainer
renderInner() { renderInner() {
if (this.props.activeId === null) return []; if (this.props.activeId === null) return [];
const sorted = this.props.item.messages const sorted = this.props.item.messages
.sort((msg1, msg2) => parseMongoId(msg1._id).timestamp - parseMongoId(msg2._id).timestamp); .sort((msg1, msg2) => mongoId(msg1._id).timestamp - mongoId(msg2._id).timestamp);
return sorted.map((msg) => ( return sorted.map((msg) => (
<Message <Message
key={msg._id} key={msg._id}
@ -82,8 +81,8 @@ export default connect(mapStateToProps)(class MessagePadDialogueContentContainer
} }
> >
<div>{this.renderContent(msg)}</div> <div>{this.renderContent(msg)}</div>
<time data-tooltip={moment(parseMongoId(msg._id).timestamp * 1000).format('YYYY-MM-DD HH:mm:ss')}> <time data-tooltip={moment(mongoId(msg._id).timestamp * 1000).format('YYYY-MM-DD HH:mm:ss')}>
<TimeAgo datetime={parseMongoId(msg._id).timestamp * 1000} locale={i18n('timeago_locale')} /> <TimeAgo datetime={mongoId(msg._id).timestamp * 1000} locale={i18n('timeago_locale')} />
</time> </time>
</Message> </Message>
)); ));

@ -4,7 +4,7 @@ import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import ListItem from './DialogueListItemComponent'; import ListItem from './DialogueListItemComponent';
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Icon from 'vj/components/react/IconComponent'; import Icon from 'vj/components/react/IconComponent';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
activeId: state.activeId, activeId: state.activeId,

@ -2,8 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Icon from 'vj/components/react/IconComponent'; import Icon from 'vj/components/react/IconComponent';
import i18n from 'vj/utils/i18n'; import { i18n, request } from 'vj/utils';
import request from 'vj/utils/request';
import MessagePadDialogueContent from './MessagePadDialogueContentContainer'; import MessagePadDialogueContent from './MessagePadDialogueContentContainer';
import MessagePadDialogueList from './MessagePadDialogueListContainer'; import MessagePadDialogueList from './MessagePadDialogueListContainer';
import MessagePadInput from './MessagePadInputContainer'; import MessagePadInput from './MessagePadInputContainer';

@ -6,8 +6,7 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import { IQuickInputService } from 'monaco-editor/esm/vs/platform/quickinput/common/quickInput'; import { IQuickInputService } from 'monaco-editor/esm/vs/platform/quickinput/common/quickInput';
import list from 'monaco-themes/themes/themelist.json'; import list from 'monaco-themes/themes/themelist.json';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import i18n from 'vj/utils/i18n'; import { i18n, request } from 'vj/utils';
import request from 'vj/utils/request';
export default monaco; export default monaco;
export const customOptions: monaco.editor.IStandaloneDiffEditorConstructionOptions = JSON.parse(localStorage.getItem('editor.config') || '{}'); export const customOptions: monaco.editor.IStandaloneDiffEditorConstructionOptions = JSON.parse(localStorage.getItem('editor.config') || '{}');

@ -1,7 +1,7 @@
import keyword from 'emojis-keywords'; import keyword from 'emojis-keywords';
import list from 'emojis-list'; import list from 'emojis-list';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import api, { gql } from 'vj/utils/api'; import { api, gql } from 'vj/utils';
const qqEmojies = [ const qqEmojies = [
'weixiao', 'weixiao',

@ -1,4 +1,4 @@
import loadExternalModule from 'vj/utils/loadModule'; import { loadExternalModule } from 'vj/utils';
let loaded; let loaded;
const loaders = { const loaders = {

@ -3,9 +3,7 @@ import Slideout from 'slideout';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import selectUser from 'vj/components/selectUser'; import selectUser from 'vj/components/selectUser';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, request, tpl } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
function handleNavLogoutClick(ev) { function handleNavLogoutClick(ev) {
const $logoutLink = $(ev.currentTarget); const $logoutLink = $(ev.currentTarget);

@ -1,7 +1,6 @@
import { Intent, Position, Toaster } from '@blueprintjs/core'; import { Intent, Position, Toaster } from '@blueprintjs/core';
import $ from 'jquery'; import $ from 'jquery';
import tpl from 'vj/utils/tpl'; import { tpl, zIndexManager } from 'vj/utils/base';
import zIndexManager from 'vj/utils/zIndexManager';
const ToasterContainer = document.createElement('div'); const ToasterContainer = document.createElement('div');
ToasterContainer.style.position = 'fixed'; ToasterContainer.style.position = 'fixed';

@ -1,6 +1,6 @@
import Notification from 'vj/components/notification/index'; import Notification from 'vj/components/notification/index';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
export default new AutoloadPage('notificationPage', (pagename) => { export default new AutoloadPage('notificationPage', (pagename) => {
const message = i18n(`Hint::Page::${pagename}`); const message = i18n(`Hint::Page::${pagename}`);

@ -4,10 +4,9 @@ import { nanoid } from 'nanoid';
import { ActionDialog, InfoDialog } from 'vj/components/dialog/index'; import { ActionDialog, InfoDialog } from 'vj/components/dialog/index';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import {
import pjax from 'vj/utils/pjax'; i18n, pjax, request, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
async function startEdit(filename, value, fileCategory = 'file') { async function startEdit(filename, value, fileCategory = 'file') {
const { default: Editor } = await import('vj/components/editor/index'); const { default: Editor } = await import('vj/components/editor/index');

@ -1,8 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import { InfoDialog } from 'vj/components/dialog'; import { InfoDialog } from 'vj/components/dialog';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
export default new NamedPage(['problem_create', 'problem_edit'], () => { export default new NamedPage(['problem_create', 'problem_edit'], () => {
$('input[name="pid"]').on('blur', () => { $('input[name="pid"]').on('blur', () => {

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete'; import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete';
import type { RootState } from './reducer/index'; import type { RootState } from './reducer/index';

@ -3,7 +3,7 @@ import {
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import React from 'react'; import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete'; import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete';
import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete'; import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete';
import { FormItem } from './BasicForm'; import { FormItem } from './BasicForm';

@ -3,7 +3,7 @@ import {
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import React from 'react'; import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import { FormItem, ManagedSelect, SingleFileSelect } from './BasicForm'; import { FormItem, ManagedSelect, SingleFileSelect } from './BasicForm';
import type { RootState } from './reducer/index'; import type { RootState } from './reducer/index';

@ -4,7 +4,7 @@ import type { SubtaskConfig } from 'hydrooj/src/interface';
import { isEqual, pick } from 'lodash'; import { isEqual, pick } from 'lodash';
import React from 'react'; import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete'; import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete';
import { FormItem } from './BasicForm'; import { FormItem } from './BasicForm';
import { RootState } from './reducer'; import { RootState } from './reducer';

@ -4,7 +4,7 @@ import type { SubtaskConfig } from 'hydrooj/src/interface';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete'; import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete';
import type { RootState } from './reducer/index'; import type { RootState } from './reducer/index';

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import delay from 'vj/utils/delay'; import { delay } from 'vj/utils';
const ANIMATION_DURATION = 4000; const ANIMATION_DURATION = 4000;

@ -2,7 +2,7 @@ import AnsiUp from 'ansi_up';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Icon from 'vj/components/react/IconComponent'; import Icon from 'vj/components/react/IconComponent';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import DataInput from './DataInputComponent'; import DataInput from './DataInputComponent';
import Panel from './PanelComponent'; import Panel from './PanelComponent';

@ -2,7 +2,7 @@ import classNames from 'classnames';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Icon from 'vj/components/react/IconComponent'; import Icon from 'vj/components/react/IconComponent';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
import Panel from './PanelComponent'; import Panel from './PanelComponent';
import ScratchpadRecordsRow from './ScratchpadRecordsRowContainer'; import ScratchpadRecordsRow from './ScratchpadRecordsRowContainer';

@ -5,10 +5,9 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import TimeAgo from 'timeago-react'; import TimeAgo from 'timeago-react';
import * as recordEnum from 'vj/constant/record'; import * as recordEnum from 'vj/constant/record';
import emulateAnchorClick from 'vj/utils/emulateAnchorClick'; import {
import i18n from 'vj/utils/i18n'; emulateAnchorClick, i18n, mongoId, substitute,
import { parse as parseMongoId } from 'vj/utils/mongoId'; } from 'vj/utils';
import substitute from 'vj/utils/substitute';
const shouldShowDetail = (data) => recordEnum.STATUS_SCRATCHPAD_SHOW_DETAIL_FLAGS[data.status]; const shouldShowDetail = (data) => recordEnum.STATUS_SCRATCHPAD_SHOW_DETAIL_FLAGS[data.status];
@ -58,7 +57,7 @@ export default connect(mapStateToProps, null, mergeProps)(class ScratchpadRecord
render() { render() {
const { data } = this.props; const { data } = this.props;
const submitAt = parseMongoId(data._id).timestamp * 1000; const submitAt = mongoId(data._id).timestamp * 1000;
// Is pretest // Is pretest
return data.contest?.toString() === '000000000000000000000000' ? null : ( return data.contest?.toString() === '000000000000000000000000' ? null : (
<tr onClick={(ev) => this.handleRowClick(ev, data._id)}> <tr onClick={(ev) => this.handleRowClick(ev, data._id)}>

@ -4,9 +4,7 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Icon from 'vj/components/react/IconComponent'; import Icon from 'vj/components/react/IconComponent';
import getAvailableLangs from 'vj/utils/availableLangs'; import { getAvailableLangs, i18n, request } from 'vj/utils';
import i18n from 'vj/utils/i18n';
import request from 'vj/utils/request';
import Toolbar, { import Toolbar, {
ToolbarButtonComponent as ToolbarButton, ToolbarButtonComponent as ToolbarButton,
ToolbarItemComponent as ToolbarItem, ToolbarItemComponent as ToolbarItem,

@ -1,5 +1,5 @@
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
export default function reducer(state = { export default function reducer(state = {
main: { main: {

@ -1,7 +1,6 @@
import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete'; import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete';
import { ActionDialog } from 'vj/components/dialog'; import { ActionDialog } from 'vj/components/dialog';
import i18n from 'vj/utils/i18n'; import { i18n, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
import createHint from './hint'; import createHint from './hint';
let hintInserted = false; let hintInserted = false;

@ -3,9 +3,7 @@ import responsiveCutoff from 'vj/breakpoints.json';
import DomDialog from 'vj/components/dialog/DomDialog'; import DomDialog from 'vj/components/dialog/DomDialog';
import { InfoDialog } from 'vj/components/dialog/index'; import { InfoDialog } from 'vj/components/dialog/index';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, mediaQuery, tpl } from 'vj/utils';
import { isBelow } from 'vj/utils/mediaQuery';
import tpl from 'vj/utils/tpl';
const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => { const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
const signInDialog = DomDialog.getOrConstruct($('.dialog--signin'), { const signInDialog = DomDialog.getOrConstruct($('.dialog--signin'), {
@ -17,7 +15,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
if ($('[name="nav_login"]').length > 0) { if ($('[name="nav_login"]').length > 0) {
// nav // nav
$('[name="nav_login"]').on('click', (ev) => { $('[name="nav_login"]').on('click', (ev) => {
if (isBelow(responsiveCutoff.mobile)) return; if (mediaQuery.isBelow(responsiveCutoff.mobile)) return;
if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return; if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return;
signInDialog.show(); signInDialog.show();
ev.preventDefault(); ev.preventDefault();
@ -32,7 +30,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
} }
window.showSignInDialog = () => { window.showSignInDialog = () => {
if (isBelow(responsiveCutoff.mobile)) { if (mediaQuery.isBelow(responsiveCutoff.mobile)) {
if ($('[name="nav_login"]').length > 0) { if ($('[name="nav_login"]').length > 0) {
window.location.href = $('[name="nav_login"]').attr('href'); window.location.href = $('[name="nav_login"]').attr('href');
return; return;

@ -1,6 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
function setStarButtonState($starButton, star) { function setStarButtonState($starButton, star) {
if (star) { if (star) {

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import * as timeago from 'timeago.js'; import * as timeago from 'timeago.js';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n } from 'vj/utils';
try { try {
const locales = require.context('timeago.js/lib/lang', false, /\.js$/); const locales = require.context('timeago.js/lib/lang', false, /\.js$/);

@ -1,9 +1,8 @@
import { Dialog } from 'vj/components/dialog/index'; import { Dialog } from 'vj/components/dialog/index';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import delay from 'vj/utils/delay'; import {
import i18n from 'vj/utils/i18n'; delay, i18n, pjax, request,
import pjax from 'vj/utils/pjax'; } from 'vj/utils';
import request from 'vj/utils/request';
function onBeforeUnload(e) { function onBeforeUnload(e) {
e.returnValue = ''; e.returnValue = '';

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import Rotator from 'vj/components/rotator'; import Rotator from 'vj/components/rotator';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
function setVoteState($container, value, status) { function setVoteState($container, value, status) {
const $num = $container.find('.vote-number'); const $num = $container.find('.vote-number');

@ -2,11 +2,9 @@ import { dump } from 'js-yaml';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import streamsaver from 'streamsaver'; import streamsaver from 'streamsaver';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import api, { gql } from 'vj/utils/api'; import {
import i18n from 'vj/utils/i18n'; api, createZipStream, gql, i18n, pipeStream, request,
import pipeStream from 'vj/utils/pipeStream'; } from 'vj/utils';
import request from 'vj/utils/request';
import { createZipStream } from 'vj/utils/zip';
let isBeforeUnloadTriggeredByLibrary = !window.isSecureContext; let isBeforeUnloadTriggeredByLibrary = !window.isSecureContext;
function onBeforeUnload(e) { function onBeforeUnload(e) {
@ -29,8 +27,8 @@ export default async function download(filename, targets) {
await waitForWritableStream; await waitForWritableStream;
const fileStream = streamsaver.createWriteStream(filename); const fileStream = streamsaver.createWriteStream(filename);
const queue = new PQueue({ concurrency: 5 }); const queue = new PQueue({ concurrency: 5 });
const abortCallbackReceiver = {}; const abortCallbackReceiver: any = {};
function stopDownload() { abortCallbackReceiver.abort(); } function stopDownload() { abortCallbackReceiver.abort?.(); }
let i = 0; let i = 0;
async function downloadFile(target) { async function downloadFile(target) {
try { try {

@ -46,10 +46,11 @@ document.addEventListener('DOMContentLoaded', async () => {
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { } try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { }
const [data] = await Promise.all([ const [data, HydroExports] = await Promise.all([
(await fetch(`/constant/${UiContext.constantVersion}`, { cache: 'force-cache' })).json(), fetch(`/constant/${UiContext.constantVersion}`, { cache: 'force-cache' }).then((r) => r.json()),
await import('./modules'), import('./api'),
]); ]);
Object.assign(window, { HydroExports });
eval(data[0]); // eslint-disable-line no-eval eval(data[0]); // eslint-disable-line no-eval
data.shift(); data.shift();
window.Hydro.preload = data; window.Hydro.preload = data;

@ -4,16 +4,15 @@ import $ from 'jquery';
import _ from 'lodash'; import _ from 'lodash';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import PageLoader from 'vj/misc/PageLoader'; import PageLoader from 'vj/misc/PageLoader';
import delay from 'vj/utils/delay'; import { delay } from 'vj/utils';
declare global { declare global {
interface Window { interface Window {
UserContext: any; UserContext: any;
UiContext: any; UiContext: any;
Hydro: any; Hydro: any;
// eslint-disable-next-line camelcase
node_modules: any;
externalModules: Record<string, string>; externalModules: Record<string, string>;
captureException?: (e: Error) => void;
} }
} }

@ -51,7 +51,22 @@ export async function buildUI() {
target: [ target: [
'chrome60', 'chrome60',
], ],
plugins: global.Hydro.ui.esbuildPlugins || [], plugins: [
...(global.Hydro.ui.esbuildPlugins || []),
{
name: 'federation',
setup(b) {
b.onResolve({ filter: /^@hydrooj\/ui-default/ }, () => ({
path: 'api',
namespace: 'ui-default',
}));
b.onLoad({ filter: /.*/, namespace: 'ui-default' }, () => ({
contents: 'module.exports = window.HydroExports;',
loader: 'tsx',
}));
},
},
],
minify: !process.env.DEV, minify: !process.env.DEV,
}); });
if (build.errors.length) console.error(build.errors); if (build.errors.length) console.error(build.errors);

@ -1,37 +0,0 @@
import './utils/delay';
import './utils/emulateAnchorClick';
import './utils/i18n';
import './utils/loadReactRedux';
import './utils/mediaQuery';
import './utils/mongoId';
import './utils/pipeStream';
import './utils/pjax';
import './utils/request';
import './utils/slide';
import './utils/substitute';
import './utils/tpl';
import './utils/zIndexManager';
import './utils/zip';
import './components/autocomplete';
import './components/dialog';
import './components/notification';
import './components/monaco/loader';
import $ from 'jquery';
import _ from 'lodash';
import React from 'react';
import ReactDOM from 'react-dom/client';
import * as redux from 'react-redux';
const modules = {
_, $, React, redux, ReactDOM,
};
export default async function load(name) {
if (modules[name]) return modules[name];
if (name === 'echarts') return import('echarts');
if (name === 'moment') return import('moment');
throw new Error(`Module ${name} not found`);
}
window.node_modules = { ...modules, load };
window.$ = $;
window.jQuery = $;

@ -4,6 +4,7 @@
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "index.ts", "main": "index.ts",
"types": "api.ts",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",
"preferUnplugged": true, "preferUnplugged": true,
"scripts": { "scripts": {
@ -29,6 +30,7 @@
"@types/markdown-it": "^12.2.3", "@types/markdown-it": "^12.2.3",
"@types/pickadate": "^3.5.32", "@types/pickadate": "^3.5.32",
"@types/qrcode": "^1.5.0", "@types/qrcode": "^1.5.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9", "@types/react-dom": "^18.0.9",
"@types/redux-logger": "^3.0.9", "@types/redux-logger": "^3.0.9",
"@types/serviceworker": "^0.0.57", "@types/serviceworker": "^0.0.57",

@ -2,7 +2,7 @@ import { pick } from 'lodash';
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
const defaultQuery = `\ const defaultQuery = `\
query Example( query Example(

@ -1,7 +1,7 @@
import { formatSeconds } from '@hydrooj/utils/lib/common'; import { formatSeconds } from '@hydrooj/utils/lib/common';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import tpl from 'vj/utils/tpl'; import { tpl } from 'vj/utils';
const contestTimer = $(tpl`<pre class="contest-timer" style="display:none"></pre>`); const contestTimer = $(tpl`<pre class="contest-timer" style="display:none"></pre>`);
contestTimer.appendTo(document.body); contestTimer.appendTo(document.body);

@ -2,9 +2,7 @@ import $ from 'jquery';
import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete'; import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete';
import { ConfirmDialog } from 'vj/components/dialog'; import { ConfirmDialog } from 'vj/components/dialog';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, request, tpl } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
const page = new NamedPage(['contest_edit', 'contest_create', 'homework_create', 'homework_edit'], (pagename) => { const page = new NamedPage(['contest_edit', 'contest_create', 'homework_create', 'homework_edit'], (pagename) => {
ProblemSelectAutoComplete.getOrConstruct($('[name="pids"]'), { multi: true, clearDefaultValue: false }); ProblemSelectAutoComplete.getOrConstruct($('[name="pids"]'), { multi: true, clearDefaultValue: false });

@ -4,10 +4,9 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoCom
import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import { ActionDialog, ConfirmDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import api, { gql } from 'vj/utils/api'; import {
import delay from 'vj/utils/delay'; api, delay, gql, i18n, tpl,
import i18n from 'vj/utils/i18n'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
function update(name: string, uids: number[]) { function update(name: string, uids: number[]) {
return api(gql` return api(gql`

@ -3,10 +3,9 @@ import _ from 'lodash';
import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import { ActionDialog, ConfirmDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import {
import i18n from 'vj/utils/i18n'; delay, i18n, request, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const page = new NamedPage('domain_role', () => { const page = new NamedPage('domain_role', () => {
const createRoleDialog = new ActionDialog({ const createRoleDialog = new ActionDialog({

@ -4,10 +4,9 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoCom
import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import { ActionDialog, ConfirmDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import {
import i18n from 'vj/utils/i18n'; delay, i18n, request, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const page = new NamedPage('domain_user', () => { const page = new NamedPage('domain_user', () => {
const addUserSelector = UserSelectAutoComplete.getOrConstruct($('.dialog__body--add-user [name="user"]')); const addUserSelector = UserSelectAutoComplete.getOrConstruct($('.dialog__body--add-user [name="user"]'));

@ -4,10 +4,9 @@ import { ConfirmDialog } from 'vj/components/dialog/index';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import uploadFiles from 'vj/components/upload'; import uploadFiles from 'vj/components/upload';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import {
import pjax from 'vj/utils/pjax'; i18n, pjax, request, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
function ensureAndGetSelectedFiles() { function ensureAndGetSelectedFiles() {
const files = _.map( const files = _.map(

@ -4,8 +4,7 @@ import { createRoot } from 'react-dom/client';
import VjNotification from 'vj/components/notification'; import VjNotification from 'vj/components/notification';
import selectUser from 'vj/components/selectUser'; import selectUser from 'vj/components/selectUser';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import api, { gql } from 'vj/utils/api'; import { api, gql, loadReactRedux } from 'vj/utils';
import loadReactRedux from 'vj/utils/loadReactRedux';
const page = new NamedPage('home_messages', () => { const page = new NamedPage('home_messages', () => {
let reduxStore; let reduxStore;

@ -1,10 +1,9 @@
import $ from 'jquery'; import $ from 'jquery';
import { renderLanguageSelect } from 'vj/components/languageselect'; import { renderLanguageSelect } from 'vj/components/languageselect';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import getAvailableLangs from 'vj/utils/availableLangs'; import {
import delay from 'vj/utils/delay'; delay, getAvailableLangs, i18n, tpl,
import i18n from 'vj/utils/i18n'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
async function initCodeLangHelper() { async function initCodeLangHelper() {
const $el = $(tpl`<div class="row" id="codelang-select"></div>`); const $el = $(tpl`<div class="row" id="codelang-select"></div>`);

@ -3,10 +3,9 @@ import QRCode from 'qrcode';
import { ActionDialog } from 'vj/components/dialog'; import { ActionDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import api, { gql } from 'vj/utils/api'; import {
import delay from 'vj/utils/delay'; api, delay, gql, i18n, tpl,
import i18n from 'vj/utils/i18n'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
export default new NamedPage('home_security', () => { export default new NamedPage('home_security', () => {
$(document).on('click', '[name="tfa_enable"]', async () => { $(document).on('click', '[name="tfa_enable"]', async () => {

@ -1,7 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, mongoId } from 'vj/utils';
import { parse as parseMongoId } from 'vj/utils/mongoId';
const page = new NamedPage('homework_main', async () => { const page = new NamedPage('homework_main', async () => {
// Homework Calendar // Homework Calendar
@ -13,7 +12,7 @@ const page = new NamedPage('homework_main', async () => {
title: doc.title, title: doc.title,
maskFrom: doc.penaltySince ? doc.penaltySince : null, maskFrom: doc.penaltySince ? doc.penaltySince : null,
maskTitle: i18n('Time Extension'), maskTitle: i18n('Time Extension'),
colorIndex: parseMongoId(doc._id).timestamp % 12, colorIndex: mongoId(doc._id).timestamp % 12,
link: doc.url, link: doc.url,
})); }));
const calendar = new Calendar(events); const calendar = new Calendar(events);

@ -2,7 +2,7 @@ import $ from 'jquery';
import { ActionDialog } from 'vj/components/dialog'; import { ActionDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
const page = new NamedPage('manage_script', () => { const page = new NamedPage('manage_script', () => {
const runScriptDialog = new ActionDialog({ const runScriptDialog = new ActionDialog({

@ -1,9 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import { delay, i18n, request } from 'vj/utils';
import i18n from 'vj/utils/i18n';
import request from 'vj/utils/request';
const page = new NamedPage('manage_user_import', () => { const page = new NamedPage('manage_user_import', () => {
async function post(draft) { async function post(draft) {

@ -3,9 +3,7 @@ import { ActionDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import selectUser from 'vj/components/selectUser'; import selectUser from 'vj/components/selectUser';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, pjax, request } from 'vj/utils';
import pjax from 'vj/utils/pjax';
import request from 'vj/utils/request';
const page = new NamedPage('manage_user_priv', () => { const page = new NamedPage('manage_user_priv', () => {
const setPrivDialog = new ActionDialog({ const setPrivDialog = new ActionDialog({

@ -10,10 +10,9 @@ import { configYamlFormat } from 'vj/components/problemconfig/ProblemConfigEdito
import uploadFiles from 'vj/components/upload'; import uploadFiles from 'vj/components/upload';
import download from 'vj/components/zipDownloader'; import download from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import {
import loadReactRedux from 'vj/utils/loadReactRedux'; i18n, loadReactRedux, request, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const page = new NamedPage('problem_config', () => { const page = new NamedPage('problem_config', () => {
let reduxStore; let reduxStore;
@ -72,7 +71,7 @@ const page = new NamedPage('problem_config', () => {
async function handleClickDownloadAll() { async function handleClickDownloadAll() {
const files = reduxStore.getState().testdata.map((i) => i.name); const files = reduxStore.getState().testdata.map((i) => i.name);
const { links, pdoc } = await request.post('./files', { operation: 'get_links', files, type: 'testdata' }); const { links, pdoc } = await request.post('./files', { operation: 'get_links', files, type: 'testdata' });
const targets = []; const targets: { filename: string, url: string }[] = [];
for (const filename of Object.keys(links)) targets.push({ filename, url: links[filename] }); for (const filename of Object.keys(links)) targets.push({ filename, url: links[filename] });
await download(`${pdoc.docId} ${pdoc.title}.zip`, targets); await download(`${pdoc.docId} ${pdoc.title}.zip`, targets);
} }
@ -103,7 +102,7 @@ const page = new NamedPage('problem_config', () => {
store.dispatch({ store.dispatch({
type: 'CONFIG_LOAD', type: 'CONFIG_LOAD',
payload: request.get(), payload: request.get(''),
}); });
const unsubscribe = store.subscribe(() => { const unsubscribe = store.subscribe(() => {
// TODO set yaml schema // TODO set yaml schema
@ -124,7 +123,7 @@ const page = new NamedPage('problem_config', () => {
subtasks: normalizeSubtasks(subtasks, (i) => i, state.config.time, state.config.memory, true), subtasks: normalizeSubtasks(subtasks, (i) => i, state.config.time, state.config.memory, true),
}); });
}); });
createRoot($('#ProblemConfig').get(0)).render( createRoot(document.getElementById('ProblemConfig')!).render(
<Provider store={store}> <Provider store={store}>
<div className="row"> <div className="row">
<div className="medium-5 columns"> <div className="medium-5 columns">

@ -6,12 +6,9 @@ import { createRoot } from 'react-dom/client';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { downloadProblemSet } from 'vj/components/zipDownloader'; import { downloadProblemSet } from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import {
import i18n from 'vj/utils/i18n'; delay, i18n, loadReactRedux, pjax, request, tpl,
import loadReactRedux from 'vj/utils/loadReactRedux'; } from 'vj/utils';
import pjax from 'vj/utils/pjax';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
class ProblemPageExtender { class ProblemPageExtender {
constructor() { constructor() {

@ -8,10 +8,9 @@ import Notification from 'vj/components/notification';
import uploadFiles from 'vj/components/upload'; import uploadFiles from 'vj/components/upload';
import download from 'vj/components/zipDownloader'; import download from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import {
import request from 'vj/utils/request'; i18n, request, slideDown, slideUp, tpl,
import { slideDown, slideUp } from 'vj/utils/slide'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const categories = {}; const categories = {};
const dirtyCategories = []; const dirtyCategories = [];

@ -7,10 +7,9 @@ import { previewFile } from 'vj/components/preview/preview.page';
import uploadFiles from 'vj/components/upload'; import uploadFiles from 'vj/components/upload';
import download from 'vj/components/zipDownloader'; import download from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import {
import pjax from 'vj/utils/pjax'; i18n, pjax, request, tpl,
import request from 'vj/utils/request'; } from 'vj/utils';
import tpl from 'vj/utils/tpl';
async function downloadProblemFilesAsArchive(type, files) { async function downloadProblemFilesAsArchive(type, files) {
const { links, pdoc } = await request.post('', { operation: 'get_links', files, type }); const { links, pdoc } = await request.post('', { operation: 'get_links', files, type });

@ -6,11 +6,9 @@ import createHint from 'vj/components/hint';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { downloadProblemSet } from 'vj/components/zipDownloader'; import { downloadProblemSet } from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import delay from 'vj/utils/delay'; import {
import i18n from 'vj/utils/i18n'; delay, i18n, pjax, request, tpl,
import pjax from 'vj/utils/pjax'; } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
const categories = {}; const categories = {};
const dirtyCategories = []; const dirtyCategories = [];

@ -2,9 +2,7 @@ import $ from 'jquery';
import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import { ActionDialog, ConfirmDialog } from 'vj/components/dialog';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, request, tpl } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
const page = new NamedPage([ const page = new NamedPage([
'problem_create', 'problem_edit', 'problem_solution', 'problem_submit', 'problem_create', 'problem_edit', 'problem_solution', 'problem_submit',

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import { renderLanguageSelect } from 'vj/components/languageselect'; import { renderLanguageSelect } from 'vj/components/languageselect';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import getAvailableLangs from 'vj/utils/availableLangs'; import { getAvailableLangs } from 'vj/utils';
const page = new NamedPage(['problem_submit', 'contest_detail_problem_submit', 'homework_detail_problem_submit'], async () => { const page = new NamedPage(['problem_submit', 'contest_detail_problem_submit', 'homework_detail_problem_submit'], async () => {
const { config } = UiContext.pdoc; const { config } = UiContext.pdoc;

@ -2,8 +2,7 @@ import $ from 'jquery';
import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete'; import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete';
import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete'; import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import getAvailableLangs from 'vj/utils/availableLangs'; import { getAvailableLangs, tpl } from 'vj/utils';
import tpl from 'vj/utils/tpl';
const page = new NamedPage('record_main', async () => { const page = new NamedPage('record_main', async () => {
const [{ default: WebSocket }, { DiffDOM }] = await Promise.all([ const [{ default: WebSocket }, { DiffDOM }] = await Promise.all([

@ -2,7 +2,7 @@ import yaml from 'js-yaml';
import Schema from 'schemastery'; import Schema from 'schemastery';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import request from 'vj/utils/request'; import { request } from 'vj/utils';
const page = new NamedPage('manage_config', async () => { const page = new NamedPage('manage_config', async () => {
const schema = new Schema(UiContext.schema); const schema = new Schema(UiContext.schema);

@ -1,9 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import { ConfirmDialog } from 'vj/components/dialog'; import { ConfirmDialog } from 'vj/components/dialog';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import i18n from 'vj/utils/i18n'; import { i18n, request, tpl } from 'vj/utils';
import request from 'vj/utils/request';
import tpl from 'vj/utils/tpl';
export default new NamedPage('training_edit', () => { export default new NamedPage('training_edit', () => {
let confirmed = false; let confirmed = false;

@ -1,6 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { AutoloadPage } from 'vj/misc/Page'; import { AutoloadPage } from 'vj/misc/Page';
import api, { gql } from 'vj/utils/api'; import { api, gql } from 'vj/utils';
export default new AutoloadPage('user_login', (pagename) => { export default new AutoloadPage('user_login', (pagename) => {
(pagename === 'user_login' ? $(document) : $('.dialog--signin__main')).on('blur', '[name="uname"]', async () => { (pagename === 'user_login' ? $(document) : $('.dialog--signin__main')).on('blur', '[name="uname"]', async () => {

@ -1,11 +0,0 @@
declare global {
interface Window {
node_modules: any;
LANGS: Record<string, any>;
}
let UserContext: Record<string, any>;
let UiContext: Record<string, any>;
}
export { };

@ -1,26 +0,0 @@
import request from './request';
export default async (q: string, path: string[] = []) => {
let query = q.trim();
if (!query.startsWith('query')) query = `query{${query}}`;
const res = await request.post(`/d/${UiContext.domainId}/api`, { query });
if (res.errors) throw new Error(res.errors[0].message);
let cursor = res;
for (const p of path) {
cursor = cursor[p];
if (!cursor) return undefined;
}
return cursor;
};
export const gql = (
pieces: TemplateStringsArray,
...templates: (string | number | string[] | number[])[]
) => {
let res = '';
for (let i = 0; i < pieces.length; i++) {
res += pieces[i];
if (templates[i]) res += JSON.stringify(templates[i]);
}
return res;
};

@ -1,12 +0,0 @@
const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0]));
export default function getAvailableLangs(langsList?: string[]) {
const Langs = {};
for (const key in window.LANGS) {
if (prefixes.has(key)) continue;
if (langsList && langsList.length && langsList.join('') && !langsList.includes(key)) continue;
else if (window.LANGS[key].hidden && !langsList?.includes(key)) continue;
Langs[key] = window.LANGS[key];
}
return Langs;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save