diff --git a/TODO b/TODO index 7a7cf97fb..b63a0ea2b 100644 --- a/TODO +++ b/TODO @@ -9,4 +9,4 @@ SETTINGS_PRIVACY.allowRegisteredUsers model.setting.ui.name pdoc.secretConfig? 在线IDE 更换颜色主题, fix resize - +better place to store config.json diff --git a/bin/hydrooj.js b/bin/hydrooj.js new file mode 100644 index 000000000..181a3ab83 --- /dev/null +++ b/bin/hydrooj.js @@ -0,0 +1,18 @@ +#!/usr/bin/env node + +const hydro = require('../dist/loader'); + +hydro.addon('.'); + +try { + const uiDefault = require.resolve('@hydrooj/ui-default'); + hydro.addon(uiDefault); +} catch (e) { + console.error('Please also install @hydrooj/ui-default'); + process.exit(1); +} + +hydro.load().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/build/hack.js b/build/hack.js deleted file mode 100644 index 499b2e206..000000000 --- a/build/hack.js +++ /dev/null @@ -1,51 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -module.exports = { - formidable: () => { - const tasks = ['incoming_form', 'file', 'json_parser', 'querystring_parser']; - for (const task of tasks) { - const p = require.resolve(`formidable/lib/${task}`); - const file = fs.readFileSync(p).toString().split('\n'); - if (file[0].includes('if (global.GENTLY) require = GENTLY.hijack(require);')) { - file[0] = '// Hacked'; - fs.writeFileSync(p, file.join('\n')); - } else if (!file[0].includes('// Hacked')) { - console.error('Cannot hack formidable', task); - } - } - }, - sockjs: () => { - const p = require.resolve('sockjs/lib/sockjs.js'); - const file = fs.readFileSync(p).toString().split('\n'); - if (file[139].includes('this.app.log')) { - file[139] = '// Hacked'; - fs.writeFileSync(p, file.join('\n')); - } else if (!file[139].includes('// Hacked')) { - console.error('Cannot hack sockjs'); - } - }, - saslprep: () => { - const q = require.resolve('saslprep'); - const mem = path.join(path.dirname(q), 'code-points.mem'); - const p = path.join(path.dirname(q), 'lib', 'memory-code-points.js'); - const data = fs.readFileSync(mem); - const file = fs.readFileSync(p).toString().split('\n'); - if (file[7].includes('fs.readFileSync')) { - file[7] = `const memory = Buffer.from('${data.toString('base64')}', 'base64');`; - fs.writeFileSync(p, file.join('\n')); - } else if (!file[7].includes('Buffer.from')) { - console.error('Cannot hack saslprep'); - } - }, - ftp: () => { - const p = require.resolve('ftp/lib/connection'); - const file = fs.readFileSync(p).toString().split('\n'); - if (file[51].includes('new Buffer')) { - file[51] = file[51].replace('new Buffer', 'Buffer.from'); - fs.writeFileSync(p, file.join('\n')); - } else if (!file[51].includes('Buffer.from')) { - console.error('Cannot hack ftp'); - } - }, -}; diff --git a/build/index.js b/build/index.js deleted file mode 100644 index beb529909..000000000 --- a/build/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable no-await-in-loop */ -const path = require('path'); -const fs = require('fs-extra'); -const argv = require('yargs-parser')(process.argv.slice(2)); -const hack = require('./hack'); - -async function build(type) { - if (!['development', 'production'].includes(type)) throw new Error(`Unknown type: ${type}`); - fs.ensureDirSync(path.resolve(__dirname, '..', '.build')); - for (const task in hack) hack[task](); - console.log('Build::Main'); - await require('./webpack')(type); - console.log('Build::Resources'); - require('./resource')(type); -} - -build(argv.development ? 'development' : 'production').catch((e) => { - console.error(e); - process.exit(1); -}); diff --git a/build/resource.js b/build/resource.js deleted file mode 100644 index aa3a9181b..000000000 --- a/build/resource.js +++ /dev/null @@ -1,13 +0,0 @@ -const AdmZip = require('adm-zip'); - -function build() { - const zip = new AdmZip(); - zip.addLocalFolder('public', 'public'); - zip.addLocalFolder('locales', 'locales'); - zip.addLocalFolder('templates', 'templates'); - zip.addLocalFolder('wiki', 'wiki'); - zip.addLocalFile('package.json'); - zip.writeZip('.build/builtin.hydro'); -} - -module.exports = build; diff --git a/build/webpack.js b/build/webpack.js deleted file mode 100644 index 22a6d0df7..000000000 --- a/build/webpack.js +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -const path = require('path'); -const webpack = require('webpack'); -const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); - -const build = async (type) => { - const config = { - mode: type, - entry: { - app: path.resolve(__dirname, '..', 'hydro', 'loader.ts'), - }, - output: { - filename: '[name].js', - path: path.resolve(__dirname, '..', '.build'), - }, - target: 'node', - module: { - rules: [ - { - test: /\.ts$/, - exclude: /node_modules/, - use: [ - { - loader: 'cache-loader', - options: { - cacheDirectory: path.resolve(__dirname, '..', '.cache', 'ts'), - }, - }, - 'ts-loader', - ], - }, - ], - }, - node: { - __dirname: false, - }, - resolve: { extensions: ['.js', '.ts'] }, - plugins: [ - new webpack.ProgressPlugin(), - new FriendlyErrorsPlugin({ - clearConsole: false, - }), - ], - }; - const compiler = webpack(config); - await new Promise((resolve, reject) => { - compiler.run((err, stats) => { - if (err) { - console.error(err.stack || err); - if (err.details) console.error(err.details); - reject(); - } - if (stats.hasErrors()) process.exitCode = 1; - resolve(); - }); - }); -}; - -module.exports = build; diff --git a/entry.js b/entry.js new file mode 100644 index 000000000..0cb7db60b --- /dev/null +++ b/entry.js @@ -0,0 +1,14 @@ +const loader = require('./dist/loader'); + +function load(callback) { + callback({ addon: loader.addon }); + loader.load().catch((e) => { + console.error(e); + process.exit(1); + }); +} + +load.load = loader.load; +load.addon = loader.addon; + +module.exports = load; diff --git a/hydro/entry/common.ts b/hydro/entry/common.ts index 900192a26..9543c5a36 100644 --- a/hydro/entry/common.ts +++ b/hydro/entry/common.ts @@ -246,6 +246,7 @@ export async function wiki(pending: string[], fail: string[]) { } catch (e) { fail.push(i); console.error(`Wiki Load Fail: ${i}`); + console.error(e); } } } diff --git a/hydro/entry/master.ts b/hydro/entry/master.ts index 8a55dd7d5..1f12dfe04 100644 --- a/hydro/entry/master.ts +++ b/hydro/entry/master.ts @@ -5,8 +5,8 @@ import { builtinLib, builtinHandler, builtinModel, } from './common'; -export async function load(call) { - let pending = await require('../lib/hpm').getInstalled(); +export async function load(call, args) { + let pending = args; const fail = []; require('../lib/i18n'); require('../utils'); diff --git a/hydro/entry/setup.ts b/hydro/entry/setup.ts index 7f3419752..195319232 100644 --- a/hydro/entry/setup.ts +++ b/hydro/entry/setup.ts @@ -1,7 +1,7 @@ -import fs from 'fs'; import os from 'os'; import path from 'path'; import http from 'http'; +import fs from 'fs-extra'; import Koa from 'koa'; import Body from 'koa-body'; import Router from 'koa-router'; @@ -89,7 +89,7 @@ const env = new nunjucks.Environment(new Loader(), { autoescape: true, trimBlock function render(name) { return new Promise((resolve, reject) => { env.render(name, { - _: (str) => str, + _: (str: string) => str, }, (err, res) => { if (err) reject(err); else resolve(res); @@ -145,7 +145,8 @@ export function load() { { upsert: true }, ), ]); - fs.writeFileSync(path.resolve(process.cwd(), 'config.json'), JSON.stringify({ + fs.ensureDirSync(path.resolve(os.homedir(), '.hydro')); + fs.writeFileSync(path.resolve(os.homedir(), '.hydro', 'config.json'), JSON.stringify({ host, port, name, username, password, })); ctx.body = `

This page will reload in 3 secs.

diff --git a/hydro/entry/unzip.ts b/hydro/entry/unzip.ts deleted file mode 100644 index e58a01f36..000000000 --- a/hydro/entry/unzip.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable no-continue */ -import os from 'os'; -import path from 'path'; -import AdmZip from 'adm-zip'; -import fs from 'fs-extra'; - -const moduleRoots = []; -const _moduleRoots = Array.from(new Set([ - path.resolve(process.cwd(), 'node_modules', '@hydrooj'), - path.resolve(process.cwd(), 'modules'), - path.resolve(process.cwd(), 'module'), - path.resolve(process.cwd(), '.build'), - process.cwd(), - path.resolve(__dirname, 'node_modules', '@hydrooj'), - path.resolve(__dirname, 'modules'), - path.resolve(__dirname, 'module'), - path.resolve(__dirname, '.build'), - __dirname, - path.resolve(os.homedir(), '.hydro', 'module'), - // TODO add npm global path - '/usr/local/share/.config/yarn/global/node_modules/@hydrooj', - path.resolve(os.homedir(), 'AppData', 'Local', 'Yarn', 'Data', 'global', 'node_modules', '@hydrooj'), -])); -for (const moduleRoot of _moduleRoots) { - if (fs.existsSync(moduleRoot)) moduleRoots.push(moduleRoot); -} - -if (process.env.debug) console.log('Module roots: ', moduleRoots); - -const moduleTemp = path.resolve(os.tmpdir(), 'hydro', 'module'); -const publicTemp = path.resolve(os.tmpdir(), 'hydro', 'public'); -const tmp = path.resolve(os.tmpdir(), 'hydro', '__'); - -process.on('unhandledRejection', (e) => { - console.error(e); - process.exit(1); -}); - -export async function load() { - fs.ensureDirSync(moduleTemp); - fs.ensureDirSync(publicTemp); - for (const moduleRoot of moduleRoots) { - if (fs.existsSync(moduleRoot)) { - const files = fs.readdirSync(moduleRoot); - for (const file of files) { - try { - const modulePath = path.join(moduleRoot, file); - const packagejson = path.join(modulePath, 'package.json'); - if (fs.statSync(modulePath).isFile() && file.endsWith('.hydro')) { - // Is *.hydro module - const zip = new AdmZip(modulePath); - const targetPath = path.resolve(moduleTemp, file.split('.')[0]); - zip.extractAllTo(targetPath, true); - const content = fs.readdirSync(targetPath); - const ipath = path.join(targetPath, content[0]); - if (content.length === 1 && fs.statSync(ipath).isDirectory()) { - fs.moveSync(ipath, tmp); - fs.rmdirSync(targetPath); - fs.moveSync(tmp, targetPath); - } - const publicPath = path.resolve(moduleTemp, file.split('.')[0], 'public'); - if (fs.existsSync(publicPath)) fs.copySync(publicPath, publicTemp); - } else if (fs.existsSync(packagejson)) { - // Is a npm package - const q = fs.readFileSync(packagejson).toString(); - if (!JSON.parse(q).name.startsWith('@hydrooj/')) continue; - const publicPath = path.resolve(modulePath, 'public'); - if (fs.existsSync(publicPath)) fs.copySync(publicPath, publicTemp); - } else continue; - } catch (e) { - console.error('Module extract fail:', file, e); - } - } - } - } - console.log('Unzip done.'); - process.exit(0); -} diff --git a/hydro/entry/worker.ts b/hydro/entry/worker.ts index bdf241b15..7b6835232 100644 --- a/hydro/entry/worker.ts +++ b/hydro/entry/worker.ts @@ -5,8 +5,8 @@ import { builtinLib, builtinScript, builtinHandler, builtinModel, } from './common'; -export async function load() { - let pending = await require('../lib/hpm').getInstalled(); +export async function load(call, args) { + let pending = args; const fail = []; const active = []; require('../lib/i18n'); diff --git a/hydro/interface.ts b/hydro/interface.ts index 94d7aaade..567cef8a1 100644 --- a/hydro/interface.ts +++ b/hydro/interface.ts @@ -292,8 +292,9 @@ declare global { locales: Dict>, postInit: Function[], }, - nodeModules: any, onDestory: Function[], + addons: string[], + argv: any, } } } diff --git a/hydro/lib/hpm.ts b/hydro/lib/hpm.ts index f03e34dd8..0cacb16aa 100644 --- a/hydro/lib/hpm.ts +++ b/hydro/lib/hpm.ts @@ -13,20 +13,6 @@ const moduleRoots = Array.from(new Set([ path.resolve(os.tmpdir(), 'hydro', 'module'), ])); -export async function getInstalled() { - const modules = []; - for (const moduleRoot of moduleRoots) { - if (fs.existsSync(moduleRoot)) { - const files = fs.readdirSync(moduleRoot); - for (const file of files) { - const info = path.resolve(moduleRoot, file, 'package.json'); - if (fs.existsSync(info)) modules.push(path.resolve(moduleRoot, file)); - } - } - } - return modules; -} - export async function getDetail() { const modules = []; for (const moduleRoot of moduleRoots) { @@ -55,5 +41,5 @@ export function install(url: string, name: string = '') { } global.Hydro.lib.hpm = { - getInstalled, getDetail, install, + getDetail, install, }; diff --git a/hydro/loader.ts b/hydro/loader.ts index a1859658c..f621b051d 100644 --- a/hydro/loader.ts +++ b/hydro/loader.ts @@ -1,12 +1,17 @@ +/* eslint-disable no-continue */ /* eslint-disable import/no-dynamic-require */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-eval */ import './interface'; +import os from 'os'; +import path from 'path'; import cluster from 'cluster'; +import fs from 'fs-extra'; import parse from 'yargs-parser'; +import AdmZip from 'adm-zip'; -const argv = parse(process.argv.slice(2)); +global.argv = parse(process.argv.slice(2)); global.Hydro = { stat: { reqCount: 0 }, @@ -30,8 +35,9 @@ global.Hydro = { postInit: [], }; global.onDestory = []; +global.addons = []; -if (argv.debug) { +if (global.argv.debug) { console.log(process.argv); process.env.debug = 'enable'; } @@ -46,7 +52,7 @@ async function terminate() { async function fork(args: string[] = []) { const _args = process.argv.slice(2); - _args.push(...args); + _args.push(...args, `--addons=${Buffer.from(JSON.stringify(global.addons)).toString('base64')}`); cluster.setupMaster({ args: _args }); return cluster.fork(); } @@ -54,7 +60,8 @@ async function fork(args: string[] = []) { async function entry(config: any) { if (config.entry) { if (config.newProcess) { - const p = await fork([`--entry=${config.entry}`]); + const sargv = [`--entry=${config.entry}`]; + const p = await fork(sargv); await new Promise((resolve, reject) => { p.on('exit', resolve); p.on('error', (err: Error) => { @@ -64,7 +71,7 @@ async function entry(config: any) { }); } else { const loader = require(`./entry/${config.entry}`); - return await loader.load(entry); + return await loader.load(entry, global.addons); } } return null; @@ -75,8 +82,9 @@ async function stopWorker() { } async function startWorker(cnt: number) { - await fork(['--firstWorker']); - for (let i = 1; i < cnt; i++) await fork(); + const sargv = [`--args=${Buffer.from(JSON.stringify(global.addons)).toString('base64')}`]; + await fork(['--firstWorker', ...sargv]); + for (let i = 1; i < cnt; i++) await fork(sargv); } async function executeCommand(input: string) { @@ -92,7 +100,9 @@ async function executeCommand(input: string) { async function messageHandler(worker: cluster.Worker, msg: any) { if (!msg) msg = worker; if (msg.event) { - if (msg.event === 'bus') { + if (msg.event === 'setAddon') { + global.addons = msg.addons; + } else if (msg.event === 'bus') { if (cluster.isMaster) { for (const i in cluster.workers) { cluster.workers[i].send(msg); @@ -113,19 +123,61 @@ async function messageHandler(worker: cluster.Worker, msg: any) { } } -async function load() { - global.nodeModules = { - 'adm-zip': require('adm-zip'), - superagent: require('superagent'), - 'js-yaml': require('js-yaml'), - mongodb: require('mongodb'), - }; +const moduleTemp = path.resolve(os.tmpdir(), 'hydro', 'module'); +const publicTemp = path.resolve(os.tmpdir(), 'hydro', 'public'); +const tmp = path.resolve(os.tmpdir(), 'hydro', '__'); + +export function addon(addonPath: string) { + let modulePath = path.resolve(addonPath); + if (!(fs.existsSync(addonPath) && fs.statSync(addonPath).isFile())) { + try { + // Is a npm package + const packagejson = require.resolve(`${addonPath}/package.json`); + modulePath = path.dirname(packagejson); + const publicPath = path.resolve(modulePath, 'public'); + if (fs.existsSync(publicPath)) fs.copySync(publicPath, publicTemp); + global.addons.push(modulePath); + } catch (e) { + throw new Error(`Addon not found: ${addonPath}`); + } + } else { + try { + // Is *.hydro module + const t = modulePath.split(path.sep); + const name = t[t.length - 1].split('.')[0]; + const zip = new AdmZip(modulePath); + const targetPath = path.resolve(moduleTemp, name); + zip.extractAllTo(targetPath, true); + const content = fs.readdirSync(targetPath); + const ipath = path.join(targetPath, content[0]); + if (content.length === 1 && fs.statSync(ipath).isDirectory()) { + fs.moveSync(ipath, tmp); + fs.rmdirSync(targetPath); + fs.moveSync(tmp, targetPath); + } + const publicPath = path.resolve(targetPath, 'public'); + if (fs.existsSync(publicPath)) fs.copySync(publicPath, publicTemp); + global.addons.push(targetPath); + } catch (e) { + console.error('Addon load fail: ', e); + throw e; + } + } +} + +export async function load() { + while (!global.addons) { + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); + } + addon(path.resolve(__dirname, '..')); Error.stackTraceLimit = 50; process.on('unhandledRejection', (e) => console.error(e)); process.on('SIGINT', terminate); process.on('message', messageHandler); cluster.on('message', messageHandler); - if (cluster.isMaster || argv.startAsMaster) { + if (cluster.isMaster || global.argv.startAsMaster) { console.log(`Master ${process.pid} Starting`); process.stdin.setEncoding('utf8'); process.stdin.on('data', (buf) => { @@ -139,7 +191,6 @@ async function load() { executeCommand(input); } }); - await entry({ entry: 'unzip', newProcess: true }); const cnt = await entry({ entry: 'master' }); console.log('Master started'); cluster.on('exit', (worker, code, signal) => { @@ -153,14 +204,15 @@ async function load() { }); cluster.on('online', (worker) => { console.log(`Worker ${worker.process.pid} ${worker.id} is online`); + worker.send({ event: 'setAddon', addons: global.addons }); }); await startWorker(cnt); - } else if (argv.entry) { - console.log(`Worker ${process.pid} Starting as ${argv.entry}`); - await entry({ entry: argv.entry }); - console.log(`Worker ${process.pid} Started as ${argv.entry}`); + } else if (global.argv.entry) { + console.log(`Worker ${process.pid} Starting as ${global.argv.entry}`); + await entry({ entry: global.argv.entry }); + console.log(`Worker ${process.pid} Started as ${global.argv.entry}`); } else { - if (argv.firstWorker) cluster.isFirstWorker = true; + if (global.argv.firstWorker) cluster.isFirstWorker = true; else cluster.isFirstWorker = false; console.log(`Worker ${process.pid} Starting`); await entry({ entry: 'worker' }); @@ -169,7 +221,7 @@ async function load() { if (global.gc) global.gc(); } -if (argv.pandora || !module.parent) { +if (global.argv.pandora || !module.parent) { load().catch((e) => { console.error(e); process.exit(1); diff --git a/hydro/model/setting.ts b/hydro/model/setting.ts index 3470409a3..52d1fb87e 100644 --- a/hydro/model/setting.ts +++ b/hydro/model/setting.ts @@ -113,6 +113,7 @@ SystemSetting( Setting('setting_smtp', 'smtp.port', null, 465, 'number', 'SMTP Server Port'), Setting('setting_smtp', 'smtp.from', null, null, 'text', 'Mail From'), Setting('setting_smtp', 'smtp.secure', null, false, 'boolean', 'SSL'), + Setting('setting_server', 'server.name', null, 'Hydro', 'text', 'Server Name'), Setting('setting_server', 'server.worker', null, 1, 'number', 'Server Workers Number'), Setting('setting_server', 'server.hostname', null, null, 'text', 'Server Hostname'), Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'), @@ -125,16 +126,6 @@ SystemSetting( Setting('setting_session', 'session.secure', null, false, 'boolean', 'session.secure'), Setting('setting_session', 'session.saved_expire_seconds', null, 3600 * 24 * 30, 'number', 'Saved session expire seconds'), Setting('setting_session', 'session.unsaved_expire_seconds', null, 3600 * 3, 'number', 'Unsaved session expire seconds'), - Setting('setting_ui', 'ui.name', null, 'Hydro', 'text', 'Site Name'), - Setting('setting_ui', 'ui.footer_extra_html', null, '', 'textarea', 'Footer Extra HTML'), - Setting('setting_ui', 'ui.header_logo', null, '/components/header/header-logo.png', 'text', 'Header Logo'), - Setting('setting_ui', 'ui.header_logo_2x', null, '/components/header/header-logo@2x.png', 'text', 'Header Logo@2x'), - Setting('setting_ui', 'ui.header_background', null, '/components/header/header-background.png', 'text', 'Header Background'), - Setting('setting_ui', 'ui.header_background_2x', null, '/components/header/header-background@2x.png', 'text', 'Header Background@2x'), - Setting('setting_ui', 'ui.nav_logo_light', null, '/components/navigation/nav-logo-small_light.png', 'text', 'Nav Logo(Light)'), - Setting('setting_ui', 'ui.nav_logo_dark', null, '/components/navigation/nav-logo-small_dark.png', 'text', 'Nav Logo(Dark)'), - Setting('setting_ui', 'ui.nav_logo_light_2x', null, '/components/navigation/nav-logo-small@2x_light.png', 'text', 'Nav Logo@2x(Light)'), - Setting('setting_ui', 'ui.nav_logo_dark_2x', null, '/components/navigation/nav-logo-small@2x_dark.png', 'text', 'Nav Logo@2x(Dark)'), Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'), Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret', null, FLAG_SECRET), Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID', null), diff --git a/hydro/options.ts b/hydro/options.ts index 84aefa850..ffcff2866 100644 --- a/hydro/options.ts +++ b/hydro/options.ts @@ -6,6 +6,7 @@ export default function load() { let f = path.resolve(process.cwd(), 'config.json'); if (!fs.existsSync(f)) f = path.resolve(__dirname, 'config.json'); if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.config', 'hydro', 'config.json'); + if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.hydro', 'config.json'); if (!fs.existsSync(f)) f = path.resolve('/config/config.json'); return JSON.parse(fs.readFileSync(f).toString()); } diff --git a/package.json b/package.json index 6e40abfa3..b493ee200 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "hydrooj", - "version": "2.8.5", - "main": "hydro/loader.ts", + "version": "2.9.0", + "main": "entry.js", + "bin": "bin/hydrooj.js", "repository": "https://github.com/hydro-dev/Hydro.git", "author": "masnn", "license": "GPL-3.0-only", @@ -65,35 +66,19 @@ "@types/yargs": "^15.0.5", "@typescript-eslint/eslint-plugin": "^3.6.0", "@typescript-eslint/parser": "^3.6.0", - "cache-loader": "^4.1.0", "eslint": "^7.4.0", "eslint-config-airbnb-base": "^14.2.0", "eslint-import-resolver-typescript": "^2.0.0", "eslint-plugin-import": "^2.20.2", - "friendly-errors-webpack-plugin": "^1.7.0", "snyk": "^1.354.0", - "ts-loader": "^8.0.0", - "typescript": "^3.9.6", - "webpack": "^4.42.1" + "typescript": "^3.9.7" }, "scripts": { - "build": "node build --development", - "build:development": "node build --development", - "build:production": "node build --production", - "build:all": "cd ui && yarn && yarn build:production && cd .. && yarn && yarn build:production", - "start": "ts-node hydro/loader.ts", - "pkg": "pkg .", + "build": "tsc", + "build:watch": "tsc --watch", "lint": "eslint build hydro tool --ext ts,js --fix", "snyk-protect": "snyk protect", "prepare": "yarn run snyk-protect" }, - "pkg": { - "scripts": [ - ".build/app.js" - ], - "assets": [ - ".build/module/**" - ] - }, "snyk": true } \ No newline at end of file diff --git a/templates/bsod.html b/templates/bsod.html deleted file mode 100644 index fe2c3bd5a..000000000 --- a/templates/bsod.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - Hydro System Error - - - - -
-
-#include <string.h>
-int main() {
-  char answer[42];
-  strcpy(answer, "masnn will tell you the answer to life the universe and everything,");
-  strcpy(answer, "but first of all, she is very sorry that Hydro meets some issues now.");
-  strcpy(answer, "please wait patiently, or contact masnn impatiently about this error.");
-  return 0;
-}
-/*
-{{ error.message }}
-{{ error.stack }}
-*/
-
-
- - \ No newline at end of file diff --git a/templates/components/comments_discussion.html b/templates/components/comments_discussion.html deleted file mode 100644 index c2e1cdb8c..000000000 --- a/templates/components/comments_discussion.html +++ /dev/null @@ -1,150 +0,0 @@ -{% import "components/user.html" as user with context %} -{% macro reply(mode_create, udoc, doc = {}, rdoc = {}) %} -
  • -
    -
    - -
    -
    - {% if not mode_create %} -
    -
    - {{ user.render_inline(udoc, avatar=false) }} - @ {{ datetimeSpan(rdoc['_id'])|safe }} -
    - {{ reply_operations(doc, rdoc) }} -
    -
    - {{ rdoc['content']|markdown|safe }} -
    -
    - {% else %} -
    - {% endif %} -
    -
    -
  • -{% endmacro %} - -{% macro reply_operations(doc, rdoc) %} -
    -{% if handler.user.hasPerm(reply_post_perm) %} - -{% endif %} -{% if doc.owner == handler.user._id or handler.user.hasPerm(reply_edit_perm) %} - -{% endif %} -{% if doc.owner == handler.user._id or handler.user.hasPerm(reply_delete_perm) %} - -{% endif %} -
    -{% endmacro %} - -{% macro render( - view, docs, udict, - comment_ref, - reply_ref, - comment_post_perm, - comment_edit_perm, - comment_edit_self_perm, - comment_delete_perm, - comment_delete_self_perm, - reply_post_perm, - reply_edit_perm, - reply_edit_self_perm, - reply_delete_perm, - reply_delete_self_perm, - comment_placeholder = 'Write Your Comment', - comment_post_op = 'reply', - comment_edit_op = 'edit_reply', - comment_post_text = 'Comment', - comment_edit_text = 'Update', - reply_post_op = 'tail_reply', - reply_post_text = 'Reply', - reply_edit_op = 'edit_tail_reply', - reply_edit_text = 'Update', - comment_delete_op = 'delete_reply', - reply_delete_op = 'delete_tail_reply' -) %} - -{% endmacro %} diff --git a/templates/components/comments_solution.html b/templates/components/comments_solution.html deleted file mode 100644 index fc01f9548..000000000 --- a/templates/components/comments_solution.html +++ /dev/null @@ -1,177 +0,0 @@ -{% import "components/user.html" as user with context %} -{% macro reply(mode_create, udoc, doc = {}, rdoc = {}) %} - -{% endmacro %} - -{% macro render( - view, docs, udict, - comment_ref, - reply_ref, - comment_post_perm, - comment_edit_perm, - comment_edit_self_perm, - comment_delete_perm, - comment_delete_self_perm, - reply_post_perm, - reply_edit_perm, - reply_edit_self_perm, - reply_delete_perm, - reply_delete_self_perm, - comment_placeholder = 'Write Your Comment', - comment_post_op = 'reply', - comment_edit_op = 'edit_reply', - comment_post_text = 'Comment', - comment_edit_text = 'Update', - reply_post_op = 'tail_reply', - reply_post_text = 'Reply', - reply_edit_op = 'edit_tail_reply', - reply_edit_text = 'Update', - comment_delete_op = 'delete_reply', - reply_delete_op = 'delete_tail_reply' -) %} - -{% endmacro %} diff --git a/templates/components/contest.html b/templates/components/contest.html deleted file mode 100644 index cd46bf87b..000000000 --- a/templates/components/contest.html +++ /dev/null @@ -1,8 +0,0 @@ -{% import "components/user.html" as user with context %} -{% macro render_time(tdoc_time) %} -{{ datetimeSpan(tdoc_time, false, '%Y-%m-%d %H:%M')|safe }} -{% endmacro %} - -{% macro render_duration(tdoc) %} -{{ ((tdoc.endAt.getTime() - tdoc.beginAt.getTime()) /1000 / 3600)|round(1) }} -{% endmacro %} diff --git a/templates/components/form.html b/templates/components/form.html deleted file mode 100644 index 9f445f845..000000000 --- a/templates/components/form.html +++ /dev/null @@ -1,201 +0,0 @@ -{% macro form_begin(args) %} -{% set label = args.label %} -{% set columns = args.columns %} -{% set last_column = args.last_column|default(true) %} -{% set required = args.required or false %} -{% set hotkeys = args.hotkeys or none %} -{% set row = args.row|default(true) %} -{% set no_label = args.no_label or false %} -{% set label_wrap = args.label_wrap|default(true) %} -{% if row %} -
    -{% endif %} -
    - {% if not no_label %} - - {% endif %} - {% endif %} -{% endmacro %} - -{% macro form_end(args) %} -{% set help_text = args.help_text or none %} -{% set row = args.row|default(true) %} -{% set no_label = args.no_label or false %} -{% set label_wrap = args.label_wrap|default(true) %} - {% if not no_label and label_wrap %} - - {% endif %} - {% if help_text %} -

    {{ _(help_text)|safe }}

    - {% endif %} -
    -{% if row %} -
    -{% endif %} -{% endmacro %} - -{% macro form_attr(form_class, args) %} -{% set disabled = args.disabled or false %} -{% set required = args.required or false %} -{% set autofocus = args.autofocus or false %} -{% set date = args.date or false %} -{% set time = args.time or false %} - class="{{ form_class }}{% if extra_class %} {{ extra_class }}{% endif %}" - {% if disabled %}disabled{% endif %} - {% if required %}required{% endif %} - {% if autofocus %}autofocus data-autofocus{% endif %} - {% if date %}data-pick-date{% endif %} - {% if time %}data-pick-time{% endif %} -{% endmacro %} - -{% macro container_attr(container_class, args) %} -{% set extra_class = args.extra_class or none %} - class="{{ container_class }}{% if extra_class %} {{ extra_class }}{% endif %}" -{% endmacro %} - -{% macro select(args) %} -
    - -
    -{% endmacro %} - -{% macro form_select(args) %} -{{ form_begin({columns:5}|assign(args)) }} -{{ select(args) }} -{{ form_end(args) }} -{% endmacro %} - -{% macro radio(args) %} -
    -{% for k, v in options %} - -{% endfor %} -
    -{% endmacro %} - -{% macro form_radio(args) %} -{{ form_begin({columns:5, label_wrap:false}|assign(args)) }} -{{ radio(args) }} -{{ form_end({label_warp:false}|assign(args)) }} -{% endmacro %} - -{% macro image_radio(args) %} -
    -{% for k, v in options %} - -{% endfor %} -
    -{% endmacro %} - -{% macro form_image_radio(args) %} -{{ form_begin({columns:8, label_warp:false}|assign(args)) }} -{{ image_radio(args) }} -{{ form_end({label_warp:false}|assign(args)) }} -{% endmacro %} - -{% macro text(args) %} -
    - -
    -{% endmacro %} - -{% macro form_text(args) %} -{{ form_begin({columns:5}|assign(args)) }} -{{ text(args) }} -{{ form_end(args) }} -{% endmacro %} - -{% macro textarea(args) %} -{% set markdown = args.markdown or false %} -
    - -
    -{% endmacro %} - -{% macro form_textarea(args) %} -{{ form_begin({columns:8}|assign(args)) }} -{{ textarea(args) }} -{{ form_end(args) }} -{% endmacro %} - -{% macro checkbox(args) %} -
    - -
    -{% endmacro %} - -{% macro form_checkbox(args) %} -{{ form_begin({columns:5, no_label:true}|assign(args)) }} -{{ checkbox(args) }} -{{ form_end({no_label:true}|assign(args)) }} -{% endmacro %} diff --git a/templates/components/home.html b/templates/components/home.html deleted file mode 100644 index 1fe07eed8..000000000 --- a/templates/components/home.html +++ /dev/null @@ -1,15 +0,0 @@ -{% import "components/sidemenu.html" as sidemenu with context %} -{% macro render_sidebar() %} -
    - -
    -{% endmacro %} diff --git a/templates/components/homework.html b/templates/components/homework.html deleted file mode 100644 index b6a69b27b..000000000 --- a/templates/components/homework.html +++ /dev/null @@ -1,8 +0,0 @@ -{% import "components/user.html" as user with context %} -{% macro render_time(tdoc_time) %} -{{ datetimeSpan(tdoc_time, false, '%Y-%m-%d %H:%M')|safe }} -{% endmacro %} - -{% macro render_extension(tdoc) %} -{{ ((tdoc['endAt'].getTime() - tdoc['penaltySince'].getTime())/ 1000 / 3600/ 24)|round(1) }} -{% endmacro %} diff --git a/templates/components/md_hint.html b/templates/components/md_hint.html deleted file mode 100644 index 98602bd13..000000000 --- a/templates/components/md_hint.html +++ /dev/null @@ -1,29 +0,0 @@ -
    -
    -
    -
    {{ _('Inline codes') }}
    -
    `std::cout()`
    -
    {{ _('Code blocks') }}
    -
    -
    ```c++
    -int main() {
    -  return 0;
    -}
    -```
    -
    -
    {{ _('Inline formulas') }}
    -
    $ E=mc^2 $
    -
    {{ _('Formula blocks') }}
    -
    $$ E=mc^2 $$
    -
    {{ _('Links') }}
    -
    [{{ _('Text') }}](http://...)
    -
    {{ _('Images') }}
    -
    ![{{ _('Description') }}](http://...)
    -
    -

    {{ _('Learn More') }}

    - -
    -
    diff --git a/templates/components/noscript_note.html b/templates/components/noscript_note.html deleted file mode 100644 index 8f4e1c3bb..000000000 --- a/templates/components/noscript_note.html +++ /dev/null @@ -1,15 +0,0 @@ -{% macro render() %} -{% if not isIE(handler.request.headers['user-agent']) %} - -{% else %} -
    -
    -

    {{ _('This page needs JavaScript to work.') }}

    -
    -
    -{% endif %} -{% endmacro %} diff --git a/templates/components/nothing.html b/templates/components/nothing.html deleted file mode 100644 index b4f3143e7..000000000 --- a/templates/components/nothing.html +++ /dev/null @@ -1,6 +0,0 @@ -{% macro render(text, compact=false) %} -
    -
    - {{ _(text) }} -
    -{% endmacro %} diff --git a/templates/components/paginator.html b/templates/components/paginator.html deleted file mode 100644 index bd8136336..000000000 --- a/templates/components/paginator.html +++ /dev/null @@ -1,25 +0,0 @@ -{% macro render(page, num_pages, add_qs='') %} -{% if num_pages > 0 %} - -{% endif %} -{% endmacro %} diff --git a/templates/components/problem.html b/templates/components/problem.html deleted file mode 100644 index 2b4d95296..000000000 --- a/templates/components/problem.html +++ /dev/null @@ -1,37 +0,0 @@ -{% macro render_problem_title(pdoc, tdoc=none, show_tags=true, show_invisible_flag=true, invalid=false) %} - {%- if not invalid %} - - {%- endif %} - {{ pdoc['pid'] }} {{ pdoc['title'] }} - {%- if not invalid %} - - {%- endif %} -{%- if pdoc['hidden'] and show_invisible_flag %} - ({{ _('Hidden') }}) -{%- endif %} -{%- if show_tags %} - {{ render_problem_tags(pdoc) }} -{%- endif %} -{% endmacro %} - -{% macro render_problem_tags(pdoc, show_none_label=false) %} -{%- if pdoc['tag']|length > 0 %} - -{%- else %} -{%- if show_none_label %} - {{ _('(None)') }} -{%- endif %} -{%- endif %} -{% endmacro %} diff --git a/templates/components/record.html b/templates/components/record.html deleted file mode 100644 index e09a88c67..000000000 --- a/templates/components/record.html +++ /dev/null @@ -1,15 +0,0 @@ -{% macro render_status_td(rdoc, rid_key='_id') %} - -
    - - - {{ model.builtin.STATUS_TEXTS[rdoc['status']] }} - -
    -{% if rdoc['status'] == STATUS.STATUS_JUDGING %} -
    -
    -
    -{% endif %} - -{% endmacro %} diff --git a/templates/components/sidemenu.html b/templates/components/sidemenu.html deleted file mode 100644 index f09110e10..000000000 --- a/templates/components/sidemenu.html +++ /dev/null @@ -1,15 +0,0 @@ -{% macro render_item(icon_name, page_name, args={}, label=none) %} -{% set target_url = url(page_name, args) %} - -{% endmacro %} diff --git a/templates/components/user.html b/templates/components/user.html deleted file mode 100644 index 1af29920b..000000000 --- a/templates/components/user.html +++ /dev/null @@ -1,21 +0,0 @@ -{% macro render_inline( - udoc, - avatar=true, - badge=true, - modbadge=true -) %} - -{% if avatar %} - -{% endif %} - -{% if badge %} - -{% if modbadge and udoc.hasPerm(perm.PERM_MOD_BADGE) %} - -{% endif %} -{% endif %} - -{% endmacro %} diff --git a/templates/contest_detail.html b/templates/contest_detail.html deleted file mode 100644 index 24a893505..000000000 --- a/templates/contest_detail.html +++ /dev/null @@ -1,79 +0,0 @@ -{% extends "layout/basic.html" %} -{% import "components/contest.html" as contest with context %} -{% import "components/record.html" as record with context %} -{% import "components/problem.html" as problem with context %} -{% block content %} -
    -
    -
    -
    - {{ tdoc['content']|markdown|safe }} - {% if model.contest.isOngoing(tdoc) %} -
    -

    {{ _('We will check code similarity after this contest.') }}

    -
    - {% endif %} -
    -
    - {% if model.contest.isOngoing(tdoc) or model.contest.isDone(tdoc) %} -
    -
    -

    {{ _('Problem') }}

    -
    - {% if attended or model.contest.isDone(tdoc) %} -
    - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - - {% endif %} - - - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - - {% endif %} - - - - - {% for pid in tdoc.pids %} - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - {% if psdict[pid] and psdict[pid].rid %} - {% set rdoc = rdict[psdict[pid].rid] %} - {% if handler.canShowRecord(tdoc) %} - {{ record.render_status_td(rdoc) }} - {% else %} - - {% endif %} - - {% else %} - - - {% endif %} - {% endif %} - - - {% endfor %} - -
    {{ _('Status') }}{{ _('Last Submit At') }}{{ _('Problem') }}
    {{ _('Submitted') }}{{ datetimeSpan(rdoc._id)|safe }}{{ _('No Submissions') }}- - #{{ loop.index }}: - {{ problem.render_problem_title(pdict[pid], tdoc=tdoc, show_invisible_flag=false, show_tags=false) }} -
    -
    - {% else %} - {{ nothing.render('Please attend contest to see the problems.') }} - {% endif %} -
    - {% endif %} -
    -
    - {% set owner_udoc = udict[tdoc.owner] %} - {% include "partials/contest_sidebar.html" %} -
    -
    -{% endblock %} diff --git a/templates/contest_edit.html b/templates/contest_edit.html deleted file mode 100644 index 4cff1e971..000000000 --- a/templates/contest_edit.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    -
    - {{ form.form_select({ - columns:4, - label:'Rule', - name:'rule', - options:rules, - value:tdoc['rule']|default(''), - row:false - }) }} - {{ form.form_text({ - columns:4, - label:'Title', - name:'title', - placeholder:_('title'), - value:tdoc.title|default(''), - autofocus:true, - row:false - }) }} - {{ form.form_checkbox({ - columns:4, - label:'Rated', - name:'rated', - placeholder:_('Is rated'), - value:tdoc.rated|default(true), - row:false - }) }} -
    - {{ form.form_text({ - columns:4, - label:'Begin Date', - name:'beginAtDate', - placeholder:'YYYY-mm-dd', - value:date_text, - date:true, - row:false - }) }} - {{ form.form_text({ - columns:4, - label:'Begin Time', - name:'beginAtTime', - placeholder:'HH:MM', - value:time_text, - time:true, - row:false - }) }} - {{ form.form_text({ - columns:4, - label:'Duration (hours)', - name:'duration', - value:duration|default('3'), - row:false - }) }} -
    - {{ form.form_text({ - columns:null, - label:'Problems', - name:'pids', - value:pids - }) }} - {{ form.form_textarea({ - columns:null, - label:'Content', - name:'content', - value:tdoc['content']|default(''), - markdown:true - }) }} -
    - - {% if page_name == 'contest_edit' %} - - {% else %} - - {% endif %} - -
    -
    -
    -
    -
    -
    - {% include 'components/md_hint.html' %} -
    -
    -{% endblock %} diff --git a/templates/contest_main.html b/templates/contest_main.html deleted file mode 100644 index aa630dd4b..000000000 --- a/templates/contest_main.html +++ /dev/null @@ -1,132 +0,0 @@ -{% extends "layout/basic.html" %} -{% import "components/contest.html" as contest with context %} -{% macro render_important_contest(view, title, tdoc) %} -
    -
    -
    -
    -
    -

    {{ _(title) }}

    -
    -

    {{ tdoc.title }}

    -
      -
    • - - {{ _('Rule') }}: {{ model.contest.RULES[tdoc['rule']].TEXT }} -
    • -
    • - - {{ _('Start at') }}: {{ contest.render_time(tdoc.beginAt) }} -
    • -
    • - - {{ _('Duration') }}: {{ contest.render_duration(tdoc) }} {{ _('hour(s)') }} -
    • -
    • - - {{ _('Partic.') }}: {{ tdoc['attend']|default(0) }} -
    • -
    -
    -
    -
    -
    - {{ _('View Details') }} -
    - {% if tsdict[tdoc.docId].attend %} -

    {{ _('Attended') }}

    - {% else %} -

    {{ _('Not Attended') }}

    - {% endif %} -
    -
    -
    -
    -
    -{% endmacro %} -{% block content %} -
    -
    - {% for tdoc in tdocs %} - {% if model.contest.isOngoing(tdoc) %} - {{ render_important_contest('live', 'Live...', tdoc) }} - {% elif model.contest.isUpcoming(tdoc) %} - {{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }} - {% endif %} - {% endfor %} -
    -
    -

    {% if not rule %}{{ _('All Contests') }}{% else %}{{ _('All {0} Contests').format(_(model.contest.RULES[rule].TEXT)) }}{% endif %}

    -
    -
    - {{ _('Show') }}: - - -
    -
    -
    -
    - {% if not tdocs.length %} - {{ nothing.render('There are no contests...') }} - {% else %} -
      - {% for tdoc in tdocs %} -
    1. -
      -
      -
      -
      {{ datetimeSpan(tdoc['beginAt'], false, '%d')|safe }}
      -
      {{ datetimeSpan(tdoc['beginAt'], false, '%Y-%m')|safe }}
      -
      -
      -
      -

      {{ tdoc.title }}

      -
        -
      • - {{ model.contest.RULES[tdoc.rule].TEXT }} -
      • -
      • - {{ contest.render_duration(tdoc) }} {{ _('hour(s)') }} -
      • -
      • - {{ tdoc['attend']|default(0) }} -
      • - {% if tsdict[tdoc.docId]['attend'] == 1 %} -
      • - {{ _('Attended') }} -
      • - {% endif %} -
      -
      -
      -
    2. - {% endfor %} -
    - {{ paginator.render(page, tpcount, add_qs=qs) }} - {% endif %} -
    -
    -
    -
    - {% if handler.user.hasPerm(perm.PERM_CREATE_CONTEST) %} -
    -
    -

    - {{ _('Create Contest') }} -

    -
    - -
    - {% endif %} -
    -
    -{% endblock %} diff --git a/templates/contest_scoreboard.html b/templates/contest_scoreboard.html deleted file mode 100644 index d7336ab2b..000000000 --- a/templates/contest_scoreboard.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    - -
    - - - {%- for column in rows[0] -%} - - {%- endfor -%} - - - - {%- for column in rows[0] -%} - - {%- endfor -%} - - - - {%- for row in rows -%}{% if loop.index0>0 %} - - {%- for column in row -%} - - {%- endfor -%} - - {% endif %}{%- endfor -%} - -
    - {% if column.type == 'problem_detail' %} - {{ column.value }} - {% else %} - {{ column.value }} - {% endif %} -
    - {% if column.type == 'user' %} - {{ user.render_inline(udict[column.raw], badge=false) }} - {% elif column.type == 'record' and column.raw %} - {{ column.value }} - {% else %} - {{ column.value }} - {% endif %} -
    -
    -
    -
    -{% endblock %} diff --git a/templates/contest_scoreboard_download_html.html b/templates/contest_scoreboard_download_html.html deleted file mode 100644 index b6a61565e..000000000 --- a/templates/contest_scoreboard_download_html.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - {%- for column in rows[0] -%} - - {%- endfor -%} - - - - {%- for row in rows -%}{% if loop.index0 %} - - {%- for column in row -%} - - {%- endfor -%} - - {% endif %}{%- endfor -%} - -
    - {{ column['value'] }} -
    - {{ column['value'] }} -
    diff --git a/templates/discussion_create.html b/templates/discussion_create.html deleted file mode 100644 index 6362dd208..000000000 --- a/templates/discussion_create.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -

    {{ _('Create Discussion in {0}').format(vnode.title) }}

    -
    -
    -
    - {% include "partials/discussion_edit_form.html" %} -
    - - - -
    -
    -
    -
    -
    -
    - {% include 'components/md_hint.html' %} -
    -
    -{% endblock %} diff --git a/templates/discussion_detail.html b/templates/discussion_detail.html deleted file mode 100644 index f5abadbb2..000000000 --- a/templates/discussion_detail.html +++ /dev/null @@ -1,136 +0,0 @@ -{% extends "layout/basic.html" %} -{% import "components/comments_discussion.html" as comments with context %} -{% block content %} -
    -
    -
    -
    - -
    - {{ ddoc['content']|markdown|safe }} -
    -
    - -
    -
    -
    -

    {{ _('{0} comments').format(drcount) }}

    -
    -
    - {{ comments.render( - view = 'discussion', - docs = drdocs, - udict = udict, - comment_ref = 'drid', - reply_ref = 'drrid', - comment_post_op = 'reply', - reply_post_op = 'tail_reply', - comment_edit_op = 'edit_reply', - comment_delete_op = 'delete_reply', - reply_edit_op = 'edit_tail_reply', - reply_delete_op = 'delete_tail_reply', - comment_post_perm = perm.PERM_REPLY_DISCUSSION, - comment_edit_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION if ddoc.owner == handler.user._id else perm.PERM_EDIT_DISCUSSION_REPLY, - comment_delete_perm = perm.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION if ddoc.owner == handler.user._id else perm.PERM_DELETE_DISCUSSION_REPLY, - comment_edit_self_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF, - comment_delete_self_perm = perm.PERM_DELETE_DISCUSSION_REPLY_SELF, - reply_post_perm = perm.PERM_REPLY_DISCUSSION, - reply_edit_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION if ddoc.owner == handler.user._id else perm.PERM_EDIT_DISCUSSION_REPLY, - reply_delete_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION if ddoc.owner == handler.user._id else perm.PERM_DELETE_DISCUSSION_REPLY, - reply_edit_self_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF, - reply_delete_self_perm = perm.PERM_DELETE_DISCUSSION_REPLY_SELF - ) }} - {{ paginator.render(page, pcount) }} - {% if drcount == 0 %} - {{ nothing.render('No comments so far...') }} - {% endif %} -
    -
    -
    -
    - {% set udoc = udict[ddoc['owner']] %} -
    - -
    - -
    -
    - -
    -
    - {% if vnode.type == model.document.TYPE_PROBLEM %} - {% set pdoc = vnode %} - {% set owner_udoc = udict[vnode['owner']] %} - {% include "partials/problem_sidebar.html" %} - {% endif %} -
    -
    -{% endblock %} diff --git a/templates/discussion_edit.html b/templates/discussion_edit.html deleted file mode 100644 index 2b89d5800..000000000 --- a/templates/discussion_edit.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    - {% include "partials/discussion_edit_form.html" %} -
    - - - {% if ddoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_DELETE_DISCUSSION) %} - - {% endif %} - -
    -
    -
    -
    -
    -
    - {% include 'components/md_hint.html' %} -
    -
    -{% endblock %} diff --git a/templates/discussion_main_or_node.html b/templates/discussion_main_or_node.html deleted file mode 100644 index fa89fdb61..000000000 --- a/templates/discussion_main_or_node.html +++ /dev/null @@ -1,44 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    - {% include "partials/discussion_list.html" %} -
    -
    -
    - {% if vnode.type == model.document.TYPE_PROBLEM %} - {% set pdoc = vnode %} - {% set owner_udoc = udict[vnode.owner] %} - {% include "partials/problem_sidebar.html" %} - {% elif vnode.type == model.document.TYPE_CONTEST %} - {% set tdoc = vnode %} - {% set owner_udoc = udict[vnode.owner] %} - {% include "partials/contest_sidebar.html" %} - {% elif vnode.type == model.document.TYPE_DISCUSSION_NODE %} -
    - {% if vnode.pic %}
    {% endif %} -
    -

    {{ vnode['title'] or _('Create Discussion') }}

    -
    -
    - {% if vnode %} - {% if handler.user.hasPerm(perm.PERM_CREATE_DISCUSSION) %} -

    {{ _('Create a Discussion') }}

    - {% else %} - {% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} -

    {{ _('Login to Create a Discussion') }}

    - {% else %} -

    {{ _('No Permission to Create a Discussion') }}

    - {% endif %} - {% endif %} - {% else %} -

    {{ _('Select a node to create discussion.') }}

    - {% endif %} -
    -
    - {% endif %} - {% include 'partials/discussion_nodes_widget.html' %} -
    -
    -{% endblock %} diff --git a/templates/domain_base.html b/templates/domain_base.html deleted file mode 100644 index 120e338e3..000000000 --- a/templates/domain_base.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    - {% block domain_content %}{% endblock %} -
    -
    -
    - -
    -
    -
    -{% endblock %} diff --git a/templates/domain_create.html b/templates/domain_create.html deleted file mode 100644 index ea957b6b4..000000000 --- a/templates/domain_create.html +++ /dev/null @@ -1,56 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -

    {{ _('Create Domain') }}

    -
    -
    -
    -

    {{ _('A domain ID cannot be changed after creation. It is supposed to be:') }}

    -
      -
    • {{ _('Unique') }}
    • -
    • {{ _('At least 4 characters') }}
    • -
    • {{ _('Not wrapped with space') }}
    • -
    • {{ _('Not started with a number') }}
    • -
    • {{ _('Only A-Z, a-z, 0-9 and _ are accepted') }}
    • -
    -
    -
    - {{ form.form_text({ - label:'ID', - name:'id', - autofocus:true, - required:true - }) }} - {{ form.form_text({ - label:'Name', - name:'name', - required:true - }) }} - {{ form.form_textarea({ - label:'Bulletin', - name:'bulletin', - markdown:true, - required:true - }) }} - {{ form.form_text({ - label:'Gravatar', - name:'gravatar' - }) }} -
    - - - -
    -
    -
    -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/domain_dashboard.html b/templates/domain_dashboard.html deleted file mode 100644 index 935a2de5e..000000000 --- a/templates/domain_dashboard.html +++ /dev/null @@ -1,22 +0,0 @@ -{% import "components/user.html" as user %} -{% extends "domain_base.html" %} -{% block domain_content %} -{% if domain.bulletin %} -
    -
    - {{ domain.bulletin|markdown|safe }} -
    -
    -{% endif %} -
    -
    -

    {{ _('Information') }}

    -
    -
    -
    -
    {{ _('Gravatar') }}
    -
    {{ _('Name') }}
    {{ domain.name }}
    -
    -
    -
    -{% endblock %} diff --git a/templates/domain_edit.html b/templates/domain_edit.html deleted file mode 100644 index b312beb5f..000000000 --- a/templates/domain_edit.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "domain_base.html" %} -{% block domain_content %} -
    - {% include "partials/setting.html" %} -
    -{% endblock %} diff --git a/templates/domain_permission.html b/templates/domain_permission.html deleted file mode 100644 index 8c1563041..000000000 --- a/templates/domain_permission.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "domain_base.html" %} -{% block domain_content %} -
    -
    -
    -

    {{ _('{0}: Permissions').format(domain.name) }}

    -
    -
    - - - - {% for role in roles %} - - {% endfor %} - - - - - {% for role in roles %} - - {% endfor %} - - - {% for family, perms in PERMS_BY_FAMILY %} - - - - - {% for perm in perms %} - - - {% for role in roles %} - - {% endfor %} - - {% endfor %} - - {% endfor %} -
    {{ _('Permissions') }}{{ role._id }}
    - {{ _(family) }} -
    - {{ _(perm.desc) }} - - -
    -
    -
    - - - -
    -
    -
    -{% endblock %} diff --git a/templates/domain_role.html b/templates/domain_role.html deleted file mode 100644 index 0e4dd9b63..000000000 --- a/templates/domain_role.html +++ /dev/null @@ -1,72 +0,0 @@ -{% extends "domain_base.html" %} -{% block domain_content %} - -
    -
    -

    {{ _('{0}: Roles').format(domain.name) }}

    -
    - -
    -
    - {{ noscript_note.render() }} -
    - - - - - - - - - - - - - - - - {% for role in roles %} - {% set isBuiltin = role._id in ['default', 'guest', 'admin'] %} - - - - - - {% endfor %} - -
    - - {{ _('Role') }}{{ _('Users') }}
    - - - {{ _('Built-in') if isBuiltin else _('User-defined role') }}: {{ role._id }} - - {{ role.count|default('--') }} -
    -
    -
    - -
    -
    -{% endblock %} diff --git a/templates/domain_user.html b/templates/domain_user.html deleted file mode 100644 index f336deefd..000000000 --- a/templates/domain_user.html +++ /dev/null @@ -1,112 +0,0 @@ -{% extends "domain_base.html" %} -{% block domain_content %} - - -
    -
    -

    {{ _('{0}: Users').format(domain.name) }}

    -
    - -
    -
    - {{ noscript_note.render() }} -
    - - - - - - - - - - - - - - - - - {% for role in roles %} - {% for rudoc in rudocs[role._id] %} - {% set is_disabled=(rudoc._id == handler.user._id) %} - - - - - - - {% endfor %} - {% endfor %} - -
    - - {{ _('User ID') }}{{ _('Username') }}{{ _('Role') }}
    - - - {{ rudoc._id }} - - {{ user.render_inline(udict[rudoc._id], badge=false) }} - - {{ form.select({ - options:rolesSelect, - name:'role', - value:rudoc.role, - extra_class:'compact', - disabled:is_disabled - }) }} -
    -
    -
    - - -
    -
    -{% endblock %} diff --git a/templates/error.html b/templates/error.html deleted file mode 100644 index 0f9553a31..000000000 --- a/templates/error.html +++ /dev/null @@ -1,22 +0,0 @@ -{% set no_path_section = true %} -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    -

    {{ _('Oops!') }}

    -

    {{ _(error.message).format.apply(error.params) }}

    -

    {{ _('Technical Information') }}:

    -

    {{ _('Type') }}: {{ error.code }}

    -

    {{ _('Arguments') }}: -

      - {% for param in error.params %} -
    1. {{ param }}
    2. - {% endfor %} -
    -

    -
    -
    -{% endblock %} diff --git a/templates/extra.css b/templates/extra.css deleted file mode 100644 index f27b86956..000000000 --- a/templates/extra.css +++ /dev/null @@ -1,54 +0,0 @@ -/* {% if header_logo %} */ - -.header__logo { - background: url({{header_logo}}) -} - -/* {% endif %} */ - -/* {% if header_background %} */ - -.header { - background: url({{header_background}}) -} - -/* {% endif %} */ - -/* {% if nav_logo_light %} */ - -.nav__logo, .header--mobile__domain.is--system { - background-image: url({{nav_logo_light}}) -} - -/* {% endif %} */ - -/* {% if nav_logo_dark %} */ - -.nav.floating .nav__logo, .nojs .nav .nav__logo { - background-image: url({{nav_logo_dark}}) !important -} - -/* {% endif %} */ - -@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx), only screen and (min-resolution: 144dpi) { - /* {% if header_2x %} */ - .header__logo { - background-image: url({{header_2x}}) - } - /* {% endif %} */ - /* {% if header_background_2x %} */ - .header { - background-image: url({{header_background_2x}}) - } - /* {% endif %} */ - /* {% if nav_logo_light_2x %} */ - .nav__logo, .header--mobile__domain.is--system { - background-image: url({{nav_logo_light_2x}}) !important - } - /* {% endif %} */ - /* {% if nav_logo_dark_2x %} */ - .nav.floating .nav__logo, .nojs .nav .nav__logo, .header--mobile__domain.is--system { - background-image: url({{nav_logo_dark_2x}}) !important - } - /* {% endif %} */ -} \ No newline at end of file diff --git a/templates/fs_upload.html b/templates/fs_upload.html deleted file mode 100644 index 398962734..000000000 --- a/templates/fs_upload.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends 'layout/simple.html' %} -{% block body %} -{% if fdoc %} -

    {{ _('Uploaded file info') }}:

    -

    {{ _('ID') }}: {{ fdoc['_id'] }}

    -

    {{ _('Userfile doc_id') }}: {{ ufid }}

    -

    {{ _('MD5') }}: {{ fdoc['md5'] }}

    -

    {{ _('URL') }}: {{ model.file.url(fdoc) }}

    -{% endif %} -
    - -

    -{{ _('New file') }}: - - - -

    -

    {{ _('Note: file size should be less than or equal to {0}, and file type should be in {1}. Empty files are not allowed.').format(size(FILE_MAX_LENGTH), ALLOWED_MIMETYPE_PREFIX) }}

    -

    {{ _('Usage: {0}, Quota: {1}').format(size(usage), size(quota)) }}

    -
    -{% endblock %} diff --git a/templates/home_domain.html b/templates/home_domain.html deleted file mode 100644 index 1e2e30a1f..000000000 --- a/templates/home_domain.html +++ /dev/null @@ -1,53 +0,0 @@ -{% extends "layout/home_base.html" %} -{% block home_content %} -
    -
    -

    {{ _('My Domains') }}

    -
    - {% if handler.user.hasPriv(model.builtin.PRIV.PRIV_CREATE_DOMAIN) %} - {{ _('Create Domain') }} - {% endif %} - {{ _("What's domain?") }} -
    -
    -
    - - - - - - - - - - - - - - - - - {% for ddoc in ddocs %} - - - - - - - {% endfor %} - -
     {{ _('Name') }} ({{ _('ID') }}){{ _('My Role') }}{{ _('Action') }}
    - - - {{ ddoc['name'] }} ({{ ddoc['_id'] }}) - - {{ dudict[ddoc._id].role|default('default') }} - - {{ _('Visit') }} - {% if canManage[ddoc['_id']] %} - · {{ _('Manage') }} - {% endif %} -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/home_file.html b/templates/home_file.html deleted file mode 100644 index 604f9bd91..000000000 --- a/templates/home_file.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "layout/home_base.html" %} -{% block home_content %} -
    -
    -

    {{ _('My Files') }}

    -
    - {% if handler.user.hasPriv(model.builtin.PRIV.PRIV_CREATE_FILE) %} - {{ _('Upload File') }} - {% endif %} - {{ _("What's file?") }} -
    -
    -
    - - - - - - - - - - - - - - - - - {% for ufdoc in ufdocs %} - - - - - - - {% endfor %} - -
    {{ _('Description') }}{{ _('Size') }}{{ _('Hash') }}{{ _('Action') }}
    {{ ufdoc.filename }}{{ size(ufdoc.size) if ufdoc.size else '?' }}{{ ufdoc.md5|default('?') }}{{ _('Download') }}
    -
    -
    -{% endblock %} diff --git a/templates/home_messages.html b/templates/home_messages.html deleted file mode 100644 index 6c96dd2ea..000000000 --- a/templates/home_messages.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "layout/home_base.html" %} -{% block home_content %} - -
    -
    - {{ noscript_note.render() }} -
    -
    -
    -{% endblock %} diff --git a/templates/home_security.html b/templates/home_security.html deleted file mode 100644 index d4669250c..000000000 --- a/templates/home_security.html +++ /dev/null @@ -1,135 +0,0 @@ -{% extends "layout/home_base.html" %} -{% block home_content %} -
    -
    -
    -
    -
    -

    {{ _('Change Email') }}

    -
    -
    -
    - {{ form.form_text({ - type:'password', - label:'Current Password', - columns:10, - name:'password', - required:true - }) }} - {{ form.form_text({ - type:'text', - label:'Current Email', - columns:10, - name:'currentEmail', - value:handler.user.mail, - disabled:true - }) }} - {{ form.form_text({ - type:'text', - label:'New Email', - columns:10, - name:'mail', - required:true - }) }} -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    -

    {{ _('Change Password') }}

    -
    -
    -
    - {{ form.form_text({ - type:'password', - label:'Current Password', - columns:10, - name:'current', - required:true - }) }} - {{ form.form_text({ - type:'password', - label:'New Password', - columns:10, - name:'password', - required:true - }) }} - {{ form.form_text({ - type:'password', - label:'Repeat Password', - columns:10, - name:'verifyPassword', - required:true - }) }} -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    {{ _('Active Sessions') }}

    -
    -
    -
      - {% for session in sessions %} -
    • -
      -
      - -
      -
      -

      {{ _('Last Update At') }}: {{ datetimeSpan(session.updateAt)|safe }}

      -

      {{ _('Location') }}: {{ _(session.updateGeoip.display) }} ({{ session.updateIp }})

      -

      {{ _('Operating System') }}: {{ session.updateUa.os }}

      -

      {{ _('Browser') }}: {{ session.updateUa.name }} {{ session.updateUa.version }}

      - {% if session.isCurrent %} -

      {{ _('This is the current session') }}

      - {% endif %} -
      - {% if not session.isCurrent %} -
      -
      - - - - -
      -
      - {% endif %} -
      -
    • - {% endfor %} -
    -
    -
    -
    - - - -
    - {% if geoipProvider %} -
    -

    IP geo-location data is provided by {{ geoipProvider|safe }}.

    -
    - {% endif %} -
    -
    -
    -
    -
    -{% endblock %} diff --git a/templates/home_settings.html b/templates/home_settings.html deleted file mode 100644 index 7cffae76f..000000000 --- a/templates/home_settings.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "layout/home_base.html" %} -{% block home_content %} -
    - {% include "partials/setting.html" %} -
    -{% endblock %} diff --git a/templates/homework_detail.html b/templates/homework_detail.html deleted file mode 100644 index cba9a3c53..000000000 --- a/templates/homework_detail.html +++ /dev/null @@ -1,77 +0,0 @@ -{% extends "layout/basic.html" %} -{% import "components/record.html" as record with context %} -{% import "components/problem.html" as problem with context %} -{% block content %} -
    -
    -
    -
    -

    {{ _('Homework Introduction') }}

    -
    -
    - {{ tdoc['content']|markdown|safe }} -
    -
    -
    -
    -

    {{ _('Problem') }}

    -
    - {% if model.contest.isNotStarted(tdoc) %} - {{ nothing.render('This homework is not open and you cannot view problems.') }} - {% else %} - {% if attended or model.contest.isDone(tdoc) %} -
    - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - - {% endif %} - - - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - - {% endif %} - - - - - {% for pid in tdoc['pids'] %} - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - {% if psdict[pid] and psdict[pid]['rid'] %} - {% set rdoc = rdict[psdict[pid]['rid']] %} - {% if handler.canShowRecord(tdoc) %} - {{ record.render_status_td(rdoc) }} - {% else %} - - {% endif %} - - {% else %} - - - {% endif %} - {% endif %} - - - {% endfor %} - -
    {{ _('Status') }}{{ _('Last Submit At') }}{{ _('Problem') }}
    {{ _('Submitted') }}{{ datetimeSpan(rdoc['_id'])|safe }}{{ _('No Submissions') }}- - #{{ loop.index }}: - {{ problem.render_problem_title(pdict[pid], tdoc=tdoc, show_invisible_flag=false, show_tags=false) }} -
    -
    - {% else %} - {{ nothing.render('Please claim the assignment to see the problems.') }} - {% endif %}{# attended or handler.is_done(tdoc) #} - {% endif %}{# handler.is_not_started(tdoc) #} -
    -
    -
    - {% include "partials/homework_sidebar.html" %} -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/homework_edit.html b/templates/homework_edit.html deleted file mode 100644 index cf7f9852b..000000000 --- a/templates/homework_edit.html +++ /dev/null @@ -1,105 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    - {{ form.form_text({ - columns:8, - label:'Title', - name:'title', - placeholder:_('title'), - value:tdoc['title']|default(''), - autofocus:true - }) }} -
    - {{ form.form_text({ - columns:4, - label:'Begin Date', - name:'beginAtDate', - placeholder:'YYYY-mm-dd', - value:dateBeginText, - date:true, - row:false - }) }} - {{ form.form_text({ - columns:4, - last_column:true, - label:'Begin Time', - name:'beginAtTime', - placeholder:'HH:MM', - value:timeBeginText, - time:true, - row:false - }) }} -
    - {{ form.form_text({ - columns:4, - label:'End Date', - name:'penaltySinceDate', - placeholder:'YYYY-mm-dd', - value:datePenaltyText, - date:true, - row:false - }) }} - {{ form.form_text({ - columns:4, - last_column:true, - label:'End Time', - name:'penaltySinceTime', - placeholder:'HH:MM', - value:timePenaltyText, - time:true, - row:false - }) }} -
    - {{ form.form_text({ - columns:4, - label:'Extension (days)', - name:'extensionDays', - value:extension_days - }) }} -
    - -
    - {{ form.form_text({ - columns:null, - label:'Problems', - name:'pids', - value:pids - }) }} - {{ form.form_textarea({ - columns:null, - label:'Content', - name:'content', - value:tdoc['content']|default(''), - markdown:true - }) }} -
    - - {% if page_name == 'homework_edit' %} - - {% else %} - - {% endif %} - -
    -
    -
    -
    -
    -
    - {% include 'components/md_hint.html' %} -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/homework_main.html b/templates/homework_main.html deleted file mode 100644 index ff17fa9a3..000000000 --- a/templates/homework_main.html +++ /dev/null @@ -1,68 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} - -
    -
    -
    -
    -

    {{ _('All Homeworks') }}

    -
    - - {{ _('View') }}: - - - {% if handler.user.hasPerm(perm.PERM_CREATE_HOMEWORK) %} - {{ _('Create Homework') }} - {% endif %} -
    -
    -
    - {% if not tdocs.length %} - {{ nothing.render('There is no homework so far ╰(*°▽°*)╯') }} - {% else %} -
      - {% for tdoc in tdocs %} -
    1. -
      -
      -
      -
      {{ datetimeSpan(tdoc['penaltySince'], false, '%d')|safe }}
      -
      {{ datetimeSpan(tdoc['penaltySince'], false, '%Y-%m')|safe }}
      -
      -
      -
      -

      {{ tdoc.title }}

      -
        -
      • - {{ _('Status') }}: {{ _(model.contest.statusText(tdoc)) }} -
      • -
      • - {{ _('Open Since') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }} -
      • -
      • - {% if model.contest.isExtended(tdoc) or model.contest.isDone(tdoc) %} - {{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }} - {% else %} - {{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }} - {% endif %} -
      • -
      -
      -
      -
    2. - {% endfor %} -
    -
    - {% endif %} -
    -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/judge_playground.html b/templates/judge_playground.html deleted file mode 100644 index 40dcc69b3..000000000 --- a/templates/judge_playground.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -{% endblock %} diff --git a/templates/layout/basic.html b/templates/layout/basic.html deleted file mode 100644 index ef8bf6116..000000000 --- a/templates/layout/basic.html +++ /dev/null @@ -1,19 +0,0 @@ -{% set layout_name = "basic" %} -{% extends "layout/html5.html" %} -{% block body %} - -{% include "partials/nav.html" %} - -
    -
    - {% include "partials/header_mobile.html" %} - {% include "partials/header.html" %} -
    - {% block content %}{% endblock %} -
    - {% include "partials/footer.html" %} -
    - -{% include "partials/login_dialog.html" %} - -{% endblock %} diff --git a/templates/layout/home_base.html b/templates/layout/home_base.html deleted file mode 100644 index b76cfae0f..000000000 --- a/templates/layout/home_base.html +++ /dev/null @@ -1,13 +0,0 @@ -{% import "components/home.html" as home with context %} -{% extends "layout/basic.html" %} -{% block content %} -
    -
    - {% block home_content %}{% endblock %} -
    -
    - {{ home.render_sidebar() }} - {% block home_sidebar %}{% endblock %} -
    -
    -{% endblock %} diff --git a/templates/layout/html5.html b/templates/layout/html5.html deleted file mode 100644 index 8c0d521d0..000000000 --- a/templates/layout/html5.html +++ /dev/null @@ -1,44 +0,0 @@ -{% import "components/form.html" as form with context %} -{% import "components/sidemenu.html" as sidemenu with context %} -{% import "components/paginator.html" as paginator with context %} -{% import "components/nothing.html" as nothing with context %} -{% import "components/noscript_note.html" as noscript_note with context %} -{% import "components/user.html" as user with context %} - - - - - - - - - - - - - - - - - - - - - {{ handler.renderTitle(page_name) }} - {% if not isIE(handler.request.headers['user-agent']) %} - - {% endif %} - - - {% block body %}{% endblock %} - {% if not isIE(handler.request.headers['user-agent']) %} - - - {% endif %} - - diff --git a/templates/layout/immersive.html b/templates/layout/immersive.html deleted file mode 100644 index 7d7aa702d..000000000 --- a/templates/layout/immersive.html +++ /dev/null @@ -1,22 +0,0 @@ -{% set layout_name = "immersive" %} -{% set no_path_section = true %} -{% extends "layout/html5.html" %} -{% block body %} - -{% include "partials/nav.html" %} - -
    -
    - {% include "partials/header_mobile.html" %} - -
    - {% block content %}{% endblock %} -
    - -{% set show_topics = false %} -{% include "partials/footer.html" %} -
    - -{% include "partials/login_dialog.html" %} - -{% endblock %} diff --git a/templates/layout/mail.html b/templates/layout/mail.html deleted file mode 100644 index a8e6a09ee..000000000 --- a/templates/layout/mail.html +++ /dev/null @@ -1,30 +0,0 @@ -{% set layout_name = "mail" %} - -
    -
    -
    {% block title %}{% endblock %} - Hydro
    -
    -{% block content %}{% endblock %} -
    - -
    -
    - diff --git a/templates/layout/simple.html b/templates/layout/simple.html deleted file mode 100644 index a9ac9511b..000000000 --- a/templates/layout/simple.html +++ /dev/null @@ -1,4 +0,0 @@ -{% set layout_name = "basic" %} -{% extends "layout/html5.html" %} -{% block body %} -{% endblock %} diff --git a/templates/main.html b/templates/main.html deleted file mode 100644 index 3e05324c5..000000000 --- a/templates/main.html +++ /dev/null @@ -1,197 +0,0 @@ -{% set no_path_section = true %} -{% set page_name = "homepage" %} -{% extends "layout/basic.html" %} -{% import "components/contest.html" as contest with context %} -{% block content %} -{% if domain['bulletin'] %} -
    -
    -
    -
    - {{ domain['bulletin']|markdown|safe }} -
    -
    -
    -
    -{% endif %} -
    -
    - {% if tdocs.length %} -
    -
    -

    {{ _('Contest') }}

    -
    -
      - {% for tdoc in tdocs %} -
    1. -
      -
      -
      -
      {{ datetimeSpan(tdoc['beginAt'], false, '%d')|safe }}
      -
      {{ datetimeSpan(tdoc['beginAt'], false, '%Y-%m')|safe }}
      -
      -
      -
      -

      {{ tdoc.title }}

      -
        -
      • - {{ model.contest.RULES[tdoc.rule].TEXT }} -
      • -
      • - {{ contest.render_duration(tdoc) }} {{ _('hour(s)') }} -
      • -
      • - {{ tdoc['attend']|default(0) }} -
      • - {% if tsdict[tdoc.docId]['attend'] == 1 %} -
      • - {{ _('Attended') }} -
      • - {% endif %} -
      -
      -
      -
    2. - {% endfor %} -
    -
    - {% endif %} - {% if htdocs.length %} -
    -
    -

    {{ _('Homework') }}

    -
    -
      - {% for tdoc in htdocs %} -
    1. -
      -
      -
      -
      {{ datetimeSpan(tdoc['penaltySince'], false, '%d')|safe }}
      -
      {{ datetimeSpan(tdoc['penaltySince'], false, '%Y-%m')|safe }}
      -
      -
      -
      -

      {{ tdoc['title'] }}

      -
        -
      • - {{ _('Status') }}: {{ _(model.contest.statusText(tdoc)) }} -
      • -
      • - {{ _('Open Since') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }} -
      • -
      • - {% if model.contest.isExtended(tdoc) or model.contest.isDone(tdoc) %} - {{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }} - {% else %} - {{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }} - {% endif %} -
      • - {% if htsdict[tdoc['docId']]['attend'] == 1 %} -
      • - {{ _('Claimed') }} -
      • - {% endif %} -
      -
      -
      -
    2. - {% endfor %} -
    -
    - {% endif %} - {% if trdocs.length %} -
    -
    -

    {{ _('Training') }}

    -
    - {% set tdocs = trdocs %} - {% set tsdict = trsdict %} -
      - {% for tdoc in tdocs %} -
    1. -
      -
      -
      -
      {{ tdoc['enroll']|default(0) }}
      -
      {{ _('Enrolled') }}
      -
      -
      -
      -

      {{ tdoc.title }}

      -
      -

      {{ tdoc['content'] }}

      -
      -
        -
      • - {{ _('{0} sections').format(tdoc['dag']|length) }}, {{ _('{0} problems').format(model.training.getPids(tdoc)|length) }} -
      • -
      • - {% if tsdict[tdoc.docId]['enroll'] %} - {% if not tsdict[tdoc.docId]['done'] %} - - {{ _('Completed') }} {{ (100 * tsdict[tdoc.docId]['donePids']|length / model.training.getPids(tdoc)|length)|round|int }}% - {% else %} - - {{ _('Completed') }} 100% - {% endif %} - {% elif handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - {{ _('Not Enrolled') }} - {% endif %} -
      • -
      -
      -
      -
    2. - {% endfor %} -
    -
    - {% endif %} - {% if ddocs.length %} -
    -
    -

    {{ _('Discussion') }}

    -
    - {% include "partials/discussion_list.html" %} -
    - {% endif %} -
    -
    - {% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) %} - {% include 'partials/discussion_nodes_widget.html' %} - {% endif %} -
    -
    -

    {{ _('Recommended') }}

    -
    -
    -
    -
    -
    -{% endblock %} diff --git a/templates/manage_base.html b/templates/manage_base.html deleted file mode 100644 index 4e142e20a..000000000 --- a/templates/manage_base.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    - {% block manage_content %}{% endblock %} -
    -
    -
    - -
    -
    -
    -{% endblock %} diff --git a/templates/manage_dashboard.html b/templates/manage_dashboard.html deleted file mode 100644 index de12869ca..000000000 --- a/templates/manage_dashboard.html +++ /dev/null @@ -1,32 +0,0 @@ -{% import "components/user.html" as user %} -{% extends "manage_base.html" %} -{% block manage_content %} - -
    -
    -

    {{ _('Messages') }}

    -
    -
    -
    -
    -
    -

    {{ _('Information') }}

    -
    -
    - - -
    -
    -
    -
    -
    -
    {{ _('Gravatar') }}
    -
    {{ _('Name') }}
    {{ domain.name }}
    -
    -
    -
    -{% endblock %} diff --git a/templates/manage_module.html b/templates/manage_module.html deleted file mode 100644 index d616343cb..000000000 --- a/templates/manage_module.html +++ /dev/null @@ -1,55 +0,0 @@ -{% extends "manage_base.html" %} -{% block manage_content %} - -
    -
    -

    {{ _('Installed Modules') }}

    -
    - - {{ _("What's module?") }} -
    -
    -
    - - - - - - - - - - - - - - - - - {% for module in installed %} - - - - - - - {% endfor %} - -
    {{ _('ID') }}{{ _('Description') }}{{ _('Size') }}{{ _('Version') }}
    {{ module.id }}{{ _(module.description|default(_('None'))) }}{{ size(module.size) if module.size else '?' }}{{ module.version|default('?') }}
    -
    -
    -{% endblock %} diff --git a/templates/manage_script.html b/templates/manage_script.html deleted file mode 100644 index b813c148f..000000000 --- a/templates/manage_script.html +++ /dev/null @@ -1,56 +0,0 @@ -{% extends "manage_base.html" %} -{% block manage_content %} - -
    -
    -

    {{ _('Scripts') }}

    - -
    -
    - {{ noscript_note.render() }} - - - - - - - - - - - - - - - {% for id, script in scripts %} - {% if not script.hidden %} - - - - - - {% endif %} - {% endfor %} - -
    {{ _('ID') }}{{ _('Description') }}{{ _('Action') }}
    {{ id }}{{ _(script.description|default(_('None'))) }} - {{ _('Run') }} -
    -
    -
    -{% endblock %} diff --git a/templates/manage_setting.html b/templates/manage_setting.html deleted file mode 100644 index 0147a7b64..000000000 --- a/templates/manage_setting.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "manage_base.html" %} -{% block manage_content %} -
    - {% include "partials/setting.html" %} -
    -{% endblock %} diff --git a/templates/partials/contest_sidebar.html b/templates/partials/contest_sidebar.html deleted file mode 100644 index 4623c9660..000000000 --- a/templates/partials/contest_sidebar.html +++ /dev/null @@ -1,92 +0,0 @@ -{% import "components/user.html" as user with context %} -{% import "components/contest.html" as contest with context %} -
    -{% if page_name != 'contest_detail' %} - -
    -

    {{ tdoc.title }}

    -
    - {% if attended %} - {{ _('Attended') }} - {% else %} - {{ _('Not Attended') }} - {% endif %} -
    -
    -
    -{% endif %} -
    - -
    -
    -
    -
    {{ _('Status') }}
    {{ _(model.contest.statusText(tdoc)) }}
    -
    {{ _('Rule') }}
    {{ model.contest.RULES[tdoc.rule].TEXT }}
    -
    {{ _('Problem') }}
    {{ tdoc.pids.length }}
    -
    {{ _('Start at') }}
    {{ contest.render_time(tdoc.beginAt) }}
    -
    {{ _('End at') }}
    {{ contest.render_time(tdoc.endAt) }}
    -
    {{ _('Duration') }}
    {{ contest.render_duration(tdoc) }} {{ _('hour(s)') }}
    -
    {{ _('Host') }}
    {{ user.render_inline(owner_udoc, badge=false) }}
    -
    {{ _('Partic.') }}
    -
    - {{ tdoc['attend']|default(0) }} - {% if attended %} - ({{ _('Attended') }}) - {% endif %} -
    -
    -
    -
    diff --git a/templates/partials/discussion_edit_form.html b/templates/partials/discussion_edit_form.html deleted file mode 100644 index f3c1c68e8..000000000 --- a/templates/partials/discussion_edit_form.html +++ /dev/null @@ -1,24 +0,0 @@ -{% import "components/form.html" as form with context %} -{% if ddoc.highlight or handler.user.hasPerm(perm.PERM_HIGHLIGHT_DISCUSSION) %} - {{ form.form_checkbox({ - label:'Highlight', - name:'highlight', - value:ddoc.highlight|default(false) - }) }} -{% endif %} - {{ form.form_text({ - label:'Title', - name:'title', - value:ddoc.title|default(''), - autofocus:true, - required:true - }) }} - {{ form.form_textarea({ - columns:12, - label:'Content', - name:'content', - value:ddoc.content|default(''), - hotkeys:'ctrl+enter:submit', - markdown:true, - required:true - }) }} diff --git a/templates/partials/discussion_list.html b/templates/partials/discussion_list.html deleted file mode 100644 index 30b90af95..000000000 --- a/templates/partials/discussion_list.html +++ /dev/null @@ -1,46 +0,0 @@ -{% import "components/nothing.html" as nothing with context %} -{% import "components/user.html" as user with context %} -{% import "components/paginator.html" as paginator with context %} -{% if not ddocs.length %} - {{ nothing.render('no discussion yet...') }} -{% else %} -
      - {% for ddoc in ddocs %} -
    1. -
      -
      -
      -
      {{ ddoc.nReply }}
      -
      {{ _('Comments') }}
      -
      -
      - -
      -
    2. - {% endfor %} -
    - {% if page != undefined and dpcount != undefined %} - {{ paginator.render(page, dpcount) }} - {% endif %} -{% endif %} diff --git a/templates/partials/discussion_nodes_widget.html b/templates/partials/discussion_nodes_widget.html deleted file mode 100644 index 0e40c458a..000000000 --- a/templates/partials/discussion_nodes_widget.html +++ /dev/null @@ -1,17 +0,0 @@ -
    -
    -

    {{ _('Discussion Nodes') }}

    -
    -
      - {%- for category, nodes in vnodes|groupby('content') -%} -
    • -

      {{ category }}

      -
        - {%- for node in nodes -%} -
      1. {{ node.docId }}
      2. - {%- endfor -%} -
      -
    • - {%- endfor -%} -
    -
    \ No newline at end of file diff --git a/templates/partials/footer.html b/templates/partials/footer.html deleted file mode 100644 index 3237fe57f..000000000 --- a/templates/partials/footer.html +++ /dev/null @@ -1,74 +0,0 @@ -{% if not isIE(handler.request.headers['user-agent']) %} - -{% endif %} \ No newline at end of file diff --git a/templates/partials/hamburger.html b/templates/partials/hamburger.html deleted file mode 100644 index d7ad263c2..000000000 --- a/templates/partials/hamburger.html +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/templates/partials/header.html b/templates/partials/header.html deleted file mode 100644 index 8ba1591ff..000000000 --- a/templates/partials/header.html +++ /dev/null @@ -1,6 +0,0 @@ -
    -
    - -
    - {% include "partials/path.html" %} -
    diff --git a/templates/partials/header_mobile.html b/templates/partials/header_mobile.html deleted file mode 100644 index 266770a73..000000000 --- a/templates/partials/header_mobile.html +++ /dev/null @@ -1,12 +0,0 @@ -{% if no_path_section %} -
    -
    -
    -   -
    -
    - {% include "partials/hamburger.html" %} -
    -
    -
    -{% endif %} diff --git a/templates/partials/homework_default_penalty_rules.yaml b/templates/partials/homework_default_penalty_rules.yaml deleted file mode 100644 index 689730941..000000000 --- a/templates/partials/homework_default_penalty_rules.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Format: -# hours: coefficient -1: 0.9 -3: 0.8 -12: 0.75 -9999: 0.5 diff --git a/templates/partials/homework_sidebar.html b/templates/partials/homework_sidebar.html deleted file mode 100644 index 8347d2194..000000000 --- a/templates/partials/homework_sidebar.html +++ /dev/null @@ -1,85 +0,0 @@ -{% import "components/user.html" as user with context %} -{% import "components/homework.html" as homework with context %} -
    -{% if page_name != 'homework_detail' %} - -
    -

    {{ tdoc['title'] }}

    -
    - {% if attended %} - {{ _('Claimed') }} - {% else %} - {{ _('Not Claimed') }} - {% endif %} -
    -
    -
    -{% endif %} -
    - -
    -
    -
    -
    {{ _('Status') }}
    {{ _(model.contest.statusText(tdoc)) }}
    -
    {{ _('Problem') }}
    {{ tdoc['pids']|length }}
    -
    {{ _('Open Since') }}
    {{ homework.render_time(tdoc['beginAt']) }}
    -
    {{ _('Deadline') }}
    {{ homework.render_time(tdoc['penaltySince']) }}
    -
    {{ _('Can be Extended For') }}
    {{ homework.render_extension(tdoc) }} {{ _('hour(s)') }}
    -
    -
    -
    \ No newline at end of file diff --git a/templates/partials/login_dialog.html b/templates/partials/login_dialog.html deleted file mode 100644 index c80de9716..000000000 --- a/templates/partials/login_dialog.html +++ /dev/null @@ -1,52 +0,0 @@ - diff --git a/templates/partials/nav.html b/templates/partials/nav.html deleted file mode 100644 index 43833352c..000000000 --- a/templates/partials/nav.html +++ /dev/null @@ -1,76 +0,0 @@ -{%- macro nav_item(target_page_name, target_page_args, target_page_prefix) -%} - -{%- endmacro -%} - - - diff --git a/templates/partials/path.html b/templates/partials/path.html deleted file mode 100644 index b64378ceb..000000000 --- a/templates/partials/path.html +++ /dev/null @@ -1,19 +0,0 @@ -
    -{% if not no_path_section %} -
    -
    -
    -
    - {% for c in path %} - / {% if loop.index < path.length %}{{ c[0] if c[3] else _(c[0]) }}{% endif %} - {%- endfor %} -
    -

    {{ path[path.length - 1][0] if path[path.length - 1][3] else _(path[path.length - 1][0]) }}

    -
    -
    - {% include "partials/hamburger.html" %} -
    -
    -
    -{% endif %} -
    diff --git a/templates/partials/problem_default.md b/templates/partials/problem_default.md deleted file mode 100644 index a47706a29..000000000 --- a/templates/partials/problem_default.md +++ /dev/null @@ -1,30 +0,0 @@ -# Background -Special for beginners, ^_^ - -# Description -Given two integers x and y, print the sum. - -# Format - -## Input -Two integers x and y, satisfying $0\leq x,y\leq 32767$ . - -## Output -One integer, the sum of x and y. - -# Sample 1 - -## Input - -``` -123 500 -``` - -## Output - -``` -623 -``` - -# Limitation -1s, 1024KiB for each test case. diff --git a/templates/partials/problem_list.html b/templates/partials/problem_list.html deleted file mode 100644 index 7d328cf4d..000000000 --- a/templates/partials/problem_list.html +++ /dev/null @@ -1,54 +0,0 @@ -{% import "components/paginator.html" as paginator with context %} -{% import "components/record.html" as record with context %} -{% import "components/problem.html" as problem with context %} -{% import "components/nothing.html" as nothing with context %} -
    -{% if not pdocs.length %} - {{ nothing.render('Sorry, there is no problem in the problem set') }} -{% else %} - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - {% endif %} - - - - - - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - {% endif %} - - - - - - - - {% for pdoc in pdocs %} - {% set psdoc = psdict[pdoc['docId']] %} - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - {% if psdoc['rid'] %} - {{ record.render_status_td(psdoc, rid_key='rid') }} - {% else %} - - {% endif %} - {% endif %} - - - - - - {% endfor %} - -
    {{ _('Status') }}{{ _('Problem') }}{{ _('Submit') }}% {{ _('AC') }}{{ _('Difficulty') }}
    - - {{ problem.render_problem_title(pdoc) }} - {{ pdoc['nSubmit'] }}{{ (100 * pdoc['nAccept'] / pdoc['nSubmit'])|round|int if pdoc['nSubmit'] > 0 else _('?') }}{{ pdoc['difficulty'] or _('(None)') }}
    - {{ paginator.render(page, ppcount) }} -{% endif %} -
    diff --git a/templates/partials/problem_lucky.html b/templates/partials/problem_lucky.html deleted file mode 100644 index 19e5c466d..000000000 --- a/templates/partials/problem_lucky.html +++ /dev/null @@ -1,10 +0,0 @@ -
    -

    - - {{ _('Lucky') }} - -

    -
    -

    {{ _('Pick a problem randomly based on current filter') }}

    -
    -
    diff --git a/templates/partials/problem_sidebar.html b/templates/partials/problem_sidebar.html deleted file mode 100644 index 8bc871602..000000000 --- a/templates/partials/problem_sidebar.html +++ /dev/null @@ -1,17 +0,0 @@ -{% if tdoc %} - {% if tdoc.docType == 60 %} - {% include "partials/problem_sidebar_homework.html" %} - {% if not owner_udoc %} - {% set owner_udoc = udoc %} - {% endif %} - {% include "partials/homework_sidebar.html" %} - {% else %} - {% include "partials/problem_sidebar_contest.html" %} - {% if not owner_udoc %} - {% set owner_udoc = udoc %} - {% endif %} - {% include "partials/contest_sidebar.html" %} - {% endif %} -{% else %} - {% include "partials/problem_sidebar_normal.html" %} -{% endif %} diff --git a/templates/partials/problem_sidebar_contest.html b/templates/partials/problem_sidebar_contest.html deleted file mode 100644 index b3f5c253b..000000000 --- a/templates/partials/problem_sidebar_contest.html +++ /dev/null @@ -1,42 +0,0 @@ -{% import "components/user.html" as user with context %} -
    -{% if page_name != 'problem_detail' and page_name != 'contest_detail_problem' %} -
    -

    {{ pdoc['title'] }}

    -
    -{% endif %} -
    - -
    -
    diff --git a/templates/partials/problem_sidebar_homework.html b/templates/partials/problem_sidebar_homework.html deleted file mode 100644 index 7c53d6861..000000000 --- a/templates/partials/problem_sidebar_homework.html +++ /dev/null @@ -1,42 +0,0 @@ -{% import "components/user.html" as user with context %} -
    -{% if page_name != 'problem_detail' and page_name != 'homework_detail_problem' %} -
    -

    {{ pdoc['title'] }}

    -
    -{% endif %} -
    - -
    -
    \ No newline at end of file diff --git a/templates/partials/problem_sidebar_normal.html b/templates/partials/problem_sidebar_normal.html deleted file mode 100644 index 7b5a2dc02..000000000 --- a/templates/partials/problem_sidebar_normal.html +++ /dev/null @@ -1,168 +0,0 @@ -{% import "components/user.html" as user with context %} -{% import "components/problem.html" as problem with context %} -
    -{% if page_name != 'problem_detail' and page_name != 'contest_detail_problem' %} -
    -

    {{ pdoc['title'] }}

    -
    -{% endif %} -
    - -
    -
    - -
    -
    -

    {{ _('Information') }}

    -
    -
    -
    -
    {{ _('ID') }}
    -
    {{ pdoc.docId }}
    - {% if page_name != 'discussion_node' and page_name != 'discussion_detail' %} - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} -
    {{ _('Submission') }}
    -
    - {% if pdoc.psdoc.status %} - - - {{ model.builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }} - - {% else %} - {{ _('(None)') }} - {% endif %} - {% if pdoc['acMsg'] and pdoc['psdoc']['status'] == STATUS.STATUS_ACCEPTED %} -
    {{ _('AC Message') }}
    -
    {{ pdoc['acMsg'] }}
    - {% endif %} - - {% endif %} - {% endif %} -
    {{ _('Difficulty') }}
    -
    {{ pdoc['difficulty'] or _('(None)') }}
    -
    {{ _('Category') }}
    -
    - {% if pdoc['category'] %} - - {% for category in pdoc['category'] %} - {{ category }} - {% if loop.index < pdoc['category']|length %} - {{ _('problem-subcategory-delim') }} - {% endif %} - {% endfor %} - - - {{ _('page.problem_detail.sidebar.show_category') }} - - {% else %} - {{ _('(None)') }} - {% endif %} -
    -
    {{ _('Tags') }}
    -
    - {{ problem.render_problem_tags(pdoc, true) }} -
    -
    {{ _('# Submissions') }}
    -
    {{ pdoc.nSubmit }}
    - {% if page_name != 'discussion_node' and page_name != 'discussion_detail' %} - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} -
    {{ _('# My Submissions') }}
    -
    {{ pdoc['psdoc'].nSubmit|default(0) }}
    - {% endif %} - {% endif %} -
    {{ _('Accepted') }}
    -
    {{ pdoc.nAccept }}
    -
    {{ _('Accepted Ratio') }}
    -
    {% if pdoc.nSubmit > 0 %}{{ (100 * pdoc.nAccept / pdoc.nSubmit)|round|int }}%{% else %}{{ _('?') }}{% endif %}
    -
    {{ _('Uploaded By') }}
    -
    {{ user.render_inline(owner_udoc, badge=false) }}
    -
    -
    -
    -{% if page_name == 'problem_detail' and (tdocs or ctdocs) %} -
    -
    -

    {{ _('Related') }}

    -
    -
    - {% if tdocs %} -

    {{ _('In following training plans') }}:

    - {% for tdoc in tdocs %} -

    {{ tdoc.title }}

    - {% endfor %} - {% endif %} - {% if ctdocs %} -

    {{ _('In following contests') }}:

    - {% for tdoc in ctdocs %} -

    {{ tdoc.title }}

    - {% endfor %} - {% endif %} -
    -
    -{% endif %} diff --git a/templates/partials/problem_stat.html b/templates/partials/problem_stat.html deleted file mode 100644 index 56604e18d..000000000 --- a/templates/partials/problem_stat.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -

    {{ _('{0} problems').format(pcount) }}

    -
    \ No newline at end of file diff --git a/templates/partials/setting.html b/templates/partials/setting.html deleted file mode 100644 index f7eccd573..000000000 --- a/templates/partials/setting.html +++ /dev/null @@ -1,84 +0,0 @@ -
    -{% for family, family_settings in settings|groupby('family') %} -
    -

    {{ _(family) }}

    -
    -
    - {% for setting in family_settings %} - {% if not setting.flag|bitand(model.setting.FLAG_HIDDEN) %} - {% set secret = setting.flag|bitand(model.setting.FLAG_SECRET) != 0 %} - {% if setting.type == 'text' or setting.type == 'password' or setting.type == 'number' %} - {{ form.form_text({ - type:setting.type, - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:'' if secret else (current[setting.key]|default(setting.value)), - disabled:setting.disabled, - placeholder:_('(Not changed)') if secret else '' - }) }} - {% elif setting.type == 'select' %} - {{ form.form_select({ - options:setting.range, - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:current[setting.key] or setting.value, - disabled:setting.disabled - }) }} - {% elif setting.type == 'radio' %} - {{ form.form_radio({ - options:setting.range, - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:current[setting.key] or setting.value, - disabled:setting.disabled - }) }} - {% elif setting.type == 'image_radio' %} - {{ form.form_image_radio({ - options:setting.range, - image_class:setting.image_class, - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:current[setting.key] or setting.value, - disabled:setting.disabled - }) }} - {% elif setting.type == 'textarea' %} - {{ form.form_textarea({ - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:'' if secret else (current[setting.key]|default(setting.value)), - disabled:setting.disabled, - placeholder:_('(Not changed)') if secret else '' - }) }} - {% elif setting.type == 'markdown' %} - {{ form.form_textarea({ - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:'' if secret else (current[setting.key]|default(setting.value)), - markdown:true, - disabled:setting.disabled, - placeholder:_('(Not changed)') if secret else '' - }) }} - {% elif setting.type == 'boolean' %} - {{ form.form_checkbox({ - label:setting.name, - help_text:setting.desc, - name:setting.key, - value:current[setting.key] or setting.value, - disabled:setting.disabled - })}} - {% endif %} - {% endif %} - {% endfor %} -
    -{% endfor %} -
    - - -
    -
    \ No newline at end of file diff --git a/templates/partials/training_default.json b/templates/partials/training_default.json deleted file mode 100644 index f39debcae..000000000 --- a/templates/partials/training_default.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "_id": 1, - "title": "最初的最初 - A+B Problem", - "requireNids": [], - "pids": [1000, "56c52ade2fefd90539000005"] - }, - { - "_id": 2, - "title": "最初的进阶", - "requireNids": [1], - "pids": [1001, 1002] - } -] diff --git a/templates/problem_detail.html b/templates/problem_detail.html deleted file mode 100644 index bc29e6977..000000000 --- a/templates/problem_detail.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} - -
    -
    -
    -
    -
    -

    {{ pdoc['title'] }}

    -
    -
    - {% if not pdoc['data'] %} -
    -

    {{ _('No dataset at present.') }}

    -
    - {% elif typeof(pdoc.data) == 'string' %} -
    -

    {{ _('Test data comes from') }} {{ pdoc.data.split(':')[1] }}

    -
    - {% endif %} - {% if tdoc %} - {% if tdoc['docType'] == model.document.TYPE_CONTEST %} - {% if model.contest.isDone(tdoc) %} -
    -

    {{ _('You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.') }}

    -
    - {% endif %}{# handler.isDone(tdoc) #} - {% endif %}{# tdoc['docType'] == model.document.TYPE_CONTEST #} - {% endif %}{# tdoc #} - {{ pdoc['content']|markdown|safe }} -
    -
    -
    -
    -
    - {% set owner_udoc = udoc %} - {% include "partials/problem_sidebar.html" %} -
    -
    - -{% endblock %} diff --git a/templates/problem_edit.html b/templates/problem_edit.html deleted file mode 100644 index 0bcbfad11..000000000 --- a/templates/problem_edit.html +++ /dev/null @@ -1,64 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    - - {% if page_name == 'problem_edit' %} - - {% else %} - - {% endif %} -
    -
    -
    -
    -
    -
    - {% if page_name == 'problem_edit' %} - {% set owner_udoc = udoc %} - {% include "partials/problem_sidebar.html" %} - {% else %} - {% include 'components/md_hint.html' %} - {% endif %} -
    -
    -{% endblock %} diff --git a/templates/problem_import.html b/templates/problem_import.html deleted file mode 100644 index c1a9f23f3..000000000 --- a/templates/problem_import.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    -

    {{ _('What is this?') }}

    -

    {{ _('With this feature, you can import problems that you can view from a site to here. Their title, content, tags and categories will be imported. However, their test data are not copied directly.') }}

    -

    {{ _('About test data') }}

    -

    {{ _('Instead of copying the test data directly, the test data of the import problems will be linked to the test data of the source problems (called the source test data). Thus, the copied problems can observe the changes in the source test data. The permissions of the test data of the copied problems follow the source test data, e.g., you still might not download them but the judges can. By uploading some new test data, the link will be broken and the new test data will be used.') }}

    -
    -
    -
    -
    -{% endblock %} diff --git a/templates/problem_main.html b/templates/problem_main.html deleted file mode 100644 index 249f1a138..000000000 --- a/templates/problem_main.html +++ /dev/null @@ -1,94 +0,0 @@ -{% import "components/record.html" as record with context %} -{% import "components/problem.html" as problem with context %} -{% extends "layout/basic.html" %} -{% block content %} - -
    -
    -
    -
    - {% include "partials/problem_list.html" %} -
    -
    -
    -
    -
    -
    -

    {{ _('Categories') }}

    -
    -
    -
      - {% for category, sub_categories in model.builtin.CATEGORIES %} -
    • -

      - {{ category }} -

      - {% if sub_categories | length > 0 %} -
        - {% for sub_category in sub_categories %} -
      1. - {{ sub_category }} -
      2. - {% endfor %} -
      - {% endif %} -
    • - {% endfor %} -
    -
    -
    - {% include "partials/problem_lucky.html" %} -
    -
    -
    -
    -

    {{ _('Search') }}

    -
    -
    -
    - - -
    -
    -
    - {% if handler.user.hasPerm(perm.PERM_CREATE_PROBLEM) %} -
    -
    -

    {{ _('Create Problem') }}

    -
    - -
    -
    -

    {{ _('You have permissions to create problems for this domain.') }}

    -
    -
    -
    - {% endif %} -
    -
    -

    {{ _('Statistics') }}

    -
    - {% include "partials/problem_stat.html" %} -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/problem_settings.html b/templates/problem_settings.html deleted file mode 100644 index 6c6cdd445..000000000 --- a/templates/problem_settings.html +++ /dev/null @@ -1,99 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -

    {{ _('Dataset') }}

    -
    -
    - -

    {{ _('Hint') }}: {{ _('Dataset Format') }}, {{ _('An example of dataset') }}: {{ _('Download') }}

    - {% if pdoc and (pdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %} -

    {{ _('Download Dataset') }}

    - {% endif %} -
    -
    -
    -
    -

    {{ _('Testdata Config') }}

    -
    -
    -
    - {{ form.form_textarea({ - columns:null, - label:'Config', - name:'yaml', - value:pdoc['config']|default(''), - yaml:true - }) }} -
    - - - -
    -
    -
    -
    -
    -
    -

    {{ _('Settings') }}

    -
    -
    -
    - {{ form.form_checkbox({ - label:'Hidden', - help_text:'Note: Problem title may not be hidden.', - name:'hidden', - value:pdoc['hidden'] - }) }} - {{ form.form_text({ - columns:12, - label:'Category', - help_text:'Format: category 1, sub category 1.1, sub category 1.2, ..., sub category 1.x, ..., category n, sub category n.1, sub category n.2, ..., sub category n.m, ...', - name:'category', - value:pdoc['category']|join(', ') - }) }} - {{ form.form_text({ - columns:12, - label:'Tags', - help_text:'Splitted by \', \'.', - name:'tag', - value:pdoc['tag']|join(', ') - }) }} -
    - - - -
    -
    -
    -
    -
    -
    -

    {{ _('Categories') }} ({{ _('click to add') }})

    -
    -
    - {% for category, sub_categories in model.builtin.CATEGORIES %} -
    -

    {{ category }}

    -
      - {% for sub_category in sub_categories %} -
    1. {{ sub_category }}
    2. - {% endfor %} -
    -
    - {% endfor %} -
    -
    -
    -
    - {% set owner_udoc = udoc %} - {% include "partials/problem_sidebar.html" %} -
    -
    -{% endblock %} diff --git a/templates/problem_solution.html b/templates/problem_solution.html deleted file mode 100644 index ae97610ee..000000000 --- a/templates/problem_solution.html +++ /dev/null @@ -1,48 +0,0 @@ -{% extends "layout/basic.html" %} -{% import "components/comments_solution.html" as comments with context %} -{% block content %} -
    -
    -
    -
    -

    {{ _('{0} solutions').format(pscount) }}

    -
    -
    - {{ comments.render( - view = 'solution', - docs = psdocs, - udict = udict, - comment_ref = 'psid', - reply_ref = 'psrid', - comment_placeholder = 'Write Your Solution', - comment_post_op = 'submit', - comment_edit_op = 'edit_solution', - comment_post_text = 'Share', - reply_post_op = 'reply', - reply_edit_op = 'edit_reply', - comment_delete_op = 'delete_solution', - reply_delete_op = 'delete_reply', - comment_post_perm = perm.PERM_CREATE_PROBLEM_SOLUTION, - comment_edit_perm = perm.PERM_EDIT_PROBLEM_SOLUTION, - comment_edit_self_perm = perm.PERM_EDIT_PROBLEM_SOLUTION_SELF, - comment_delete_perm = perm.PERM_DELETE_PROBLEM_SOLUTION, - comment_delete_self_perm = perm.PERM_DELETE_PROBLEM_SOLUTION_SELF, - reply_post_perm = perm.PERM_REPLY_PROBLEM_SOLUTION, - reply_edit_perm = perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY, - reply_edit_self_perm = perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF, - reply_delete_perm = perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY, - reply_delete_self_perm = perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF - ) }} - {{ paginator.render(page, pcount) }} - {% if drcount == 0 %} - {{ nothing.render('No solutions so far...') }} - {% endif %} -
    -
    -
    -
    - {% set owner_udoc = udict[pdoc.owner] %} - {% include "partials/problem_sidebar.html" %} -
    -
    -{% endblock %} diff --git a/templates/problem_statistics.html b/templates/problem_statistics.html deleted file mode 100644 index ce67d0ddf..000000000 --- a/templates/problem_statistics.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -

    {{ _('Submission Statistics') }}

    -
    -
    - {{ _('Swx Template') }} -
    -
    -
    -
    - {% set owner_udoc = udoc %} - {% include "partials/problem_sidebar.html" %} - {% endwith %} -
    -
    -{% endblock %} diff --git a/templates/problem_submit.html b/templates/problem_submit.html deleted file mode 100644 index 7b497a98e..000000000 --- a/templates/problem_submit.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    - {% if not tdoc or rdocs.length %} -
    -
    -

    {{ _('Recent 10 Records') }}

    -
    -
    - {% if not rdocs.length %} - {{ nothing.render('You have not submitted any solutions for this problem') }} - {% else %} - - - - - - - - - - - - - - - - - - {% for rdoc in rdocs %} - {% include 'problem_submit_tr.html' %} - {% endfor %} - -
    {{ _('Status') }}{{ _('Peak Memory') }}{{ _('Total Time') }}{{ _('Submit At') }}
    - {% endif %} -
    -
    - {% endif %} -
    -
    -

    {{ _('Submit to Judge') }}

    -
    -
    -
    - {{ form.form_select({ - label:'Code language', - options:model.builtin.LANG_TEXTS, - name:'lang', - value:handler.user.codeLang - }) }} - {{ form.form_textarea({label:'Code', columns:12, name:'code', extra_class:'monospace', autofocus:true}) }} -
    - - -
    -
    -
    -
    -
    -
    - {% set owner_udoc = udoc %} - {% include "partials/problem_sidebar.html" %} -
    -
    - -{% endblock %} diff --git a/templates/problem_submit_tr.html b/templates/problem_submit_tr.html deleted file mode 100644 index acb21a969..000000000 --- a/templates/problem_submit_tr.html +++ /dev/null @@ -1,18 +0,0 @@ -{% import "components/record.html" as record with context %} - - {{ record.render_status_td(rdoc) }} - - {% if rdoc['type'] == vj4.constant.record.TYPE_PRETEST %} - {{ _('Yes') }} - {% else %} - {{ _('No') }} - {% endif %} - - - {{ (rdoc.memory / 1000)|round(0, 'ceil')|int }} MB - - - {{ (rdoc.time / 1000)|round(1) }}s - - {{ datetimeSpan(rdoc['_id'].generationTime) }} - diff --git a/templates/problem_upload.html b/templates/problem_upload.html deleted file mode 100644 index 8079130d4..000000000 --- a/templates/problem_upload.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'layout/simple.html' %} -{% block body %} -
    - {% if (typeof(pdoc.data) == 'string') %} -

    {{ _('Current dataset comes from') }} {{ pdoc.data.split('from:')[1] }}

    - {% else %} -

    {{ _('Current dataset: {0}').format((pdoc['data'] or _('(None)')).toString()) }}

    -

    {{ _('MD5') }}: {{ md5|default(_('(None)')) }}

    - {% endif %} -

    {{ _('New dataset') }}: - - - -

    -
    -{% endblock %} diff --git a/templates/ranking.html b/templates/ranking.html deleted file mode 100644 index 6c6dfa554..000000000 --- a/templates/ranking.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "layout/basic.html" %} -{% import "components/paginator.html" as paginator with context %} -{% import "components/nothing.html" as nothing with context %} -{% block content %} -
    -
    -
    -
    -
    - {% if not udocs.length %} - {{ nothing.render('Sorry, there is no user in this domain.') }} - {% else %} - - - - - - - - - - - - - - - {% for udoc in udocs %} - - - - - - {% endfor %} - -
    {{ _('Rank') }}{{ _('Username') }}{{ _('Rating') }}
    {{ (page - 1) * 100 + loop.index }}{{ user.render_inline(udoc, badge=false) }}{{ udoc.rating|default(1500)|round(0) }}
    - {{ paginator.render(page, upcount) }} - {% endif %} -
    -
    -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/record_detail.html b/templates/record_detail.html deleted file mode 100644 index 8b19a9ba4..000000000 --- a/templates/record_detail.html +++ /dev/null @@ -1,119 +0,0 @@ -{% import "components/problem.html" as problem with context %} -{% extends "layout/basic.html" %} -{% block content %} - -
    -
    - {% if showStatus %} - {% include 'record_detail_status.html' %} - {% endif %} - {% if rdoc['code'] %} -
    -
    -

    {{ _('Code') }}

    -
    -
    -
    {{ rdoc['code'] }}
    -
    -
    - {% endif %} - {% if rdoc['stdout'] or rdoc['stderr'] %} -
    -
    -

    {{ _('Stdout') }}

    -
    - {% if rdoc['stdout'] %} -
    -
    {{ rdoc['stdout']|ansi|safe }}
    -
    - {% endif %} - {% if rdoc['stderr'] %} -
    -
    {{ rdoc['stderr']|ansi|safe }}
    -
    - {% endif %} -
    - {% endif %} -
    -
    -
    -
    -

    {{ _('Information') }}

    -
    - {% if handler.user.hasPerm(perm.PERM_REJUDGE) %} -
    - -
    - {% endif %} -
    -
    -
    {{ _('Submit By') }}
    -
    {{ user.render_inline(udoc, modbadge=false) }}
    - {% if pdoc %} -
    {{ _('Problem') }}
    -
    - {{ problem.render_problem_title(pdoc, tdoc, show_tags=false, show_invisible_flag=false, rp=none) }} -
    - {% endif %} - {% if tdoc %} -
    {{ _('Contest') }}
    -
    - {{ tdoc.title }} -
    - {% endif %} - {% if pdoc and (handler.user._id == pdoc.owner or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %} -
    {{ _('Problem Data') }}
    -
    {{ _('Download') }}
    - {% endif %} -
    {{ _('Language') }}
    -
    - {{ model.builtin.LANG_TEXTS[rdoc['lang']] }} -
    -
    - {{ _('Submit At') }} -
    -
    - {{ datetimeSpan(rdoc._id)|safe }} -
    - {% if rdoc.judgeAt %} -
    {{ _('Judged At') }}
    -
    {{ datetimeSpan(rdoc.judgeAt)|safe }}
    - {% endif %} - {% if judge_udoc %} -
    {{ _('Judged By') }}
    -
    {{ user.render_inline(judge_udoc, badge=false) }}
    - {% endif %} -
    - {% if show_status %} - {% include 'record_detail_summary.html' %} - {% endif %} -
    -
    -
    -
    -{% endblock %} diff --git a/templates/record_detail_status.html b/templates/record_detail_status.html deleted file mode 100644 index f81af4635..000000000 --- a/templates/record_detail_status.html +++ /dev/null @@ -1,59 +0,0 @@ -
    -
    -

    - - - {{ model.builtin.STATUS_TEXTS[rdoc['status']] }} - -

    -
    -{% if rdoc.compilerTexts.length %} -
    -
    {{ rdoc.compilerTexts|join('\n')|ansi|safe }}
    -
    -{% endif %} -{% if rdoc.judgeTexts.length %} -
    -
    {{ rdoc.judgeTexts|join('\n')|ansi|safe }}
    -
    -{% endif %} -{% if rdoc.testCases.length %} -
    - - - - - - - - - - - - - - - - - {% for rcdoc in rdoc.testCases %} - - - - - - - {% endfor %} - -
    #{{ _('Status') }}{{ _('Time Cost') }}{{ _('Memory Cost') }}
    - #{{ loop.index }} - - - - {{ model.builtin.STATUS_TEXTS[rcdoc['status']] }} - - {{ rcdoc['judgeText'] }} - - {% if rcdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rcdoc.time }}ms{% if rcdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory, 1024) }}
    -
    -{% endif %} -
    diff --git a/templates/record_detail_summary.html b/templates/record_detail_summary.html deleted file mode 100644 index 507749c25..000000000 --- a/templates/record_detail_summary.html +++ /dev/null @@ -1,10 +0,0 @@ -
    - {% if rdoc['score'] is defined %} -
    {{ _('Score') }}
    -
    {{ rdoc['score'] }}
    - {% endif %} -
    {{ _('Total Time') }}
    -
    {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms
    -
    {{ _('Peak Memory') }}
    -
    {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }}
    -
    diff --git a/templates/record_main.html b/templates/record_main.html deleted file mode 100644 index 7ee75ec6f..000000000 --- a/templates/record_main.html +++ /dev/null @@ -1,113 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} - -
    -
    -
    -
    -
    -

    {{ _('Filter') }}

    -
    - - {{ _('Reset') }} -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -
    - {% if not rdocs %} - {% if not filterUidOrName and not filterPid and not filterTid %} - {{ nothing.render('Oh, there is no task in the queue!') }} - {% else %} - {{ nothing.render('Oh, there is no task that matches the filter!') }} - {% endif%} - {% else %} - - - - - - - - - - - - - - - - - - - - - - - {% for rdoc in rdocs %} - {% set udoc = udict[rdoc['uid']] %} - {% set pdoc = pdict[rdoc['pid']] %} - {% include 'record_main_tr.html' %} - {% endfor %} - -
    {{ _('Status') }}{{ _('Problem') }}{{ _('Submit By') }}{{ _('Time') }}{{ _('Memory') }}{{ _('Language') }}{{ _('Submit At') }}
    - {% if handler.user.hasPriv(PRIV.PRIV_VIEW_JUDGE_STATISTICS) %} - - {% endif %} - {% endif %} -
    -
    - {% if statistics %} -
    -
    -

    {{ _('Statistics') }}

    -
    -
    -
    -
    {{ _('Day') }}
    {{ statistics['day'] }}
    -
    {{ _('Week') }}
    {{ statistics['week'] }}
    -
    {{ _('Month') }}
    {{ statistics['month'] }}
    -
    {{ _('Year') }}
    {{ statistics['year'] }}
    -
    {{ _('Total') }}
    {{ statistics['total'] }}
    -
    -
    -
    - {% endif %} -
    -
    -{% endblock %} diff --git a/templates/record_main_tr.html b/templates/record_main_tr.html deleted file mode 100644 index 80d94589b..000000000 --- a/templates/record_main_tr.html +++ /dev/null @@ -1,30 +0,0 @@ -{% import "components/record.html" as record with context %} -{% import "components/problem.html" as problem with context %} - - {{ record.render_status_td(rdoc) }} - - {% if handler.user.hasPerm(perm.PERM_REJUDGE) %} -
    - - | -
    - {% endif %} - {% if pdoc and (not pdoc.hidden or handler.user.hasPerm(perm.PERM_VIEW_PROBLEM_HIDDEN)) %} - {{ problem.render_problem_title(pdoc, show_tags=false) }} - {% else %} - * - {% endif %} - - - - {{ udoc['uname'] }} - - - {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms - {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }} - {{ model.builtin.LANG_TEXTS[rdoc['lang']] }} - {{ datetimeSpan(rdoc['_id'])|safe }} - diff --git a/templates/status.html b/templates/status.html deleted file mode 100644 index 793511e4e..000000000 --- a/templates/status.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - {% for doc in stats %} - - - - - - - - {% endfor %} - -
    {{ _('ID') }}{{ _('Current Status') }}{{ _('OS Info') }}{{ _('CPU Info') }}{{ _('Memory Info') }}
    {{ doc._id }}{% if doc.isOnline %}{{ doc.status }}{% else %}{{ datetimeSpan(doc.updateAt)|safe }}{% endif %}{{ doc.osinfo }}{{ doc.cpu }}{{ doc.memory }}
    -
    -
    -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/templates/training_detail.html b/templates/training_detail.html deleted file mode 100644 index 3082e7c6f..000000000 --- a/templates/training_detail.html +++ /dev/null @@ -1,201 +0,0 @@ -{% import "components/record.html" as record with context %} -{% import "components/problem.html" as problem with context %} -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    - {{ tdoc['content'] }} -
    -
    - {{ tdoc['desc']|default('')|markdown|safe }} -
    -
    -
    - {% if not tsdoc.enroll %} -
    -
    -

    {{ _('page.training_detail.invalid_when_not_enrolled') }}

    -
    -
    - {% endif %} - {% for node in tdoc['dag'] %} -
    -
    -
    -

    {{ _('Section') }} {{ node['_id'] }}. {{ node['title'] }}

    -
    -
    -

    - - {% if nsdict[node['_id']]['isDone'] %} - {{ _('Completed') }} - {% elif nsdict[node['_id']]['isProgress'] %} - {{ _('In Progress') }} - {% elif nsdict[node['_id']]['isOpen'] %} - {{ _('Open') }} - {% else %} - {{ _('Invalid') }} - {% endif %} -

    -
    -
    - -
    - {% if nsdict[node['_id']]['isInvalid'] %} -
    -
    -

    {{ _('This section cannot be challenged at present, so please complete the following sections first') }}:

    -
      - {% for nid in node['require_nids'] %} -
    • {{ _('Section') }} {{ _(nid) }}. {{ ndict[nid]['title'] }} ({{ _('Completed') }} {{ nsdict[nid]['progress'] }}%)
    • - {% endfor %} -
    -
    -
    - {% endif %} - {% if node['content'] %} -
    - {{ node['content']|markdown|safe }} -
    - {% endif %} -
    - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - {% endif %} - - - - - - - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - - {% endif %} - - - - - - - - {% for pid in node['pids'] %} - {% if pid in pdict %} - {% set pdoc=pdict[pid] %} - {% set psdoc=psdict[pid] %} - - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - {% if psdoc['rid'] %} - {{ record.render_status_td(psdoc, rid_key='rid') }} - {% else %} - - {% endif %} - {% endif %} - - - - - - {% else %} - {% set pdoc = {'_id': pid, 'hidden': true, 'title': '*'} %} - - - - - - - - {% endif %} - {% endfor %} - -
    {{ _('Status') }}{{ _('Problem') }}{{ _('Submit') }}% {{ _('AC') }}{{ _('Difficulty') }}
    - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} -
    - - - - -
    - {% endif %} - {{ problem.render_problem_title( - pdoc, - invalid=not tsdoc['enroll'], - show_tags=false - ) }} -
    {{ pdoc.nSubmit }}{{ (100 * pdoc.nAccept / pdoc.nSubmit)|round|int if pdoc.nSubmit > 0 else _('?') }}{{ pdoc['difficulty'] or _('(None)') }}
    - - {{ problem.render_problem_title(pdoc, invalid=true) }} - ***
    -
    -
    -
    - {% endfor %} -
     
    -
    -
    -
    -
    -
    - -
    -
    -
    - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} -
    {{ _('Status') }}
    {% if tsdoc['enroll'] %}{{ _('Completed' if tsdoc['done'] else 'In Progress') }}{% else %}{{ _('Not Enrolled') }}{% endif %}
    - {% endif %} - {% if tsdoc['enroll'] %} -
    {{ _('Progress') }}
    {{ _('Completed') }} {{ (100 * tsdoc['donePids']|length / pids|length)|round|int }}%
    - {% endif %} -
    {{ _('Enrollees') }}
    {{ tdoc['enroll']|default(0) }}
    -
    {{ _('Created By') }}
    -
    {{ user.render_inline(owner, badge=false) }}
    -
    -
    -
    -
    -
    -{% endblock %} diff --git a/templates/training_edit.html b/templates/training_edit.html deleted file mode 100644 index 2f05fe688..000000000 --- a/templates/training_edit.html +++ /dev/null @@ -1,58 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - - {% if page_name == 'training_edit' %} - - {% else %} - - {% endif %} - -
    -
    -
    -
    -
    -
    - {% include 'components/md_hint.html' %} -
    -
    -{% endblock %} diff --git a/templates/training_main.html b/templates/training_main.html deleted file mode 100644 index 932481f94..000000000 --- a/templates/training_main.html +++ /dev/null @@ -1,98 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    -
    -
    -

    {{ _('All Training Plans') }}

    -
    - {% if not tdocs.length %} - {{ nothing.render('Sorry, there is no training plan.') }} - {% else %} -
      - {% for tdoc in tdocs %} -
    1. -
      -
      -
      -
      {{ tdoc.enroll|default(0) }}
      -
      {{ _('Enrolled') }}
      -
      -
      -
      -

      {{ tdoc.title }}

      -
      -

      {{ tdoc['content'] }}

      -
      -
        -
      • - {{ _('{0} sections').format(tdoc['dag']|length) }}, {{ _('{0} problems').format(model.training.getPids(tdoc)|length) }} -
      • -
      • - {% if tsdict[tdoc.docId]['enroll'] %} - {% if not tsdict[tdoc.docId]['done'] %} - - {{ _('Completed') }} {{ (100 * tsdict[tdoc.docId]['donePids']|length / model.training.getPids(tdoc)|length)|round|int }}% - {% else %} - - {{ _('Completed') }} 100% - {% endif %} - {% elif handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} - {{ _('Not Enrolled') }} - {% endif %} -
      • -
      -
      -
      -
    2. - {% endfor %} -
    - {{ paginator.render(page, tpcount, add_qs=qs) }} - {% endif %} -
    -
    -
    - {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} -
    -
    -

    {{ _('Enrolled') }}

    -
    -
    -
      - {% for tsdoc in tsdict %} - {% if tsdoc['enroll'] %} -
    1. -
      - -
      -
      -

      {{ tdict[tsdoc['docId']]['title'] }}

      -
      {{ _('Complete') }} {{ (100 * tsdoc['donePids']|length / handler.getPids(tdict[tsdoc.docId])|length)|round|int }}%
      -
      -
    2. - {% endif %} - {% endfor %} -
    -
    -
    - {% endif %} - {% if handler.user.hasPerm(perm.PERM_CREATE_TRAINING) %} -
    -
    -

    {{ _('Create Training Plan') }}

    -
    - -
    -
    -

    {{ _('You can create your own training plans and share with others.') }}

    -
    -
    -
    - {% endif %} -
    -
    -{% endblock %} diff --git a/templates/user_changemail_mail.html b/templates/user_changemail_mail.html deleted file mode 100644 index a1d81946b..000000000 --- a/templates/user_changemail_mail.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "layout/mail.html" %} -{% block title %}{{ _('Change Email') }}{% endblock %} -{% block content %} -

    {{ _('Hello, {0}! You can click following link to active your new email of your Hydro account:').format(uname) }}

    -

    {{ url_prefix }}{{ url }}

    -{% endblock %} diff --git a/templates/user_changemail_mail_sent.html b/templates/user_changemail_mail_sent.html deleted file mode 100644 index 2475cf394..000000000 --- a/templates/user_changemail_mail_sent.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -

    {{ _('New Email') }}

    -
    - {{ _('Confirmation mail has been sent to your new email.') }} -
    -
    -{% endblock %} diff --git a/templates/user_delete_pending.html b/templates/user_delete_pending.html deleted file mode 100644 index c76e9385b..000000000 --- a/templates/user_delete_pending.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -

    {{ _('Done') }}

    -
    - {{ _('Your account will be deleted in 7 days.') }} - {{ _('You can cancel this at user setting page.') }} -
    -
    -{% endblock %} diff --git a/templates/user_detail.html b/templates/user_detail.html deleted file mode 100644 index 499b8f769..000000000 --- a/templates/user_detail.html +++ /dev/null @@ -1,185 +0,0 @@ -{% import "components/home.html" as home with context %} -{% extends "layout/basic.html" %} -{% block content %} - -
    -
    -
    - -
    -
    -
      -
    • -

      {{ _('Bio') }}

      -
      - {% if not udoc.bio %} - {{ nothing.render("This person is lazy and didn't wrote anything.") }} - {% else %} - {{ udoc.bio|markdown|safe }} - {% endif %} -
      -
    • -
    • -

      {{ _('Contributions') }} ({{ pcount }})

      -
      - {% if not pdocs %} - {{ nothing.render("Oh, the user doesn't have any contributions!") }} - {% else %} -
      -

      {{ _('Contributed Problems') }}

      -
      -
      - {% for pdoc in pdocs %} -

      {{ pdoc['title'] }}

      - {% endfor %} -
      - {% endif %} -
      -
    • -
    • -

      {{ _('Submissions') }} ({{ udoc['nSubmit']|default(0) }})

      -
      - {% if not rdocs %} - {{ nothing.render("Oh, the user hasn't submitted yet!") }} - {% else %} -
      -

      {{ _('Recent Submissions') }}

      -
      -
      - - - - - - - - - - - - - - - - - - - - - - - {% for rdoc in rdocs %} - {% set pdoc = pdict[rdoc.pid] %} - {% include 'record_main_tr.html' %} - {% endfor %} - -
      {{ _('Status') }}{{ _('Problem') }}{{ _('Submit By') }}{{ _('Time') }}{{ _('Memory') }}{{ _('Language') }}{{ _('Submit At') }}
      -
      - {% endif %} -
      -
    • -
    -
    -
    -
    -
    -
    - {% if isSelfProfile %} - {{ home.render_sidebar() }} - {% endif %} -
    -
    - -
    -
    -
    -
    -{% endblock %} diff --git a/templates/user_login.html b/templates/user_login.html deleted file mode 100644 index 4f684f181..000000000 --- a/templates/user_login.html +++ /dev/null @@ -1,48 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -
    -

    {{ _('Login') }}

    -
    -
    - -
    -
    - -
    -
    - -
    - {% if loginWithGithub %} - - {% endif %} - {% if loginWithGoogle %} - - {% endif %} -
    -
    - - -
    -
    - -
    -
    -
    -{% endblock %} diff --git a/templates/user_logout.html b/templates/user_logout.html deleted file mode 100644 index 8fcfb1834..000000000 --- a/templates/user_logout.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -
    -

    {{ _('Logout') }}

    -
    - -
    - -
    -
    -
    -
    -{% endblock %} diff --git a/templates/user_lostpass.html b/templates/user_lostpass.html deleted file mode 100644 index 3117a63e5..000000000 --- a/templates/user_lostpass.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -
    -

    {{ _('Lost Password') }}

    -
    -
    - -
    -
    -
    - - -
    -
    -
    -
    -
    -{% endblock %} diff --git a/templates/user_lostpass_mail.html b/templates/user_lostpass_mail.html deleted file mode 100644 index 723d72434..000000000 --- a/templates/user_lostpass_mail.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "layout/mail.html" %} -{% block title %}{{ _('Lost Password') }}{% endblock %} -{% block content %} -

    {{ _('Hello, {0}! You can click following link to reset the password of your Hydro account:').format(uname) }}

    -

    {{ url_prefix }}{{ url }}

    -{% endblock %} diff --git a/templates/user_lostpass_mail_sent.html b/templates/user_lostpass_mail_sent.html deleted file mode 100644 index bbeeb936e..000000000 --- a/templates/user_lostpass_mail_sent.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -

    {{ _('Lost Password') }}

    -
    - {{ _('Password reset mail has been sent to your email.') }} -
    -
    -{% endblock %} diff --git a/templates/user_lostpass_with_code.html b/templates/user_lostpass_with_code.html deleted file mode 100644 index ebb6ee07d..000000000 --- a/templates/user_lostpass_with_code.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -

    {{ _('Lost Password') }}

    -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    -{% endblock %} diff --git a/templates/user_register.html b/templates/user_register.html deleted file mode 100644 index ad453cccf..000000000 --- a/templates/user_register.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -
    -

    {{ _('Sign Up') }}

    -
    -
    - -
    -
    -
    - - -
    -
    -
    -
    -
    -{% endblock %} diff --git a/templates/user_register_mail.html b/templates/user_register_mail.html deleted file mode 100644 index aa59f2af2..000000000 --- a/templates/user_register_mail.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "layout/mail.html" %} -{% block title %}{{ _('Sign Up') }}{% endblock %} -{% block content %} -

    {{ _('Hello! You can click following link to sign up your Hydro account:') }}

    -

    {{ url_prefix|safe }}{{ path|safe }}

    -{% endblock %} diff --git a/templates/user_register_mail_sent.html b/templates/user_register_mail_sent.html deleted file mode 100644 index 1167c7c8f..000000000 --- a/templates/user_register_mail_sent.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -

    {{ _('Sign Up') }}

    -
    - {{ _('Sign up mail has been sent to your email.') }} -
    -
    -{% endblock %} diff --git a/templates/user_register_with_code.html b/templates/user_register_with_code.html deleted file mode 100644 index 751f2e242..000000000 --- a/templates/user_register_with_code.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "layout/immersive.html" %} -{% block content %} -
    -

    {{ _('Sign Up') }}

    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    -{% endblock %} diff --git a/templates/wiki.html b/templates/wiki.html deleted file mode 100644 index a0b719be7..000000000 --- a/templates/wiki.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "layout/basic.html" %} -{% block content %} -
    -
    - {% for content in contents %} -
    -
    -

    {{ _(content.title) }}

    -
    -
    - {{ _('no_translation_warn')|safe }} - {{ content.content|markdown(false)|safe }} -
    -
    - {% endfor %} -
    -
    -
    - -
    -
    -
    -{% endblock %} diff --git a/tool/checkTranslation.js b/tool/checkTranslation.js index 7ac211d49..67018653e 100644 --- a/tool/checkTranslation.js +++ b/tool/checkTranslation.js @@ -9,78 +9,19 @@ const IGNORE_CHECK = ['en']; const IGNORE_MISSING = [ '?', 'AC', 'Gravatar', 'ID', 'MD5', 'URL', ]; -const RE_TEMPLATE = /_\(['"]([\s\S])*?['"]\)/gmi; -const RE_UI = /i18n\(['"]([\s\S])*?['"](,.*?)?\)/gmi; const RE_SETTING = /Setting\(['"]([\s\S])*?['"](,[^()]*?)\)/gmi; -const TEMPLATE_ROOT = path.resolve(__dirname, '..', 'templates'); const LOCALE_ROOT = path.resolve(__dirname, '..', 'locales'); -const UI_ROOT = path.resolve(__dirname, '..', 'ui'); -const IGNORE_NAME = ['node_modules']; const texts = {}; const result = {}; const locales = fs.readdirSync(LOCALE_ROOT); let currentFile = ''; // eslint-disable-next-line @typescript-eslint/no-unused-vars -function _(str, format) { +function Setting(str, format) { if (!texts[str]) texts[str] = [currentFile]; else texts[str].push(currentFile); } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const i18n = _; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const Setting = _; -function scanTemplate(folder, relative = 'templates') { - const files = fs.readdirSync(folder); - for (const file of files) { - if (!IGNORE_NAME.includes(file)) { - const p = path.join(folder, file); - if (fs.statSync(p).isDirectory()) { - scanTemplate(p, path.join(relative, file)); - } else { - currentFile = path.join(relative, file); - const f = fs.readFileSync(p).toString(); - f.replace(RE_TEMPLATE, (substr) => { - try { - // eslint-disable-next-line no-eval - eval(substr); - } catch (e) { - console.error('Cannot parse: ', substr, ' in file ', p); - } - }); - } - } - } -} -function scanUi(folder, relative = 'ui') { - const files = fs.readdirSync(folder); - for (const file of files) { - if (!IGNORE_NAME.includes(file)) { - const p = path.join(folder, file); - if (fs.statSync(p).isDirectory()) { - scanUi(p, path.join(relative, file)); - } else { - currentFile = path.join(relative, file); - const f = fs.readFileSync(p).toString(); - f.replace(RE_UI, (substr) => { - try { - eval(substr); - } catch (e) { - if (e.message.endsWith('is not defined')) { - global[e.message.split(' ')[0]] = () => { }; - try { - eval(substr); - } catch (err) { - console.error('Cannot parse: ', substr, ' in file ', p); - } - } else console.error('Cannot parse: ', substr, ' in file ', p); - } - }); - } - } - } -} function scanSetting() { const file = path.resolve(__dirname, '..', 'hydro', 'model', 'setting.ts'); const content = fs.readFileSync(file).toString(); @@ -99,8 +40,6 @@ function scanSetting() { } }); } -scanTemplate(TEMPLATE_ROOT); -scanUi(UI_ROOT); scanSetting(); if (!name) { for (const locale of locales) { diff --git a/tsconfig.json b/tsconfig.json index 82f4e35e2..e78d54560 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,12 +5,15 @@ "outDir": "./dist/", "rootDir": "./hydro/", "moduleResolution": "node", + "declaration": true, + "sourceMap": true, "esModuleInterop": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "allowJs": false }, "exclude": [ "node_modules", - "ui", - "build" + "dist", + "tool" ] } \ No newline at end of file diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js deleted file mode 100644 index 6e198ed41..000000000 --- a/ui/.eslintrc.js +++ /dev/null @@ -1,102 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -const path = require('path'); - -module.exports = { - root: true, - parser: 'babel-eslint', - plugins: [ - 'babel', - 'react', - ], - env: { - browser: true, - es6: true, - jquery: true, - commonjs: true, - }, - extends: [ - 'airbnb', - ], - parserOptions: { - sourceType: 'module', - ecmaVersion: 7, - ecmaFeatures: { - impliedStrict: true, - experimentalObjectRestSpread: true, - jsx: true, - legacyDecorators: true, - }, - }, - settings: { - 'import/resolver': { - webpack: { - config: { - resolve: { - alias: { - vj: path.resolve(__dirname), - }, - }, - }, - }, - }, - }, - globals: { - __webpack_public_path__: true, - __webpack_require__: true, - UiContext: true, - UserContext: true, - Context: true, - LOCALES: true, - window: true, - }, - rules: { - 'comma-dangle': [ - 'error', - 'always-multiline', - ], - indent: [ - 'error', - 2, - { - SwitchCase: 0, - }, - ], - 'max-len': [ - 'error', - 150, - ], - quotes: 'warn', - 'func-names': 'off', - 'class-methods-use-this': 'off', - 'consistent-return': 'warn', - 'no-restricted-syntax': 'warn', - 'no-unused-vars': 'warn', - 'no-console': 'off', - 'no-continue': 'off', - 'no-mixed-operators': 'off', - 'no-plusplus': 'off', - 'no-underscore-dangle': 'off', - 'no-await-in-loop': 'off', - 'no-lonely-if': 'off', - 'no-param-reassign': 'off', - 'no-bitwise': 'off', - 'import/first': 'off', - 'react/prefer-stateless-function': 'off', - 'react/self-closing-comp': 'off', - 'react/prop-types': 'off', - 'react/jsx-filename-extension': 'off', - 'react/jsx-one-expression-per-line': 'off', - 'react/no-string-refs': 'off', - 'react/require-default-props': 'off', - 'react/react-in-jsx-scope': 'off', - 'react/destructuring-assignment': 'off', - 'react/button-has-type': 'off', - 'react/forbid-prop-types': 'off', - 'jsx-a11y/no-static-element-interactions': 'off', - 'jsx-a11y/anchor-is-valid': 'off', - 'jsx-a11y/click-events-have-key-events': 'off', - 'max-classes-per-file': 'off', - 'react/jsx-props-no-spreading': 'off', - 'react/static-property-placement': 'off', - }, -}; diff --git a/ui/.snyk b/ui/.snyk deleted file mode 100644 index 39a0c21f9..000000000 --- a/ui/.snyk +++ /dev/null @@ -1,8 +0,0 @@ -# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. -version: v1.16.0 -ignore: {} -# patches apply the minimum changes required to fix a vulnerability -patch: - SNYK-JS-LODASH-567746: - - react-resize-detector > lodash: - patched: '2020-07-17T09:00:07.281Z' diff --git a/ui/Entry.js b/ui/Entry.js deleted file mode 100644 index 6c25a9fe3..000000000 --- a/ui/Entry.js +++ /dev/null @@ -1,92 +0,0 @@ -import 'jquery.transit'; - -import 'normalize.css/normalize.css'; -import 'codemirror/lib/codemirror.css'; - -import _ from 'lodash'; - -import 'vj/misc/float.styl'; -import 'vj/misc/typography.styl'; -import 'vj/misc/textalign.styl'; -import 'vj/misc/grid.styl'; -import 'vj/misc/slideout.styl'; - -import 'vj/misc/.iconfont/webicon.styl'; -import 'vj/misc/immersive.styl'; -import 'vj/misc/structure.styl'; -import 'vj/misc/section.styl'; -import 'vj/misc/nothing.styl'; - -import { PageLoader } from 'vj/misc/PageLoader'; -import delay from 'vj/utils/delay'; - -// eslint-disable-next-line camelcase -__webpack_public_path__ = UiContext.cdn_prefix; - -const pageLoader = new PageLoader(); - -const currentPage = pageLoader.getNamedPage(document.documentElement.getAttribute('data-page')); -const includedPages = pageLoader.getAutoloadPages(); - -function buildSequence(pages, type) { - if (process.env.NODE_ENV !== 'production') { - if (['before', 'after'].indexOf(type) === -1) { - throw new Error(`'type' should be one of 'before' or 'after'`); // eslint-disable-line quotes - } - } - return pages - .filter((p) => p && p[`${type}Loading`]) - .map((p) => ({ - page: p, - func: p[`${type}Loading`], - type, - })); -} - -async function load() { - const loadSequence = [ - ...buildSequence(includedPages, 'before'), - ...buildSequence([currentPage], 'before'), - ...buildSequence(includedPages, 'after'), - ...buildSequence([currentPage], 'after'), - ]; - // eslint-disable-next-line no-restricted-syntax - for (const { page, func, type } of loadSequence) { - if (typeof func !== 'function') { - if (process.env.NODE_ENV !== 'production') { - throw new Error(`The '${type}Loading' function of '${page.name}' is not callable`); - } - continue; - } - if (process.env.NODE_ENV !== 'production') { - console.time(`${page.name}: ${type}Loading`); - } - try { - await func(); - } catch (e) { - console.error(`Failed to call '${type}Loading' of ${page.name}\n${e.stack}`); - } - if (process.env.NODE_ENV !== 'production') { - console.timeEnd(`${page.name}: ${type}Loading`); - } - } - const sections = _.map($('.section').get(), (section, idx) => ({ - shouldDelay: idx < 5, // only animate first 5 sections - $element: $(section), - })); - // eslint-disable-next-line no-restricted-syntax - for (const { $element, shouldDelay } of sections) { - $element.addClass('visible'); - if (shouldDelay) { - await delay(50); - } - } - await delay(500); - // eslint-disable-next-line no-restricted-syntax - for (const { $element } of sections) { - $element.trigger('vjLayout'); - } - $(document).trigger('vjPageFullyInitialized'); -} - -load(); diff --git a/ui/README.md b/ui/README.md deleted file mode 100644 index 945ccbb34..000000000 --- a/ui/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Vijos UI Framework - -See [vijos/vj4/vj4/ui/README.md](https://github.com/vijos/vj4/blob/master/vj4/ui/README.md) diff --git a/ui/breakpoints.json b/ui/breakpoints.json deleted file mode 100644 index 58aba8756..000000000 --- a/ui/breakpoints.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mobile": 600, - "desktop": 1000, - "hd": 1250 -} diff --git a/ui/build/config/gulp.js b/ui/build/config/gulp.js deleted file mode 100644 index 1cbd8e5d4..000000000 --- a/ui/build/config/gulp.js +++ /dev/null @@ -1,89 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import _ from 'lodash'; -import moment from 'moment'; -import gulp from 'gulp'; -import log from 'fancy-log'; -import chalk from 'chalk'; -import svgmin from 'gulp-svgmin'; -import vinylBuffer from 'vinyl-buffer'; -import iconfont from 'gulp-iconfont'; -import nunjucks from 'gulp-nunjucks'; -import plumber from 'gulp-plumber'; -import gulpif from 'gulp-if'; -import vjGenerateLocales from '../plugins/gulpGenerateLocales'; -import vjTouch from '../plugins/gulpTouch'; - -let isInWatchMode = false; -export const tasks = {}; -const iconTimestamp = moment.utc([2017, 0, 1, 0, 0, 0, 0]).unix(); - -function handleWatchChange(name, r = 300) { - return _.debounce((ev) => { - log('File %s: %s', chalk.yellow(ev.type), ev.path); - tasks[name](); - }, r); -} - -function offsetMtimeAtFirstBuild() { - // Offset the mtime at first build to - // workaround webpack/watchpack issue #25. - return gulpif(!isInWatchMode, vjTouch(~~((Date.now() - 30 * 1000) / 1000))); -} - -export default function ({ watch, production, errorHandler }) { - let iconfontTemplateArgs = null; - - tasks['iconfont:template'] = () => gulp - .src('misc/icons/template/*.styl') - .pipe(nunjucks.compile(iconfontTemplateArgs)) - .pipe(gulp.dest('misc/.iconfont')) - .pipe(offsetMtimeAtFirstBuild()); - - tasks.iconfont = () => gulp - .src('misc/icons/*.svg') - .pipe(plumber({ errorHandler })) - .pipe(svgmin()) - .pipe(gulp.dest('misc/icons')) - .pipe(offsetMtimeAtFirstBuild()) - .pipe(iconfont({ - fontHeight: 1000, - prependUnicode: false, - descent: 6.25 / 100 * 1000, - fontName: 'vj4icon', - formats: ['svg', 'ttf', 'eot', 'woff', 'woff2'], - timestamp: iconTimestamp, - })) - .on('glyphs', (glyphs, options) => { - iconfontTemplateArgs = { glyphs, options }; - tasks['iconfont:template'](); - }) - .pipe(gulp.dest('misc/.iconfont')) - .pipe(vinylBuffer()) - .pipe(offsetMtimeAtFirstBuild()); - - tasks.locale = () => gulp - .src('../locales/*.yaml') - .pipe(plumber({ errorHandler })) - .pipe(vjGenerateLocales()) - .pipe(gulp.dest('static/locale')) - .pipe(offsetMtimeAtFirstBuild()); - - tasks.watch = () => { - isInWatchMode = true; - gulp.watch('misc/icons/*.svg', handleWatchChange('iconfont')); - gulp.watch('../locales/*.yaml', handleWatchChange('locale')); - }; - - for (const key in tasks) { - // gulp4 uses function name directly as task name - Object.defineProperty(tasks[key], 'name', { - value: key, - configurable: true, - }); - tasks[key] = gulp.series(tasks[key]); - } - - tasks.default = gulp.series(gulp.parallel(tasks.iconfont, tasks.locale)); - - return tasks; -} diff --git a/ui/build/config/webpack.js b/ui/build/config/webpack.js deleted file mode 100644 index 7fb4ff11f..000000000 --- a/ui/build/config/webpack.js +++ /dev/null @@ -1,230 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import webpack from 'webpack'; -import fs from 'fs'; - -import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; -import FriendlyErrorsPlugin from 'friendly-errors-webpack-plugin'; -import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin'; -import ExtractCssPlugin from 'mini-css-extract-plugin'; -import CopyWebpackPlugin from 'copy-webpack-plugin'; -import StaticManifestPlugin from '../plugins/webpackStaticManifestPlugin'; -import mapWebpackUrlPrefix from '../utils/mapWebpackUrlPrefix'; -import root from '../utils/root'; - -const beautifyOutputUrl = mapWebpackUrlPrefix([ - { prefix: 'node_modules/katex/dist/', replace: 'katex/' }, - { prefix: 'misc/.iconfont', replace: 'ui/iconfont' }, -]); - -export default function (env = {}) { - function eslintLoader() { - return { - loader: 'eslint-loader', - options: { - configFile: root('.eslintrc.js'), - }, - }; - } - - function babelLoader() { - let cacheDirectory = root('../.cache/babel'); - try { - fs.ensureDirSync(cacheDirectory); - } catch (ignored) { - cacheDirectory = false; - } - return { - loader: 'babel-loader', - options: { - ...require(root('package.json')).babelForProject, // eslint-disable-line - cacheDirectory, - }, - }; - } - - function cssLoader() { - return { - loader: 'css-loader', - options: { - importLoaders: 1, - }, - }; - } - - function postcssLoader() { - return { - loader: 'postcss-loader', - options: { - sourceMap: env.production, - }, - }; - } - - function fileLoader() { - return { - loader: 'file-loader', - options: { - name: '[path][name].[ext]?[sha1:hash:hex:10]', - }, - }; - } - - function extractCssLoader() { - return { - loader: ExtractCssPlugin.loader, - options: { publicPath: '/' }, - }; - } - - const config = { - bail: true, - mode: env.production ? 'production' : 'development', - profile: true, - context: root(), - entry: { - vj4: './Entry.js', - }, - output: { - path: root('../public'), - publicPath: '/', // overwrite in entry.js - hashFunction: 'sha1', - hashDigest: 'hex', - hashDigestLength: 10, - filename: '[name].js?[chunkhash]', - chunkFilename: '[name].chunk.js?[chunkhash]', - }, - resolve: { - modules: [ - root('node_modules'), - ], - alias: { - vj: root(), - }, - }, - module: { - rules: [ - { - test: /\.jsx?$/, - exclude: /node_modules[/\\]/, - enforce: 'pre', - use: [eslintLoader()], - }, - { - // fonts and images - test: /\.(svg|ttf|eot|woff|woff2|png|jpg|jpeg|gif)$/, - use: [fileLoader()], - }, - { - // ES2015 scripts - test: /\.js$/, - exclude: /node_modules[/\\]/, - use: [babelLoader()], - }, - { - test: /\.styl$/, - use: [extractCssLoader(), cssLoader(), postcssLoader(), 'stylus-loader'] - , - }, - { - test: /\.css$/, - use: [extractCssLoader(), cssLoader(), postcssLoader()] - , - }, - ], - }, - optimization: { - splitChunks: { - minChunks: 2, - }, - }, - plugins: [ - new webpack.ProgressPlugin(), - - new webpack.ProvidePlugin({ - $: 'jquery', - jQuery: 'jquery', - 'window.jQuery': 'jquery', - katex: 'katex/dist/katex.js', - React: 'react', - }), - - new FriendlyErrorsPlugin(), - - // don't include momentjs locale files - new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), - - new ExtractCssPlugin({ - filename: 'vj4.css?[contenthash:10]', - allChunks: true, - }), - - // copy static assets - new CopyWebpackPlugin({ - patterns: [ - { from: root('static') }, - { from: root('node_modules/emojify.js/dist/images/basic'), to: 'img/emoji/' }, - ], - }), - - // Options are provided by LoaderOptionsPlugin until webpack#3136 is fixed - new webpack.LoaderOptionsPlugin({ - test: /\.styl$/, - stylus: { - default: { - preferPathResolver: 'webpack', - use: [ - require('rupture')(), // eslint-disable-line global-require - ], - import: [ - '~vj/common/common.inc.styl', - ], - }, - }, - }), - - // Make sure process.env.NODE_ENV === 'production' in production mode - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: env.production ? '"production"' : '"debug"', - }, - }), - - env.production - ? new OptimizeCssAssetsPlugin() - : function () { }, - env.production - ? new webpack.optimize.ModuleConcatenationPlugin() - : function () { }, - env.production - ? new webpack.LoaderOptionsPlugin({ minimize: true }) - : function () { }, - // Replace Module Id with hash or name - env.production - ? new webpack.HashedModuleIdsPlugin() - : new webpack.NamedModulesPlugin(), - - new webpack.LoaderOptionsPlugin({ - options: { - context: root(), - customInterpolateName: (url) => beautifyOutputUrl(url), - }, - }), - - new MonacoWebpackPlugin({ - // available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options - languages: ['cpp', 'csharp', 'java', 'javascript', 'python', 'rust', 'ruby', 'php', 'pascal', 'go'], - }), - - // Finally, output asset hashes - new StaticManifestPlugin({ - fileName: 'static-manifest.json', - ignore: [ - 'img/emoji/', - 'katex/', - ], - }), - ], - }; - - return config; -} diff --git a/ui/build/index.js b/ui/build/index.js deleted file mode 100644 index 7d88dcf13..000000000 --- a/ui/build/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable */ -require('@babel/register'); -const main = require('./main.js'); -if (!module.parent) main(); diff --git a/ui/build/main.js b/ui/build/main.js deleted file mode 100644 index 5a912ed03..000000000 --- a/ui/build/main.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { argv } from 'yargs'; -import root from './utils/root'; -import runGulp from './runGulp'; -import runWebpack from './runWebpack'; - -async function main() { - const dir = process.cwd(); - process.chdir(root()); - await runGulp({}); - await runWebpack(argv); - process.chdir(dir); -} - -module.exports = main; diff --git a/ui/build/plugins/gulpGenerateLocales.js b/ui/build/plugins/gulpGenerateLocales.js deleted file mode 100644 index 15f9947f1..000000000 --- a/ui/build/plugins/gulpGenerateLocales.js +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import yaml from 'js-yaml'; - -import PluginError from 'plugin-error'; -import through from 'through2'; -import path from 'path'; - -export default function generateLocales() { - function bufferContents(file, encoding, callback) { - if (file.isNull()) { - callback(); - return; - } - if (file.isStream()) { - this.emit('error', new PluginError('gulpGenerateLocales', 'Stream not supported')); - callback(); - return; - } - - const doc = yaml.safeLoad(file.contents); - file.contents = Buffer.from(`window.LOCALES = ${JSON.stringify(doc, null, 2)}`); - file.path = path.join( - path.dirname(file.path), - `${path.basename(file.path, path.extname(file.path))}.js` - ); - - this.push(file); - callback(); - } - - return through.obj(bufferContents); -} diff --git a/ui/build/plugins/gulpTouch.js b/ui/build/plugins/gulpTouch.js deleted file mode 100644 index c1ab2d1b6..000000000 --- a/ui/build/plugins/gulpTouch.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import PluginError from 'plugin-error'; -import through from 'through2'; -import fs from 'fs-extra'; - -export default function touch(mtime) { - async function touchFile(file) { - const fd = await fs.open(file.path, 'a'); - try { - await fs.futimes(fd, mtime, mtime); - } finally { - await fs.close(fd); - } - } - - function processStream(file, encoding, callback) { - if (file.isNull()) { - callback(); - return; - } - if (file.isStream()) { - this.emit('error', new PluginError('gulpTouch', 'Stream not supported')); - callback(); - return; - } - touchFile(file) - .catch((err) => this.emit('error', err)) - .then(() => { - this.push(file); - callback(); - }); - } - - return through.obj(processStream); -} diff --git a/ui/build/plugins/webpackStaticManifestPlugin.js b/ui/build/plugins/webpackStaticManifestPlugin.js deleted file mode 100644 index 03f477b72..000000000 --- a/ui/build/plugins/webpackStaticManifestPlugin.js +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -const _ = require('lodash'); -const crypto = require('crypto'); - -export default class StaticManifestPlugin { - constructor({ fileName, ignore }) { - this.fileName = fileName; - this.ignore = ignore; - } - - apply(compiler) { - compiler.plugin('emit', (compilation, callback) => { - const stats = compilation.getStats().toJson(); - const manifest = _(stats.assets) - .map((asset) => { - const { name } = asset; - // Skip files listed in ignore - if (_.some(this.ignore, (ignorePattern) => name.indexOf(ignorePattern) > -1)) { - return null; - } - // Skip calculating hash for names like ?xxxx - if (name.indexOf('?') > -1) { - return [ - name.replace(/\?[\s\S]*/, ''), // origin name - name, // name with hash - ]; - } - // We need to calculate hash for the remaining files. - const source = compilation.assets[asset.name].source(); - const shasum = crypto.createHash('sha1'); - shasum.update(source); - const hash = shasum.digest('hex').substr(0, 10); - return [ - name, // origin name - `${name}?${hash}`, // name with hash - ]; - }) - .filter() - .orderBy(['0']) - .fromPairs() - .value(); - const json = JSON.stringify(manifest, null, 2); - compilation.assets[this.fileName] = { - source: () => json, - size: () => json.length, - }; - callback(); - }); - } -} diff --git a/ui/build/runGulp.js b/ui/build/runGulp.js deleted file mode 100644 index c12d8d9f7..000000000 --- a/ui/build/runGulp.js +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import gulp from 'gulp'; -import log from 'fancy-log'; -import chalk from 'chalk'; -import gulpConfig from './config/gulp'; - -export default async function () { - function handleError(err) { - log(chalk.red('Error: %s'), chalk.reset(err.toString() + err.stack)); - if (err) process.exit(1); - } - const gulpTasks = gulpConfig({ production: true, errorHandler: handleError }); - return new Promise((resolve) => { - const taskList = {}; - - gulp.on('start', ({ uid, name }) => { - log(chalk.blue('Starting task: %s'), chalk.reset(name)); - taskList[uid] = true; - }); - gulp.on('stop', ({ uid, name }) => { - log(chalk.green('Finished: %s'), chalk.reset(name)); - taskList[uid] = false; - - if (Object.values(taskList).filter((b) => b).length === 0) { - resolve(); - } - }); - gulpTasks.default(); - }); -} diff --git a/ui/build/runWebpack.js b/ui/build/runWebpack.js deleted file mode 100644 index d0fb67b2b..000000000 --- a/ui/build/runWebpack.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import webpack from 'webpack'; -import webpackConfig from './config/webpack'; - -export default function ({ watch, production }) { - const compiler = webpack(webpackConfig({ watch, production })); - - function compilerCallback(err, stats) { - if (err) { - console.error(err.stack || err); - if (err.details) console.error(err.details); - process.exit(1); - } - if (!watch && stats.hasErrors()) process.exitCode = 1; - } - - if (watch) compiler.watch({}, compilerCallback); - else compiler.run(compilerCallback); -} diff --git a/ui/build/utils/mapWebpackUrlPrefix.js b/ui/build/utils/mapWebpackUrlPrefix.js deleted file mode 100644 index 9b8343b65..000000000 --- a/ui/build/utils/mapWebpackUrlPrefix.js +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import _ from 'lodash'; - -export default function mapWebpackUrlPrefix(mapList) { - const rules = mapList.map((mappingRule) => { - const regex = mappingRule.prefix.split('/').map((s) => _.escapeRegExp(s)).join('[\\/\\\\]'); - return { - from: new RegExp(`^${regex}`, mappingRule.flag), - to: mappingRule.replace, - }; - }); - return function mapUrl(url) { - rules.forEach((rule) => { - url = url.replace(rule.from, rule.to); - }); - return url; - }; -} diff --git a/ui/build/utils/root.js b/ui/build/utils/root.js deleted file mode 100644 index b402e6142..000000000 --- a/ui/build/utils/root.js +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable */ -import path from 'path'; - -export default function root(fn = '.') { - return path.resolve(__dirname, '../../', fn); -} diff --git a/ui/common/color.inc.styl b/ui/common/color.inc.styl deleted file mode 100644 index defc99594..000000000 --- a/ui/common/color.inc.styl +++ /dev/null @@ -1,17 +0,0 @@ -aqua = #7FDBFF -blue = #0074D9 -navy = #001F3F -teal = #39CCCC -green = #2ECC40 -olive = #3D9970 -lime = #01FF70 -yellow = #FFDC00 -orange = #FF851B -red = #FF4136 -fuchsia = #F012BE -purple = #B10DC9 -maroon = #85144B -white = #ffffff -silver = #dddddd -gray = #aaaaaa -black = #111111 diff --git a/ui/common/common.inc.styl b/ui/common/common.inc.styl deleted file mode 100644 index ff72825e0..000000000 --- a/ui/common/common.inc.styl +++ /dev/null @@ -1,12 +0,0 @@ -// This file is imported in all stylus source code -breakpoints = json('../breakpoints.json', { hash: true }) -rupture.mobile-cutoff = unit(breakpoints.mobile, 'px') -rupture.desktop-cutoff = unit(breakpoints.desktop, 'px') -rupture.hd-cutoff = unit(breakpoints.hd, 'px') - -@import '~vj/misc/.iconfont/webicon.inc.styl' -@import 'color.inc.styl' -@import 'variables.inc.styl' -@import 'easing.inc.styl' -@import 'rem.inc.styl' -@import 'functions.inc.styl' diff --git a/ui/common/easing.inc.styl b/ui/common/easing.inc.styl deleted file mode 100644 index 50713e02c..000000000 --- a/ui/common/easing.inc.styl +++ /dev/null @@ -1,39 +0,0 @@ -// Cubic -ease-in-cubic = cubic-bezier(0.550, 0.055, 0.675, 0.190); -ease-out-cubic = cubic-bezier(0.215, 0.610, 0.355, 1.000); -ease-in-out-cubic = cubic-bezier(0.645, 0.045, 0.355, 1.000); - -// Circ -ease-in-circ = cubic-bezier(0.600, 0.040, 0.980, 0.335); -ease-out-circ = cubic-bezier(0.075, 0.820, 0.165, 1.000); -ease-in-out-circ = cubic-bezier(0.785, 0.135, 0.150, 0.860); - -// Expo -ease-in-expo = cubic-bezier(0.950, 0.050, 0.795, 0.035); -ease-out-expo = cubic-bezier(0.190, 1.000, 0.220, 1.000); -ease-in-out-expo = cubic-bezier(1.000, 0.000, 0.000, 1.000); - -// Quad -ease-in-quad = cubic-bezier(0.550, 0.085, 0.680, 0.530); -ease-out-quad = cubic-bezier(0.250, 0.460, 0.450, 0.940); -ease-in-out-quad = cubic-bezier(0.455, 0.030, 0.515, 0.955); - -// Quart -ease-in-quart = cubic-bezier(0.895, 0.030, 0.685, 0.220); -ease-out-quart = cubic-bezier(0.165, 0.840, 0.440, 1.000); -ease-in-out-quart = cubic-bezier(0.770, 0.000, 0.175, 1.000); - -// Quint -ease-in-quint = cubic-bezier(0.755, 0.050, 0.855, 0.060); -ease-out-quint = cubic-bezier(0.230, 1.000, 0.320, 1.000); -ease-in-out-quint = cubic-bezier(0.860, 0.000, 0.070, 1.000); - -// Sine -ease-in-sine = cubic-bezier(0.470, 0.000, 0.745, 0.715); -ease-out-sine = cubic-bezier(0.390, 0.575, 0.565, 1.000); -ease-in-out-sine = cubic-bezier(0.445, 0.050, 0.550, 0.950); - -// Back -ease-in-back = cubic-bezier(0.600, -0.280, 0.735, 0.045); -ease-out-back = cubic-bezier(0.175, 0.885, 0.320, 1.275); -ease-in-out-back = cubic-bezier(0.680, -0.550, 0.265, 1.550); diff --git a/ui/common/functions.inc.styl b/ui/common/functions.inc.styl deleted file mode 100644 index 778ac974a..000000000 --- a/ui/common/functions.inc.styl +++ /dev/null @@ -1,9 +0,0 @@ -word-wrap() - overflow-wrap: break-word - word-wrap: break-word - -ms-word-break: break-all - word-break: break-word - -ms-hyphens: auto - -moz-hyphens: auto - -webkit-hyphens: auto - hyphens: auto diff --git a/ui/common/rem.inc.styl b/ui/common/rem.inc.styl deleted file mode 100644 index b80603429..000000000 --- a/ui/common/rem.inc.styl +++ /dev/null @@ -1,7 +0,0 @@ -rem() { - ret = (); - for arg in arguments { - push(ret, unit(arg / $font-size, 'rem')); - } - return ret; -} diff --git a/ui/common/variables.inc.styl b/ui/common/variables.inc.styl deleted file mode 100644 index 4494d610d..000000000 --- a/ui/common/variables.inc.styl +++ /dev/null @@ -1,256 +0,0 @@ -// layout -$grid-padding = 15px // half gutter - -// fonts -$primary-font-family = "Open Sans", "Seravek", "Segoe UI", "Verdana", "PingFang SC", "Hiragino Sans GB", "Lantinghei SC", "Microsoft Yahei", "WenQuanYi Micro Hei", "sans" -$header-font-family = "Open Sans", "Seravek", "Segoe UI", "Verdana", "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", "sans-serif" -$code-font-family = "monaco", "Source Code Pro", "Consolas", "Lucida Console", "monospace" - -// primary palette -$default-color = #ddd -$primary-color = #5f9fd6 -$secondary-color = #ed5f82 -$highlight-color = #df6589 -$code-color = #c92a2a -$text-1-color = #555 -$text-2-color = lighten($text-1-color, 15%) -$text-3-color = lighten($text-1-color, 30%) -$header-1-color = #3d3d3d -$header-2-color = lighten($header-1-color, 20%) -$blockquote-color = #999 -$page-bg-color = #edf0f2 -$content-bg-color = #fff -$border-1-color = $default-color -$border-2-color = lighten($default-color, 20%) - -$immersive-primary-color = #ffef87 -$immersive-text-color = #f8f8f8 -$immersive-header-color = #ffffff - -$success-color = #25ad40 -$fail-color = #fb5555 -$progress-color = #f39800 -$invalid-color = #9fa0a0 - -// sizes -$font-size = 16px // main content -$font-size-secondary = 14px // secondary content -$font-size-small = 13px // additional informations, usually gray colored -$font-size-icon = 16px -$font-size-title = 20px - -// components -$section-gap-v = 25px -$section-gap-h = $grid-padding -$section-margin = 30px -$section-shadow = rem(0 6px 22px) rgba(#AFC2C9, 0.5) -$section-bg-color = $content-bg-color -$section-title-color = $header-2-color -$section-toolbtn-color = #AAA -$section-toolbtn-color-hover = #444 - -$autocomplete-empty-row-color = #AAA -$userselect-uid-color = #BBB - -$editor-border-color = $border-1-color - -$datepicker-shadow = rem(0 2px 7px) rgba(#000, 0.3) -$datapicker-list-item-bg-color-hover = #F0F0F0 - -$dialog-layer-bg-color = rgba(#F0F0F0, 0.6) -$dialog-bg-color = $content-bg-color -$dialog-border-color = $border-1-color -$dialog-shadow = rem(0 2px 10px) rgba(#000, 0.2) -$dialog-title-color = $header-2-color - -$comment-placeholder-color = #AAA -$comment-op-color = lighten($text-1-color, 30%) -$comment-op-link-color = lighten($text-1-color, 50%) - -$menu-item-bg-color-hover = #F4F4F4 -$menu-drop-shadow = rem(0 2px 7px) rgba(#000, 0.3) -$menu-drop-bg-color = $content-bg-color -$menu-drop-triangle-size = 4px -$menu-drop-triangle-shadow = rem(-2px -2px 2px) rgba(#000, 0.15) - -$nav-item-height = 45px -$nav-item-hover-color = $primary-color -$nav-item-active-color = $secondary-color -$nav-item-active-immersive-color = #9AE3F3 -$nav-item-round-height = 32px -$nav-item-round-color = $primary-color -$nav-item-round-border = 2px -$nav-logo-width = 45px -$nav-logo-height = 23px - -$header-layer-height = 100px -$header-bg-width = 1920px -$header-bg-height = 320px -$header-bg-height-mini = $header-bg-height - $header-layer-height -//$header-logo-width = 280px -//$header-logo-height = 150px -//$header-logo-width-real = 280px -//$header-logo-height-real = 150px -$header-logo-width = 252px -$header-logo-height = 150px -$header-logo-width-real = 381px -$header-logo-height-real = 201px -$header-logo-domain-lh = 60px -$header-logo-system-lh = 30px - -$footer-bg-color = $content-bg-color - -$input-focus-color = #C1E0FF -$form-control-height = 35px -$compact-control-height = 28px -$input-margin = 0 0 1rem -$input-multi-line-margin = 0 0.3rem 0.15rem 0 -$input-image-radio-margin = 0 0.3rem 0.3rem 0 -$input-background-color = $content-bg-color -$input-border = 1px solid $border-1-color -$input-outline = 2px solid transparent -$input-focus-border-color = $primary-color -$input-focus-outline = 2px solid $input-focus-color -$label-color = lighten($text-1-color, 10%) -$help-text-color = lighten($text-1-color, 30%) - -$table-border-color = $border-2-color -$table-alternate-bg-color = #F4F4F4 -$table-header-bg-color = rgba(#FFF, 0.95) -$table-header-shadow = rem(0 3px 2px) rgba(#000, 0.03) -$table-header-shadow-floating = rem(0 2px 5px) rgba(#000, 0.2) -$table-padding-h = 10px -$table-lh = 26px - -$toolbar-bg = #f2f6f7 -$toolbar-fore = #8395a1 -$toolbar-fore-hover = #5a6b75 -$toolbar-bg-hover = #e3e7e8 -$toolbar-fore-selected = #FFF -$toolbar-bg-selected = $secondary-color -$toolbar-disabled-opacity = 0.3 - -$supplementary-color = lighten($text-1-color, 50%) -$supplementary-border-color = lighten($supplementary-color, 62%) -$supplementary-link-color = lighten($text-1-color, 40%) - -$pager-border-color = $table-border-color - -$nprogress-color = #FFF -$nprogress-color-floating = $primary-color - -$syntax-hl-bg-color = #F8F8F8 - -// Calendar -$calendar-banner-color = { - '0': #FA6582, - '1': #EEA9C5, - '2': #FDB24D, - '3': #98CA86, - '4': #7DCBBF, - '5': #20BDE8, - '6': #6BCDDB, - '7': #6598FF, - '8': #8EB0F5, - '9': #928FE3, - '10': #676E7E, - '11': #A1A5AB, -} - -// Notification -$notification-color = { - 'success': #9BDB7B, - 'info': #78D6F4, - 'warn': #FCD279, - 'error': #FD848D, -} - -$notification-icon = { - 'success': $icon-check--circle, - 'info': $icon-info--circle, - 'warn': $icon-info--circle, - 'error': $icon-close--circle, -} - -// Badge -$badge-text-color = { - 'mod': #FFF, - 'lv0': #727171, - 'lv1': #FFF, - 'lv2': #FFF, - 'lv3': #FFF, - 'lv4': #FFF, - 'lv5': #FFF, - 'lv6': #FFF, - 'lv7': #FFF, - 'lv8': #FFF, - 'lv9': #FFF, - 'lv10': #FFF, -} -$badge-bg-color = { - 'mod': #cf70ab, - 'lv0': #efefef, - 'lv1': #9db4bd, - 'lv2': #9db4bd, - 'lv3': #73c3bc, - 'lv4': #73c3bc, - 'lv5': #81ba6c, - 'lv6': #81ba6c, - 'lv7': #f7b200, - 'lv8': #ff9251, - 'lv9': #eb6363, - 'lv10': #575757, -} - -// Record Status -$record-status-color = { - 'pass': $success-color, - 'fail': $fail-color, - 'progress': $progress-color, - 'ignored': $invalid-color, - 'pending': $invalid-color, -} - -$record-status-icon = { - 'pass': $icon-check, - 'fail': $icon-close, - 'progress': $icon-hourglass, - 'ignored': $icon-close, - 'pending': $icon-schedule, -} - -// Training Status -$training-status-color = { - 'outside': $supplementary-color, - 'progress': #e64c72, - 'done': #25ad40, -} - -$training-status-icon = { - 'outside': $icon-help2, - 'progress': $icon-schedule, - 'done': $icon-check, -} - -// Training Section Status -$training-section-status-color = { - 'done': #25ad40, - 'open': #e64c72, - 'progress': #e64c72, - 'invalid': $supplementary-color, -} - -$training-section-status-icon = { - 'done': $icon-check, - 'open': $icon-help2, - 'progress': $icon-schedule, - 'invalid': $icon-close, -} - -// Contest type -// #6EA2C7, #48AAB7, #6BB67A, #F5C735, #DC7D86 -$contest-color = { - 'acm': #6BB67A, - 'oi': #F5C735, - 'homework': #FFCDCD, -} diff --git a/ui/components/DOMAttachedObject.js b/ui/components/DOMAttachedObject.js deleted file mode 100644 index 59e381c14..000000000 --- a/ui/components/DOMAttachedObject.js +++ /dev/null @@ -1,125 +0,0 @@ -let removalChecker = null; -const res = []; - -function checkResources() { - for (let i = res.length - 1; i >= 0; --i) { - if (res[i].detached || !document.body.contains(res[i].$dom[0])) { - if (!res[i].detached) res[i].detach(); - res.splice(i, 1); - } - } - if (res.length === 0) { - clearInterval(removalChecker); - removalChecker = null; - } -} - -function monitorResource(resource) { - res.push(resource); - if (removalChecker === null) { - removalChecker = setInterval(checkResources, 500); - } -} - -export default class DOMAttachedObject { - static uniqueIdCounter = 0; - - static get($obj) { - const key = this.DOMAttachKey; - return $obj.data(key); - } - - static getOrConstruct($obj, ...args) { - const $singleObj = $obj.eq(0); - const key = this.DOMAttachKey; - const Protoclass = this; - const instance = this.get($singleObj); - if (instance !== undefined) return instance; - const newInstance = new Protoclass($singleObj, ...args); - // $dom may be updated in constructor so that we use $dom instead - // of $singleObj here. - if (!newInstance.$dom) return null; - newInstance.$dom.data(key, newInstance); - return newInstance; - } - - static attachAll(container = document.body, ...args) { - if (process.env.NODE_ENV !== 'production') { - if (!this.DOMAttachSelector) { - // eslint-disable-next-line quotes - throw new Error(`'DOMAttachSelector' should be specified`); - } - } - if (process.env.NODE_ENV !== 'production') { - console.time(`DOMAttachedObject[${this.DOMAttachKey}]: attachAll`); - } - $(container) - .find(this.DOMAttachSelector) - .addBack(this.DOMAttachSelector) - .get() - .forEach((element) => this.getOrConstruct($(element), ...args)); - if (process.env.NODE_ENV !== 'production') { - console.timeEnd(`DOMAttachedObject[${this.DOMAttachKey}]: attachAll`); - } - } - - static detachAll(container = document.body) { - const selector = this.DOMDetachSelector || this.DOMAttachSelector; - if (process.env.NODE_ENV !== 'production') { - if (!selector) { - // eslint-disable-next-line quotes - throw new Error(`'DOMDetachSelector' or 'DOMAttachSelector' should be specified`); - } - } - if (process.env.NODE_ENV !== 'production') { - console.time(`DOMAttachedObject[${this.DOMAttachKey}]: detachAll`); - } - $(container) - .find(selector) - .addBack(selector) - .get() - .forEach((element) => { - const instance = this.get($(element)); - if (instance) { - instance.detach(); - } - }); - if (process.env.NODE_ENV !== 'production') { - console.timeEnd(`DOMAttachedObject[${this.DOMAttachKey}]: detachAll`); - } - } - - static registerLifeCycleHooks(attach = true) { - if (process.env.NODE_ENV !== 'production') { - if (!this.DOMAttachSelector) { - // eslint-disable-next-line quotes - throw new Error(`'DOMAttachSelector' should be specified`); - } - } - $(document).on('vjContentNew', (e) => this.attachAll(e.target)); - $(document).on('vjContentRemove', (e) => this.detachAll(e.target)); - if (attach) { - this.attachAll(); - } - } - - detach() { - if (this.constructor.DOMAttachKey) { - this.$dom.removeData(this.constructor.DOMAttachKey); - } - this.detached = true; - } - - constructor($dom, monitorDetach = false) { - if ($dom == null) { - return null; - } - this.$dom = $dom; - this.id = ++DOMAttachedObject.uniqueIdCounter; - this.eventNS = `vj4obj_${this.id}`; - this.detached = false; - if (monitorDetach) { - monitorResource(this); - } - } -} diff --git a/ui/components/autocomplete/DomainSelectAutoComplete.js b/ui/components/autocomplete/DomainSelectAutoComplete.js deleted file mode 100644 index 170b42832..000000000 --- a/ui/components/autocomplete/DomainSelectAutoComplete.js +++ /dev/null @@ -1,43 +0,0 @@ -import _ from 'lodash'; -import tpl from 'vj/utils/tpl'; -import request from 'vj/utils/request'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import AutoComplete from '.'; - -function getText(domain) { - return domain._id; -} - -function getItems(val) { - return request.get('/domain/search', { q: val }); -} - -function renderItem(domain) { - return tpl` -
    -
    - -
    -
    -
    ${domain.name}
    -
    ID = ${domain._id}
    -
    -
    - `; -} - -export default class DomainSelectAutoComplete extends AutoComplete { - static DOMAttachKey = 'vjDomainSelectAutoCompleteInstance'; - - constructor($dom, options) { - super($dom, { - classes: 'domain-select', - items: getItems, - render: renderItem, - text: getText, - ...options, - }); - } -} - -_.assign(DomainSelectAutoComplete, DOMAttachedObject); diff --git a/ui/components/autocomplete/UserSelectAutoComplete.js b/ui/components/autocomplete/UserSelectAutoComplete.js deleted file mode 100644 index 5c0d118c4..000000000 --- a/ui/components/autocomplete/UserSelectAutoComplete.js +++ /dev/null @@ -1,43 +0,0 @@ -import _ from 'lodash'; -import tpl from 'vj/utils/tpl'; -import request from 'vj/utils/request'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import AutoComplete from '.'; - -function getText(user) { - return user.uname; -} - -function getItems(val) { - return request.get('/user/search', { q: val }); -} - -function renderItem(user) { - return tpl` -
    -
    - -
    -
    -
    ${user.uname}
    -
    UID = ${user._id}
    -
    -
    - `; -} - -export default class UserSelectAutoComplete extends AutoComplete { - static DOMAttachKey = 'vjUserSelectAutoCompleteInstance'; - - constructor($dom, options) { - super($dom, { - classes: 'user-select', - items: getItems, - render: renderItem, - text: getText, - ...options, - }); - } -} - -_.assign(UserSelectAutoComplete, DOMAttachedObject); diff --git a/ui/components/autocomplete/autocomplete.page.styl b/ui/components/autocomplete/autocomplete.page.styl deleted file mode 100644 index cd631147f..000000000 --- a/ui/components/autocomplete/autocomplete.page.styl +++ /dev/null @@ -1,11 +0,0 @@ -.drop-element.autocomplete - max-width: rem(250px) - - .empty-row - color: $autocomplete-empty-row-color - font-size: $(font-size-small) - padding: rem(10px) - - .menu - max-height: rem(250px) - overflow: auto diff --git a/ui/components/autocomplete/domainselectautocomplete.page.styl b/ui/components/autocomplete/domainselectautocomplete.page.styl deleted file mode 100644 index bcd2a6f24..000000000 --- a/ui/components/autocomplete/domainselectautocomplete.page.styl +++ /dev/null @@ -1,6 +0,0 @@ -.menu.domain-select - width: rem(200px) - -.domain-select__id - font-size: rem($font-size-small) - color: $userselect-uid-color diff --git a/ui/components/autocomplete/index.js b/ui/components/autocomplete/index.js deleted file mode 100644 index 9fbb58c43..000000000 --- a/ui/components/autocomplete/index.js +++ /dev/null @@ -1,202 +0,0 @@ -import Drop from 'tether-drop'; -import _ from 'lodash'; -import 'jquery-scroll-lock'; - -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; - -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; -import zIndexManager from 'vj/utils/zIndexManager'; - -export default class AutoComplete extends DOMAttachedObject { - static DOMAttachKey = 'vjAutoCompleteInstance'; - - constructor($dom, options = {}) { - super($dom); - this.options = { - items: async () => [], - render: () => '', - text: () => null, - minChar: 1, - cache: true, - clearDefaultValue: true, - position: 'bottom left', - classes: '', - ...options, - }; - this.clear(this.options.clearDefaultValue); - this.menuShown = false; - this.cache = {}; - this.currentItems = []; - this.$menu = $(``); - this.$menu.scrollLock({ strict: false }); - this.$menu.on('mousedown', this.onMenuClick.bind(this)); - this.$menu.on('mousedown', '.menu__item', this.onItemClick.bind(this)); - this.dropInstance = new Drop({ - classes: 'autocomplete dropdown', - target: this.$dom[0], - content: this.$menu[0], - position: this.options.position, - constrainToWindow: false, - constrainToScrollParent: false, - openOn: false, - }); - this.dropInstance.on('open', this.onDropOpen.bind(this)); - this.attach(); - } - - clear(clearValue = true) { - if (clearValue) { - this.$dom.val(''); - } - this._value = null; - this.lastText = null; - } - - attach() { - this.$dom.on(`click.${this.eventNS}`, this.onClick.bind(this)); - this.$dom.on(`focus.${this.eventNS}`, this.onFocus.bind(this)); - this.$dom.on(`blur.${this.eventNS}`, this.onBlur.bind(this)); - this.$dom.on(`keydown.${this.eventNS}`, this.onKeyDown.bind(this)); - this.$dom.on(`keyup.${this.eventNS}`, this.onKeyUp.bind(this)); - } - - onClick() { - this.updateOpenState(); - } - - onFocus() { - this.isFocus = true; - this.updateOpenState(); - } - - onBlur() { - this.isFocus = false; - this.updateOpenState(); - } - - onKeyDown() { - // TODO: Implement keyboard navigation - } - - onKeyUp(ev) { - if (ev.which === 27) { - // ESC - this.close(); - return; - } - if (this.$dom.val() === this.lastText) { - return; - } - this.lastText = this.$dom.val(); - this.updateOpenState(); - if (this.isOpen) { - this.renderList(); - } - } - - onMenuClick(ev) { - ev.preventDefault(); // prevent from losing focus - } - - onItemClick(ev) { - const idx = $(ev.currentTarget).attr('data-idx'); - if (idx === undefined) { - return; - } - const item = this.currentItems[idx]; - const text = this.options.text(item); - this._value = item; - this.$dom.trigger('vjAutoCompleteSelect', item); - if (text != null) { - this.$dom.val(text); - } - this.close(); - } - - onDropOpen() { - $(this.dropInstance.drop).css('z-index', zIndexManager.getNext()); - } - - async getItems(val) { - if (this.cache[val] !== undefined) { - return this.cache[val]; - } - const data = await this.options.items(val); - if (this.options.cache) { - this.cache[val] = data; - } - return data; - } - - getHtml(items) { - if (items.length === 0) { - return tpl`
    ${i18n('Oops, there are no results.')}
    `; - } - return items.map((item, idx) => ` - - `).join('\n'); - } - - async renderList() { - const val = this.$dom.val(); - const items = await this.getItems(val); - const html = this.getHtml(items); - this.currentItems = items; - this.$menu.html(html); - } - - open() { - if (this.isOpen) { - return; - } - this.dropInstance.open(); - this.isOpen = true; - } - - close() { - if (!this.isOpen) { - return; - } - this.dropInstance.close(); - this.isOpen = false; - } - - updateOpenState() { - if (this.isFocus && this.$dom.val().length >= this.options.minChar) { - if (!this.isOpen) { - this.open(); - this.renderList(); - } - } else { - if (this.isOpen) { - this.close(); - } - } - } - - detach() { - if (this.detached) { - return; - } - super.detach(); - this.$dom.off(`focus.${this.eventNS}`); - this.$dom.off(`blur.${this.eventNS}`); - this.$dom.off(`keydown.${this.eventNS}`); - this.$dom.off(`keyup.${this.eventNS}`); - this.dropInstance.destroy(); - this.$menu.remove(); - } - - value() { - return this._value; - } - - focus() { - this.$dom.focus(); - } -} - -_.assign(AutoComplete, DOMAttachedObject); diff --git a/ui/components/autocomplete/userselectautocomplete.page.styl b/ui/components/autocomplete/userselectautocomplete.page.styl deleted file mode 100644 index 2c0329d57..000000000 --- a/ui/components/autocomplete/userselectautocomplete.page.styl +++ /dev/null @@ -1,6 +0,0 @@ -.menu.user-select - width: rem(200px) - -.user-select__uid - font-size: rem($font-size-small) - color: $userselect-uid-color diff --git a/ui/components/calendar/calendar.page.styl b/ui/components/calendar/calendar.page.styl deleted file mode 100644 index 7606ad085..000000000 --- a/ui/components/calendar/calendar.page.styl +++ /dev/null @@ -1,129 +0,0 @@ -.calendar__header - line-height: rem(45px) - text-align: center - - h1 - display: inline-block - line-height: inherit - width: rem(280px) - - button - font-size: rem($font-size-title) - color: $text-3-color - padding: 0 rem(10px) - outline: 0 - - &:hover - color: $primary-color - -.calendar__week-title - table - width: 100% - - th - width: 14.28% - padding: rem(20px 10px) - color: $supplementary-color - -.calendar__row - position: relative - -.calendar__row__bg - position: absolute - left: 0 - top: 0 - width: 100% - height: 100% - - table - width: 100% - height: 100% - - td - width: 14.28% - border: 1px solid $table-border-color - - &.is-current-day - background: lighten($highlight-color, 85%) - -.calendar__row__content - min-height: rem(100px) - position: relative - z-index: 2 - padding-bottom: rem(10px) - - table - width: 100% - - th - width: 14.28% - padding: rem(10px) - color: $text-2-color - - &.is-inactive - opacity: 0.3 - - &.is-current-day - color: $highlight-color - - td - position: relative - height: rem(24px) - -.calendar__banner - position: absolute - display: block - top: rem(2px) - left: rem(5px) - right: rem(5px) - height: rem(20px) - line-height: rem(20px) - padding: rem(0 5px) - font-size: rem($font-size-small) - border-radius: rem(5px) - white-space: nowrap - overflow: hidden - text-overflow: ellipsis - - &, &:visited, &:hover, &:active - color: #FFF - text-decoration: none - - &.is-trunc-begin, &.is-snap-begin - border-top-left-radius: 0 - border-bottom-left-radius: 0 - - &.is-trunc-end, &.is-snap-end - border-top-right-radius: 0 - border-bottom-right-radius: 0 - - &.is-snap-begin - left: 0 - padding-left: rem(10px) - - &.is-snap-end - right: 0 - padding-right: rem(10px) - - &.is-masked - background-image: repeating-linear-gradient( - 45deg, - rgba(#000, 0.1), - rgba(#000, 0.1) 10px, - rgba(#000, 0) 10px, - rgba(#000, 0) 20px - ); - - for key, value in $calendar-banner-color - &.color-{key} - background-color: value - -.calendar__body-container - position: relative - -.calendar__body.exit - position: absolute - left: 0 - top: 0 - width: 100% - user-select: none diff --git a/ui/components/calendar/index.js b/ui/components/calendar/index.js deleted file mode 100644 index 9fde50323..000000000 --- a/ui/components/calendar/index.js +++ /dev/null @@ -1,367 +0,0 @@ -import _ from 'lodash'; -import moment from 'moment'; -import tpl from 'vj/utils/tpl'; - -export default class Calendar { - constructor(events) { - this.$dom = $(tpl` -
    -
    - -

    - -
    -
    - - - - - - - - - - -
    SUNMONTUEWEDTHUFRISAT
    -
    -
    -
    - `); - this.events = events.map((ev) => ({ - ...ev, - beginAt: moment(ev.beginAt), - endAt: moment(ev.endAt), - maskFrom: ev.maskFrom ? moment(ev.maskFrom) : null, - })); - this.$dom.find('[name="prev"]').click(() => this.navToPrev()); - this.$dom.find('[name="next"]').click(() => this.navToNext()); - this.$lastBody = null; - this.navToToday(); - } - - getDom() { - return this.$dom; - } - - navToToday() { - if (this.animating) { - return; - } - this.current = moment().date(1); - this.update(); - } - - navToNext() { - if (this.animating) { - return; - } - this.current.add('months', 1); - this.update(1); - } - - navToPrev() { - if (this.animating) { - return; - } - this.current.subtract('months', 1); - this.update(-1); - } - - update(direction = 0) { - this.updateHeader(); - this.updateBody(direction); - } - - updateHeader() { - this.$dom.find('.calendar__header__title').text(this.current.format('MMMM YYYY')); - } - - async updateBody(direction) { - this.animating = true; - const $newBody = this.buildBody(); - $newBody.appendTo(this.$dom.find('.calendar__body-container')); - if (this.$lastBody !== null) { - this.$lastBody - .addClass('exit') - .transition({ - y: direction * 100, - opacity: 0, - }, { - duration: 500, - easing: 'easeOutCubic', - }); - await $newBody - .css({ - y: -direction * 100, - opacity: 0, - }) - .transition({ - y: 0, - opacity: 1, - }, { - duration: 500, - easing: 'easeOutCubic', - }) - .promise(); - this.$lastBody.remove(); - } - this.$lastBody = $newBody; - this.animating = false; - } - - buildBody() { - const data = this.buildBodyData(); - const $body = $('
    '); - data.forEach((week) => { - const $row = $(tpl`
    -
    -
    -
    `); - const $bgContainer = $row.find('.calendar__row__bg tr'); - const $numContainer = $row.find('.calendar__row__content thead tr'); - const $bannerContainer = $row.find('.calendar__row__content tbody'); - week.days.forEach((day) => { - const isInactive = day.active ? '' : 'is-inactive'; - const isCurrentDay = day.current ? 'is-current-day' : ''; - const today = day.current ? ' (TODAY)' : ''; - $bgContainer.append($('').addClass(isInactive).addClass(isCurrentDay)); - $numContainer.append($(tpl`${day.date.format('D')}${today}`).addClass(isInactive).addClass(isCurrentDay)); - }); - week.banners.forEach((banners) => { - const $tr = $(''); - banners.forEach((bannerSpan) => { - if (!bannerSpan.banner) { - $tr.append(tpl``); - return; - } - const $cell = $(tpl``); - const $banner = $(tpl` - ${bannerSpan.banner.mask ? bannerSpan.banner.event.maskTitle : bannerSpan.banner.event.title} - `); - if (bannerSpan.banner.mask) { - $banner.addClass('is-masked'); - } - if (bannerSpan.banner.beginTrunc) { - $banner.addClass('is-trunc-begin'); - } else if (bannerSpan.banner.beginSnap) { - $banner.addClass('is-snap-begin'); - } - if (bannerSpan.banner.endTrunc) { - $banner.addClass('is-trunc-end'); - } else if (bannerSpan.banner.endSnap) { - $banner.addClass('is-snap-end'); - } - $cell.append($banner); - $tr.append($cell); - }); - $bannerContainer.append($tr); - }); - $body.append($row); - }); - return $body; - } - - buildBodyData() { - const now = moment(); - const days = []; - { - // back fill - const base = this.current.clone(); - const dayOfWeek = base.day(); - if (dayOfWeek > 0) { - base.subtract(dayOfWeek + 1, 'days'); - for (let i = dayOfWeek; i > 0; --i) { - days.push({ - active: false, - date: base.add(1, 'days').clone(), - }); - } - } - } - { - // current month - const base = this.current.clone(); - while (base.month() === this.current.month()) { - days.push({ - active: true, - date: base.clone(), - }); - base.add(1, 'days'); - } - } - { - // forward fill - const base = this.current.clone().add(1, 'months').subtract(1, 'days'); - const dayOfWeek = base.day(); - if (dayOfWeek < 6) { - for (let i = dayOfWeek; i < 6; ++i) { - days.push({ - active: false, - date: base.add(1, 'days').clone(), - }); - } - } - } - - days.forEach((day) => { - day.current = day.date.isSame(now, 'day'); // eslint-disable-line no-param-reassign - }); - - const daysByWeek = _.chunk(days, 7); - - const numberOfWeeks = days.length / 7; - const bannersByWeek = _.fill(new Array(numberOfWeeks), 1).map(() => []); - const beginDate = days[0].date.clone(); - const endDate = _.last(days).date.clone(); - - // cut events by week to banners - this.events.forEach((ev) => { - if (ev.endAt.isBefore(ev.beginAt, 'day')) { - return; - } - if (ev.endAt.isBefore(beginDate, 'day') || ev.beginAt.isAfter(endDate, 'day')) { - return; - } - if (ev.beginAt.hour() >= 22) { - ev.beginAt.add(1, 'day').startOf('day'); - } - if (ev.endAt.hour() <= 2) { - ev.endAt.subtract(1, 'day').endOf('day'); - } - let [bannerBegin, bannerBeginTruncated] = [ev.beginAt.clone(), false]; - if (bannerBegin.isBefore(beginDate, 'day')) { - [bannerBegin, bannerBeginTruncated] = [beginDate.clone(), true]; - } - do { - const bannerEndMax = bannerBegin.clone().endOf('week'); - let [bannerEnd, bannerEndTruncated] = [ev.endAt.clone(), false]; - if (bannerEnd.isAfter(bannerEndMax, 'day')) { - [bannerEnd, bannerEndTruncated] = [bannerEndMax, true]; - } - const weekIndex = bannerBegin.clone().startOf('day').diff(beginDate.clone().startOf('day'), 'week'); - bannersByWeek[weekIndex].push({ - beginAt: bannerBegin.startOf('day'), - beginTrunc: bannerBeginTruncated, - endAt: bannerEnd.endOf('day'), - endTrunc: bannerEndTruncated, - event: ev, - }); - if (!bannerEndTruncated) { - break; - } - [bannerBegin, bannerBeginTruncated] = [bannerEnd.clone().add(1, 'day'), true]; - } while (!bannerBegin.isAfter(endDate, 'day')); - }); - - // layout banners - const layout = bannersByWeek - .map((banners) => _ - .sortBy(banners, [ - (banner) => banner.beginAt.valueOf(), - (banner) => (banner.beginTrunc ? 0 : 1), // truncated events first - (banner) => -banner.endAt.valueOf(), // long events first - ])) - .map((banners) => { - const dayBitmap = _ - .fill(new Array(7), 1) - .map(() => []); - banners.forEach((banner) => { - const beginDay = banner.beginAt.day(); - const endDay = banner.endAt.day(); - // find available space - const vIndexMax = _.max(_ - .range(beginDay, endDay + 1) - .map((day) => dayBitmap[day].length)); - let vIndex = 0; - for (; vIndex < vIndexMax; ++vIndex) { - if (_.every(_ - .range(beginDay, endDay + 1) - .map((day) => !dayBitmap[day][vIndex]) // eslint-disable-line no-loop-func - )) { // eslint-disable-line function-paren-newline - break; - } - } - // fill space - for (let i = beginDay; i <= endDay; ++i) { - dayBitmap[i][vIndex] = banner; - } - }); - // 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) - .map(() => []); - for (let vIndex = 0; vIndex < vMaxLength; ++vIndex) { - let last = { span: 1, banner: dayBitmap[0][vIndex] }; - weekBanners[vIndex].push(last); - for (let day = 1; day < 7; ++day) { - const banner = dayBitmap[day][vIndex]; - if (banner !== last.banner) { - last = { span: 1, banner }; - weekBanners[vIndex].push(last); - } else { - last.span++; - } - } - } - // cut banners by masked areas, scanning from left to right - weekBanners.forEach((bannerSpans) => { - for (let i = 0; i < bannerSpans.length; ++i) { - const { banner } = bannerSpans[i]; - if (!banner) { - continue; - } - if (banner.mask) { - continue; - } - if (!banner.event.maskFrom) { - continue; - } - if (banner.event.maskFrom.isSame(banner.event.endAt)) { - // do not show masks if maskFrom === endAt - continue; - } - if (banner.event.endAt.isSame(banner.event.beginAt, 'day')) { - // do not show masks if endAt - beginAt <= 1 day - continue; - } - if (banner.event.maskFrom.isAfter(banner.endAt, 'day')) { - // we are not in the time region for masking - continue; - } - if (banner.event.maskFrom.isSameOrBefore(banner.beginAt, 'day')) { - // mask begins before this banner: replace current banner with masked banner - banner.mask = true; - } else { - // mask begins during this banner: cut current banner into two pieces - const newBanner = { - ...banner, - beginAt: banner.event.maskFrom.clone(), - beginSnap: true, - beginTrunc: false, - mask: true, - }; - const newBannerSpan = { - span: newBanner.endAt.day() - newBanner.beginAt.day() + 1, - banner: newBanner, - }; - banner.endAt = banner.event.maskFrom.clone().subtract(1, 'day'); - banner.endSnap = true; - bannerSpans[i].span -= newBannerSpan.span; // eslint-disable-line no-param-reassign - bannerSpans.splice(i + 1, 0, newBannerSpan); - i++; - } - } - }); - return weekBanners; - }); - return daysByWeek - .map((daysInWeek, weekIndex) => ({ - days: daysInWeek, - banners: layout[weekIndex], - })); - } -} diff --git a/ui/components/cmeditor/cmeditor.page.js b/ui/components/cmeditor/cmeditor.page.js deleted file mode 100644 index 107411869..000000000 --- a/ui/components/cmeditor/cmeditor.page.js +++ /dev/null @@ -1,23 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; -import delay from 'vj/utils/delay'; -import CmEditor from '.'; - -import 'vj-simplemde/src/css/simplemde.css'; -import './cmeditor.styl'; - -function runSubstitute($container) { - const selector = ['textarea[data-markdown]']; - $container.find(selector.join(', ')).get().forEach((element) => { - CmEditor.getOrConstruct($(element)); - }); -} - -const cmEditorPage = new AutoloadPage('cmEditorPage', () => { - runSubstitute($('body')); - $(document).on('vjContentNew', async (e) => { - await delay(0); - runSubstitute($(e.target)); - }); -}); - -export default cmEditorPage; diff --git a/ui/components/cmeditor/cmeditor.styl b/ui/components/cmeditor/cmeditor.styl deleted file mode 100644 index 6c00da3f4..000000000 --- a/ui/components/cmeditor/cmeditor.styl +++ /dev/null @@ -1,91 +0,0 @@ -.simplemde - border: 1px solid $editor-border-color - margin: 0 0 1rem - position: relative - height: rem(200px) - box-sizing: border-box - overflow: hidden - -.cm-s-simplemde - border: 0 - min-height: 0 - height: auto - padding: 0 - position: absolute - left: 0 - top: 40px - width: 100% - bottom: 0 - color: inherit - -.simplemde-toolbar - position: absolute - left: 0 - top: 0 - width: 100% - border-radius: 0 - border: 0 - opacity: 0 - background: $toolbar-bg - transform: translateY(-20px) - transition: opacity .5s, transform .5s - transition-timing-function: ease-out-cubic - - &:hover - opacity: 1 - -.toolbar--visible .simplemde-toolbar - opacity: 1 - transform: translateY(0) - -.cm-s-simplemde .CodeMirror-scroll - height: 100% - min-height: 0 - font-family: $code-font-family - color: $text-1-color - -.simplemde-toolbar:before, -.simplemde-toolbar:after - display: none - -.simplemde-toolbar - padding: rem(5px) - -.simplemde-toolbar i.separator - border-right: 0 - -.simplemde-toolbar a - color: $toolbar-fore !important - border: 0 - background: none - border-radius: 0 - font-size: rem($font-size-icon) - outline: 0 - - &:hover - background: $toolbar-bg-hover - color: $toolbar-fore-hover !important - border: 0 - - &.active - background: $toolbar-bg-selected - color: $toolbar-fore-selected !important - border: 0 - -.simplemde-toolbar.disabled-for-preview a:not(.no-disable) - background: none - opacity: $toolbar-disabled-opacity - -.cm-s-simplemde .CodeMirror-code .cm-comment - background: none - -.simplemde-statusbar - display: none - -.simplemde-preview - background: #FFF - -.no-heading .cm-s-simplemde .CodeMirror-code - .cm-header-1, .cm-header-2, .cm-header-3, .cm-header-4 - font-size: 1em - line-height: 1.5 diff --git a/ui/components/cmeditor/index.js b/ui/components/cmeditor/index.js deleted file mode 100644 index bf6cea053..000000000 --- a/ui/components/cmeditor/index.js +++ /dev/null @@ -1,66 +0,0 @@ -import _ from 'lodash'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; - -export default class CmEditor extends DOMAttachedObject { - static DOMAttachKey = 'vjCmEditorInstance'; - - constructor($dom, options = {}) { - super($dom); - this.editor = null; - this.options = options; - this.init(); - } - - async init() { - const { default: VjCmEditor } = await import('./vjcmeditor'); - - const hasFocus = this.$dom.is(':focus'); - const srcHeight = this.$dom.outerHeight(); - - this.editor = new VjCmEditor({ - ...this.options, - element: this.$dom.get(0), - }); - this.moveToEnd(); - - const $editor = $(this.editor.wrapper); - $editor.css('height', srcHeight); - $editor.addClass('toolbar--visible'); - - if (hasFocus) { - this.focus(); - } - } - - isValid() { - return (this.editor !== null); - } - - ensureValid() { - if (!this.isValid()) { - throw new Error('VjCmEditor is not loaded'); - } - } - - value(...argv) { - this.ensureValid(); - const ret = this.editor.value(...argv); - if (argv.length > 0) { - this.moveToEnd(); - } - return ret; - } - - focus() { - this.ensureValid(); - this.editor.codemirror.focus(); - } - - moveToEnd() { - this.ensureValid(); - const cm = this.editor.codemirror; - cm.setCursor(cm.lineCount(), 0); - } -} - -_.assign(CmEditor, DOMAttachedObject); diff --git a/ui/components/cmeditor/textareaHandler.js b/ui/components/cmeditor/textareaHandler.js deleted file mode 100644 index c959c4d83..000000000 --- a/ui/components/cmeditor/textareaHandler.js +++ /dev/null @@ -1,32 +0,0 @@ -import _ from 'lodash'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import CmEditor from '.'; - -export default class TextareaHandler extends DOMAttachedObject { - static DOMAttachKey = 'vjTextareaHandlerInstance'; - - getCmEditor() { - return CmEditor.get(this.$dom); - } - - isCmEditor() { - const editor = this.getCmEditor(); - return (editor !== undefined && editor.isValid()); - } - - val(...argv) { - if (this.isCmEditor()) { - return this.getCmEditor().value(...argv); - } - return this.$dom.val(...argv); - } - - focus() { - if (this.isCmEditor()) { - this.getCmEditor().focus(); - } - this.$dom.focus(); - } -} - -_.assign(TextareaHandler, DOMAttachedObject); diff --git a/ui/components/cmeditor/vjcmeditor.js b/ui/components/cmeditor/vjcmeditor.js deleted file mode 100644 index 06c1abebb..000000000 --- a/ui/components/cmeditor/vjcmeditor.js +++ /dev/null @@ -1,130 +0,0 @@ -import _ from 'lodash'; -import SimpleMDE from 'vj-simplemde'; - -import 'codemirror/mode/clike/clike'; -import 'codemirror/mode/pascal/pascal'; -import 'codemirror/mode/python/python'; -import 'codemirror/mode/php/php'; -import 'codemirror/mode/rust/rust'; -import 'codemirror/mode/haskell/haskell'; -import 'codemirror/mode/javascript/javascript'; -import 'codemirror/mode/go/go'; -import 'codemirror/mode/ruby/ruby'; - -import request from 'vj/utils/request'; -import i18n from 'vj/utils/i18n'; - -export default class VjCmEditor extends SimpleMDE { - constructor(options = {}) { - super({ - autoDownloadFontAwesome: false, - spellChecker: false, - forceSync: true, - toolbar: [ - { - name: 'bold', - action: SimpleMDE.toggleBold, - className: 'icon icon-bold', - title: i18n('Bold'), - }, - { - name: 'italic', - action: SimpleMDE.toggleItalic, - className: 'icon icon-italic', - title: i18n('Italic'), - }, - '|', - { - name: 'quote', - action: SimpleMDE.toggleBlockquote, - className: 'icon icon-quote', - title: i18n('Quote'), - }, - { - name: 'unordered-list', - action: SimpleMDE.toggleUnorderedList, - className: 'icon icon-unordered_list', - title: i18n('Unordered List'), - }, - { - name: 'ordered-list', - action: SimpleMDE.toggleOrderedList, - className: 'icon icon-ordered_list', - title: i18n('Ordered List'), - }, - '|', - { - name: 'code', - action: () => this.insertCodeBlock(), - className: 'icon icon-code', - title: i18n('Insert Code'), - default: true, - }, - { - name: 'link', - action: SimpleMDE.drawLink, - className: 'icon icon-link', - title: i18n('Create Link'), - default: true, - }, - { - name: 'image', - action: SimpleMDE.drawImage, - className: 'icon icon-insert--image', - title: i18n('Insert Image'), - default: true, - }, - '|', - { - name: 'preview', - action: SimpleMDE.togglePreview, - preAction: SimpleMDE.preRenderPreview, - className: 'icon icon-preview no-disable', - title: i18n('Toggle Preview'), - default: true, - }, - ], - commonmark: { - safe: true, - }, - ...options, - }); - } - - async markdown(text) { - // FIXME preview wont update after first toggle - const data = await request.ajax({ - url: '/markdown', - method: 'post', - data: $.param({ text }, true), - dataType: 'text', - }); - _.defer(this.preparePreview.bind(this)); - return data; - } - - preparePreview() { - const $preview = $(this.wrapper).find('.simplemde-preview'); - $preview.addClass('typo'); - $preview.attr('data-emoji-enabled', 'true'); - $preview.trigger('vjContentNew'); - } - - insertCodeBlock() { - const text = this.codemirror.getSelection(); - const startPoint = this.codemirror.getCursor('start'); - const endPoint = this.codemirror.getCursor('end'); - const leadingLines = (startPoint.line === 0 && startPoint.ch === 0) ? 0 : 2; - // eslint-disable-next-line prefer-template - const startText = _.repeat('\n', leadingLines) + '```cpp\n'; - const endText = '\n```\n'; - this.codemirror.replaceSelection(`${startText}${text}${endText}`); - startPoint.line += leadingLines + 1; - startPoint.ch = 0; - if (startPoint !== endPoint) { - endPoint.line += leadingLines + 1; - } - this.codemirror.setSelection(startPoint, endPoint); - this.codemirror.focus(); - } -} diff --git a/ui/components/contest/contest.page.styl b/ui/components/contest/contest.page.styl deleted file mode 100644 index 7fd9ba71b..000000000 --- a/ui/components/contest/contest.page.styl +++ /dev/null @@ -1,34 +0,0 @@ -.contest-type-tag -.contest-tag - font-size: rem($font-size-small) - display: inline-block - line-height: rem(22px) - padding: rem(0 10px) - vertical-align: middle - border-radius: rem(12px) - - &:hover - text-decoration: none - color: #FFF !important - -.contest-type-tag - &, &:visited - &, .supplementary & - color: #FFF - - .icon - margin-right: rem(5px) - -for key, value in $contest-color - .contest-type--{key} .contest-type-tag - background: value - - &:hover - background: lighten(value, 20%) - -.contest-tag - border: 1px solid $supplementary-border-color - - &:hover - background: $primary-color - border-color: $primary-color diff --git a/ui/components/contest/contest_sidebar.page.styl b/ui/components/contest/contest_sidebar.page.styl deleted file mode 100644 index 8e684f686..000000000 --- a/ui/components/contest/contest_sidebar.page.styl +++ /dev/null @@ -1,21 +0,0 @@ -.contest-sidebar__bg - display: block - background: #67AABB url(problem-contest-bg.png) right top no-repeat - color: #FFF !important - font-size: rem($font-size) - padding: rem(20px 0) - - +retina() - background-image: url(problem-contest-bg@2x.png) - background-size: 131px 150px - - h1 - font-size: rem($font-size-title) - color: #FFF - text-shadow: 0 1px 3px rgba(#000, 0.5) - - &:hover - text-decoration: none - - .contest-sidebar__status - margin-top: rem(20px) diff --git a/ui/components/contest/problem-contest-bg.png b/ui/components/contest/problem-contest-bg.png deleted file mode 100644 index 63ea08cad..000000000 Binary files a/ui/components/contest/problem-contest-bg.png and /dev/null differ diff --git a/ui/components/contest/problem-contest-bg@2x.png b/ui/components/contest/problem-contest-bg@2x.png deleted file mode 100644 index 16272e1af..000000000 Binary files a/ui/components/contest/problem-contest-bg@2x.png and /dev/null differ diff --git a/ui/components/datepicker/datepicker.page.js b/ui/components/datepicker/datepicker.page.js deleted file mode 100644 index 597b1f061..000000000 --- a/ui/components/datepicker/datepicker.page.js +++ /dev/null @@ -1,26 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -import 'pickadate/lib/themes/classic.css'; -import 'pickadate/lib/themes/classic.date.css'; -import 'pickadate/lib/themes/classic.time.css'; -import './datepicker.styl'; - -const datepickerPage = new AutoloadPage('datepickerPage', async () => { - if ($('[data-pick-date]').length > 0) { - await import('pickadate/lib/picker.date'); - $('[data-pick-date]').pickadate({ - format: 'yyyy-m-d', - clear: false, - }); - } - if ($('[data-pick-time]').length > 0) { - await import('pickadate/lib/picker.time'); - $('[data-pick-time]').pickatime({ - format: 'H:i', - interval: 15, - clear: false, - }); - } -}); - -export default datepickerPage; diff --git a/ui/components/datepicker/datepicker.styl b/ui/components/datepicker/datepicker.styl deleted file mode 100644 index 16e60db05..000000000 --- a/ui/components/datepicker/datepicker.styl +++ /dev/null @@ -1,54 +0,0 @@ -.picker__holder - margin-top: -1rem - border-radius: 0 - transform: scale(0.9) translateZ(0) - transform-origin: 0 0 - outline: 0 - font-size: rem($font-size-secondary) - transition: transform .1s ease-in-out, - opacity .1s ease-in-out, - max-height 0s .1s, - border-width 0s .1s - -.picker--opened .picker__holder - transform: scale(1) translateZ(0) - transition: transform .1s ease-in-out, - opacity .1s ease-in-out, - max-height 0s, - border-width 0s - box-shadow: $data-picker-shadow - -.picker__list - padding: 0 - -.picker__list-item - border: 0 - -.picker__list-item:hover - background: $datapicker-list-item-bg-color-hover - -.picker__list-item--selected, .picker__list-item--selected:hover, .picker--focused .picker__list-item--selected - background: $primary-color - -.picker__day--infocus:hover, .picker__day--outfocus:hover - background: $datapicker-list-item-bg-color-hover - -.picker__day--highlighted - border-color: transparent - -.picker__day--highlighted:hover, .picker--focused .picker__day--highlighted - background: transparent - -.picker__day--selected, .picker__day--selected:hover, .picker--focused .picker__day--selected - background: $primary-color - -.picker__button--today:hover, .picker__button--clear:hover, .picker__button--close:hover - background: $datapicker-list-item-bg-color-hover - border-color: $datapicker-list-item-bg-color-hover - -.picker__button--today:focus, .picker__button--clear:focus, .picker__button--close:focus - background: $datapicker-list-item-bg-color-hover - border-color: $datapicker-list-item-bg-color-hover - -.picker__nav--prev:hover, .picker__nav--next:hover - background: $datapicker-list-item-bg-color-hover diff --git a/ui/components/dialog/DomDialog.js b/ui/components/dialog/DomDialog.js deleted file mode 100644 index a401f143e..000000000 --- a/ui/components/dialog/DomDialog.js +++ /dev/null @@ -1,145 +0,0 @@ -import _ from 'lodash'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; - -import zIndexManager from 'vj/utils/zIndexManager'; - -export default class DomDialog extends DOMAttachedObject { - static DOMAttachKey = 'vjDomDialogInstance'; - - constructor($dom, options = {}) { - super($dom); - this.isShown = false; - this.isAnimating = false; - this.options = { - cancelByClickingBack: false, - cancelByEsc: false, - onDispatch: () => {}, - ...options, - }; - this._defer = null; - } - - async _show() { - this.$dom.css('z-index', zIndexManager.getNext()); - this.$dom.trigger('vjDomDialogShow'); - this.isAnimating = true; - - if (this.options.cancelByClickingBack) { - this.$dom.on(`click.${this.eventNS}`, this.handleClick.bind(this)); - } - if (this.options.cancelByEsc) { - $(document).on(`keyup.${this.eventNS}`, this.handleKeyUp.bind(this)); - } - - const $wrap = this.$dom; - $wrap.css({ - display: 'flex', - opacity: 0, - }); - $wrap.width(); - $wrap.transition({ - opacity: 1, - }, { - duration: 100, - easing: 'easeOutCubic', - }); - - const $dgContent = this.$dom.find('.dialog__content'); - $dgContent.css({ - scale: 0.8, - }); - await $dgContent - .transition({ - scale: 1, - }, { - duration: 200, - easing: 'easeOutCubic', - }) - .promise(); - - this.$dom.find('[data-autofocus]').focus(); - - this.isShown = true; - this.isAnimating = false; - this.$dom.trigger('vjDomDialogShown'); - } - - async _hide() { - this.$dom.trigger('vjDomDialogHide'); - this.isAnimating = true; - - $(document).off(`keyup.${this.eventNS}`); - this.$dom.off(`click.${this.eventNS}`); - - this.$dom.css({ - opacity: 1, - }); - this.$dom.transition({ - opacity: 0, - }, { - duration: 200, - }); - - const $dgContent = this.$dom.find('.dialog__content'); - $dgContent.css({ - scale: 1, - }); - await $dgContent - .transition({ - scale: 0.8, - }, { - duration: 200, - easing: 'easeOutCubic', - }) - .promise(); - - this.$dom.css('display', 'none'); - - this.isShown = false; - this.isAnimating = false; - this.$dom.trigger('vjDomDialogHidden'); - } - - show() { - if (this.isShown || this.isAnimating) { - return Promise.reject(); - } - this._defer = new $.Deferred(); - this._show(); - return this._defer.promise(); - } - - hide() { - if (!this.isShown || this.isAnimating) { - return false; - } - if (this._defer.state() === 'pending') { - this._defer.resolve('cancel'); - } - this._defer = null; - this._hide(); - return true; - } - - dispatchAction(data) { - if (this.options.onDispatch(data) === false) { - return; - } - this._defer.resolve(data); - this.hide(); - } - - handleClick(e) { - if (e.target === this.$dom.get(0)) { - this.dispatchAction('cancel'); - } - } - - handleKeyUp(e) { - if (e.keyCode === 27) { - this.dispatchAction('cancel'); - } - } -} - -_.assign(DomDialog, DOMAttachedObject); diff --git a/ui/components/dialog/dialog.page.styl b/ui/components/dialog/dialog.page.styl deleted file mode 100644 index b4a5da94e..000000000 --- a/ui/components/dialog/dialog.page.styl +++ /dev/null @@ -1,38 +0,0 @@ -.dialog - position: fixed - left: 0 - top: 0 - width: 100% - height: 100% - opacity: 0 - z-index: 1000 // managed - // display flex will be added after animation completes - flex-direction: column - align-items: center - justify-content: center - display: none - background: $dialog-layer-bg-color - -.dialog__content - position: relative - -.dialog.withBg .dialog__content - background: $dialog-bg-color - border: 1px solid $dialog-border-color - box-shadow: $dialog-shadow - min-width: rem(500px) - max-width: rem(900px) - padding: rem(40px) rem(30px) - -.dialog__body - margin-bottom: rem(20px) - min-height: rem(50px) - - h1 - font-size: 1.6rem - color: $dialog-title-color - margin-bottom: 1rem - -.dialog__action button - margin-left: rem(10px) - margin-bottom: 0 diff --git a/ui/components/dialog/index.js b/ui/components/dialog/index.js deleted file mode 100644 index af635b938..000000000 --- a/ui/components/dialog/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import tpl from 'vj/utils/tpl'; -import i18n from 'vj/utils/i18n'; -import DomDialog from './DomDialog'; - -export default class Dialog { - constructor(options = {}) { - this.options = { - classes: '', - $body: null, - $action: null, - ...options, - }; - this.$dom = $(tpl` - - `); - this.$dom.on('click', '[data-action]', this.handleActionButton.bind(this)); - this.$dom.on('vjDomDialogShow', this.beforeShow.bind(this)); - this.$dom.on('vjDomDialogHidden', this.afterHide.bind(this)); - this.$dom.find('.dialog__body').append(this.options.$body); - this.$dom.find('.dialog__action').append(this.options.$action); - this.domDialogInstance = new DomDialog(this.$dom, this.options); - } - - beforeShow() { - this.$dom.appendTo('body'); - } - - afterHide() { - this.$dom.detach(); - } - - open() { - return this.domDialogInstance.show(); - } - - close() { - return this.domDialogInstance.hide(); - } - - handleActionButton(ev) { - this.domDialogInstance.dispatchAction($(ev.currentTarget).attr('data-action')); - } -} - -const buttonOk = tpl``; -const buttonCancel = tpl``; -const buttonYes = tpl``; -const buttonNo = tpl``; - -export class InfoDialog extends Dialog { - constructor(options = {}) { - super({ - $action: buttonOk, - cancelByClickingBack: true, - cancelByEsc: true, - ...options, - }); - } -} - -export class ActionDialog extends Dialog { - constructor(options = {}) { - super({ - $action: [buttonCancel, buttonOk].join('\n'), - cancelByClickingBack: true, - cancelByEsc: true, - ...options, - }); - } -} - -export class ConfirmDialog extends Dialog { - constructor(options = {}) { - let buttons = []; - if (options.canCancel) { - buttons = [buttonCancel, buttonNo, buttonYes]; - } else { - buttons = [buttonNo, buttonYes]; - } - super({ - $action: buttons.join('\n'), - cancelByClickingBack: options.canCancel, - cancelByEsc: options.canCancel, - ...options, - }); - } -} diff --git a/ui/components/discussion/CommentBox.js b/ui/components/discussion/CommentBox.js deleted file mode 100644 index 23146ecb8..000000000 --- a/ui/components/discussion/CommentBox.js +++ /dev/null @@ -1,105 +0,0 @@ -import _ from 'lodash'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import request from 'vj/utils/request'; -import TextareaHandler from 'vj/components/cmeditor/textareaHandler'; -import Notification from 'vj/components/notification'; - -let initialized = false; -const $template = $('.dczcomments__box').eq(0).clone(); - -function getClosestInstance($dom) { - return $dom.closest('.dczcomments__box').data('instance'); -} - -function onBoxCancel(ev) { - const commentBox = getClosestInstance($(ev.currentTarget)); - if (commentBox) commentBox.onCancel(ev); -} - -function onBoxSubmit(ev) { - const commentBox = getClosestInstance($(ev.currentTarget)); - if (commentBox) commentBox.onSubmit(ev); - ev.preventDefault(); -} - -function init() { - if (initialized) return; - $(document).on('submit', '[name="dczcomments__box__form"]', onBoxSubmit); - $(document).on('vjCommentBoxCancel', '[name="dczcomments__box__form"]', onBoxCancel); - $(document).on('click', '[name="dczcomments__box__cancel"]', onBoxCancel); - initialized = true; -} - -export default class CommentBox extends DOMAttachedObject { - static DOMAttachKey = 'vjCommentBoxInstance'; - - constructor($dom, options = {}) { - super($dom); - init(); - this.$box = $template.clone(); - this.$box.data('instance', this); - this.options = { - initialText: null, - mode: null, - form: null, - onCancel: () => null, - ...options, - }; - if (this.options.initialText) this.setText(this.options.initialText); - if (this.options.mode) { - const submitButton = this.$box.find('.dczcomments__box__submit'); - submitButton.val(submitButton.attr(`data-value-${this.options.mode}`)); - } - } - - getTextareaHandler() { - const $textarea = this.$box.find('textarea'); - return TextareaHandler.getOrConstruct($textarea); - } - - focus() { - this.getTextareaHandler().focus(); - return this; - } - - setText(text) { - this.getTextareaHandler().val(text); - return this; - } - - getText() { - return this.getTextareaHandler().val(); - } - - insertText(text) { - const handler = this.getTextareaHandler(); - handler.val(handler.val() + text); - return this; - } - - appendTo($dom) { - this.$box.appendTo($dom); - this.$box.trigger('vjContentNew'); - return this; - } - - async onSubmit() { - try { - await request.post('', { - ...this.options.form, - content: this.getText(), - }); - window.location.reload(); - } catch (error) { - Notification.error(error.message); - } - } - - async onCancel(ev) { - await this.options.onCancel(ev); - this.$box.remove(); - this.detach(); - } -} - -_.assign(CommentBox, DOMAttachedObject); diff --git a/ui/components/discussion/comments.page.js b/ui/components/discussion/comments.page.js deleted file mode 100644 index b17483ec3..000000000 --- a/ui/components/discussion/comments.page.js +++ /dev/null @@ -1,206 +0,0 @@ -import 'jquery.easing'; - -import { AutoloadPage } from 'vj/misc/PageLoader'; -import CommentBox from 'vj/components/discussion/CommentBox'; -import { ConfirmDialog } from 'vj/components/dialog'; - -import delay from 'vj/utils/delay'; -import { slideDown, slideUp } from 'vj/utils/slide'; -import request from 'vj/utils/request'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; - -const $replyTemplate = $('.commentbox-container').eq(0).clone(); - -function createReplyContainer($parent) { - const $container = $replyTemplate - .clone() - .hide() - .prependTo($parent.find('.commentbox-reply-target').eq(0)) - .trigger('vjContentNew'); - return $container.find('.commentbox-placeholder'); -} - -async function showReplyContainer($parent) { - const $container = $parent.find('.commentbox-container'); - // TODO: fix ugly hack. cannot get $container rect because it is invisible - const rect = $container.parent()[0].getBoundingClientRect(); - const rectBody = document.body.getBoundingClientRect(); - if (rect.top < 100 || rect.top + 100 > window.innerHeight) { - const targetScrollTop = rect.top - rectBody.top - window.innerHeight * 0.382; - $('html, body').stop().animate({ scrollTop: targetScrollTop }, 400, 'easeOutCubic'); - await delay(300); - // delay duration is set smaller than animation duration intentionally - } - $container.css('opacity', 0); - await slideDown($container, 300); - await $container.transition({ opacity: 1 }, { duration: 200 }).promise(); - $container.removeAttr('style'); -} - -async function destroyReplyContainer($parent) { - const $container = $parent.find('.commentbox-container'); - $container.css('opacity', 1); - await $container.transition({ opacity: 0 }, { duration: 200 }).promise(); - await slideUp($container, 300); - $container.remove(); -} - -function onClickDummyBox(ev) { - const $evTarget = $(ev.currentTarget); - - if (CommentBox.get($evTarget)) { - CommentBox - .get($evTarget) - .focus(); - return; - } - - const $mediaBody = $evTarget.closest('.media__body'); - - const opt = { - form: JSON.parse($evTarget.attr('data-form')), - mode: 'comment', - onCancel: () => { - $mediaBody.removeClass('is-editing'); - }, - }; - - $mediaBody.addClass('is-editing'); - - CommentBox - .getOrConstruct($evTarget, opt) - .appendTo($mediaBody.find('.commentbox-placeholder').eq(0)) - .focus(); -} - -async function onCommentClickReplyComment(ev, options = {}) { - const $evTarget = $(ev.currentTarget); - - if (CommentBox.get($evTarget)) { - // If comment box is already expanded, - // we should insert "initialText" - CommentBox - .get($evTarget) - .insertText(options.initialText || '') - .focus(); - return; - } - - const $mediaBody = $evTarget.closest('.media__body'); - - const opt = { - initialText: '', - mode: 'reply', - ...options, - onCancel: async () => { - await destroyReplyContainer($mediaBody); - }, - }; - - const cbox = CommentBox - .getOrConstruct($evTarget, { - form: JSON.parse($evTarget.attr('data-form')), - ...opt, - }) - .appendTo(createReplyContainer($mediaBody)); - await showReplyContainer($mediaBody); - cbox.focus(); -} - -async function onCommentClickReplyReply(ev) { - const $evTarget = $(ev.currentTarget); - const $mediaBody = $evTarget.closest('.media__body'); - const username = $mediaBody - .find('.user-profile-name').eq(0) - .text(); - - $evTarget - .closest('.dczcomments__item') - .find('[name="dczcomments__op-reply-comment"]').eq(0) - .trigger('click', { initialText: `@${username}: ` }); -} - -async function onCommentClickEdit(mode, ev) { - const $evTarget = $(ev.currentTarget); - - if (CommentBox.get($evTarget)) { - CommentBox - .get($evTarget) - .focus(); - return; - } - - const $mediaBody = $evTarget.closest('.media__body'); - - const raw = await request.get( - $mediaBody.find('.typo').eq(0).attr('data-raw-url'), - {}, - { dataType: 'text' }, - ); - - const opt = { - initialText: raw, - form: JSON.parse($evTarget.attr('data-form')), - mode, - onCancel: () => { - $mediaBody.removeClass('is-editing'); - }, - }; - - $mediaBody.addClass('is-editing'); - - CommentBox - .getOrConstruct($evTarget, opt) - .appendTo($mediaBody.find('.commentbox-edit-target').eq(0)) - .focus(); -} - -function onCommentClickEditComment(ev) { - return onCommentClickEdit('comment-update', ev); -} - -function onCommentClickEditReply(ev) { - return onCommentClickEdit('reply-update', ev); -} - -async function onCommentClickDelete(type, ev) { - const message = (type === 'comment') - ? 'Confirm deleting this comment? Its replies will be deleted as well.' - : 'Confirm deleting this reply?'; - const action = await new ConfirmDialog({ - $body: tpl` -
    -

    ${i18n(message)}

    -
    `, - }).open(); - if (action !== 'yes') { - return; - } - - const $evTarget = $(ev.currentTarget); - const form = JSON.parse($evTarget.attr('data-form')); - - await request.post('', form); - window.location.reload(); -} - -function onCommentClickDeleteComment(ev) { - onCommentClickDelete('comment', ev); -} - -function onCommentClickDeleteReply(ev) { - onCommentClickDelete('reply', ev); -} - -const commentsPage = new AutoloadPage('commentsPage', () => { - $(document).on('click', '[name="dczcomments__dummy-box"]', onClickDummyBox); - $(document).on('click', '[name="dczcomments__op-reply-comment"]', onCommentClickReplyComment); - $(document).on('click', '[name="dczcomments__op-reply-reply"]', onCommentClickReplyReply); - $(document).on('click', '[name="dczcomments__op-edit-comment"]', onCommentClickEditComment); - $(document).on('click', '[name="dczcomments__op-edit-reply"]', onCommentClickEditReply); - $(document).on('click', '[name="dczcomments__op-delete-comment"]', onCommentClickDeleteComment); - $(document).on('click', '[name="dczcomments__op-delete-reply"]', onCommentClickDeleteReply); -}); - -export default commentsPage; diff --git a/ui/components/discussion/comments.page.styl b/ui/components/discussion/comments.page.styl deleted file mode 100644 index 4cb8106bc..000000000 --- a/ui/components/discussion/comments.page.styl +++ /dev/null @@ -1,125 +0,0 @@ -$upvote-color = green -$downvote-color = red - -.dczcomments__item .is-editing - [name="dczcomments__dummy-box"] - display: none - > .typo - display: none - -.dczcomments__box .textbox - height: rem(180px) - resize: vertical - -.textbox[name="dczcomments__dummy-box"] - height: rem(60px) - resize: none - font-size: rem($font-size-title) - color: $comment-placeholder-color - padding: rem(10px) - -.dczcomments__item, -.dczcomments__reply - border-top: 1px solid $table-border-color - - >.media - padding: rem(20px 0) - - &:first-child - border-top: 0 - -.dczcomments__reply - &:last-child - >.media - padding-bottom: 0 - -.dczcomments__supplementary - float: left - margin-bottom: rem(5px) - -.dczcomments__operations - font-size: rem($font-size) - float: right - vertical-align: top - color: $comment-op-color - - a, a:visited - color: $comment-op-link-color - margin-left: rem(3px) - a:hover - color: $primary-color - text-decoration: none - -.view--solution .dczcomments__replies - margin-left: rem(50px) - -.dczcomments__item - .vote-number - width: rem(82px) - text-align: center - font-size: rem(25px) - font-weight: 300 - margin-bottom: rem(10px) - - .vote-op - position: relative - border: 1px solid transparent - border-radius: rem(14px) - transition: border-color .2s linear, opacity .2s linear - opacity: 0.5 - - &>form - width: rem(40px) - float: left - position: relative - - &:hover .vote-op - opacity: 1 - border-color: $supplementary-border-color - - .vote-button - appearance: none - border: 0 - background: none - width: 100% - outline: 0 - font-size: rem($font-size-icon) - line-height: rem(26px) - color: #AAA - transition: color .2s linear, background-color .2s linear, box-shadow .2s linear - - &.upvote - border-radius: rem(13px 0 0 13px) - padding-left: rem(7px) - - &:hover - background: $upvote-color - color: #FFF - box-shadow: 0 2px 10px rgba($upvote-color, 0.4) - - &.active - color: $upvote-color - - - &.downvote - border-radius: rem(0 13px 13px 0) - padding-right: rem(7px) - - &:hover - background: $downvote-color - color: #FFF - box-shadow: 0 2px 10px rgba($downvote-color, 0.4) - - &.active - color: $downvote-color - - &.active - background: transparent !important - box-shadow: none !important - cursor: default - - -+mobile() - .dczcomments__item - .media__left - display: none diff --git a/ui/components/discussion/discussion.page.styl b/ui/components/discussion/discussion.page.styl deleted file mode 100644 index 387ea3a4d..000000000 --- a/ui/components/discussion/discussion.page.styl +++ /dev/null @@ -1,17 +0,0 @@ -.discussion-node-tag - font-size: rem($font-size-small) - display: inline-block - line-height: rem(22px) - padding: rem(0 10px) - vertical-align: middle - border-radius: rem(12px) - border: 1px solid $supplementary-border-color - - &:hover - background: $primary-color - color: #FFF !important - text-decoration: none - border-color: $primary-color - - .icon - margin-right: rem(5px) diff --git a/ui/components/drop/drop.page.styl b/ui/components/drop/drop.page.styl deleted file mode 100644 index a5b964884..000000000 --- a/ui/components/drop/drop.page.styl +++ /dev/null @@ -1,40 +0,0 @@ -.drop-element, -.drop-element:after, -.drop-element:before, -.drop-element *, -.drop-element *:after, -.drop-element *:before - box-sizing: border-box - -.drop-element - position: absolute - display: none - opacity: 0 - transition: opacity .1s ease-in-out - z-index: 1000 // managed - - &.drop-open - display: block - - &.drop-open-transitionend - display: block - - &.drop-after-open - opacity: 1 - -.drop-target-attached-top.drop-target-attached-left .drop-content - transform-origin: -10% 110% -.drop-target-attached-top.drop-target-attached-center .drop-content - transform-origin: 50% 110% -.drop-target-attached-top.drop-target-attached-right .drop-content - transform-origin: 110% 110% -.drop-target-attached-bottom.drop-target-attached-left .drop-content - transform-origin: -10% -10% -.drop-target-attached-bottom.drop-target-attached-center .drop-content - transform-origin: 50% -10% -.drop-target-attached-bottom.drop-target-attached-right .drop-content - transform-origin: 110% -10% -.drop-target-attached-center.drop-target-attached-right .drop-content - transform-origin: -10% 50% -.drop-target-attached-center.drop-target-attached-left .drop-content - transform-origin: 110% 50% diff --git a/ui/components/dropdown/Dropdown.js b/ui/components/dropdown/Dropdown.js deleted file mode 100644 index 9b48a8b1e..000000000 --- a/ui/components/dropdown/Dropdown.js +++ /dev/null @@ -1,55 +0,0 @@ -import Drop from 'tether-drop'; -import _ from 'lodash'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import responsiveCutoff from 'vj/breakpoints.json'; - -import zIndexManager from 'vj/utils/zIndexManager'; -import { isBelow } from 'vj/utils/mediaQuery'; - -export default class Dropdown extends DOMAttachedObject { - static DOMAttachKey = 'vjDropdownInstance'; - - static DOMAttachSelector = '[data-dropdown-target]'; - - constructor($dom, options = {}) { - if ($dom.attr('data-dropdown-trigger-desktop-only') !== undefined) { - if (isBelow(responsiveCutoff.mobile)) { - super(null); - return; - } - } - super($dom); - this.options = { - target: null, - position: $dom.attr('data-dropdown-pos') || 'bottom left', - ...options, - }; - this.dropInstance = new Drop({ - target: $dom[0], - classes: 'dropdown', - content: this.options.target || $.find($dom.attr('data-dropdown-target'))[0], - position: this.options.position, - openOn: 'hover', - constrainToWindow: true, - constrainToScrollParent: false, - }); - this.dropInstance.on('open', this.onDropOpen.bind(this)); - this.dropInstance.on('close', this.onDropClose.bind(this)); - } - - detach() { - super.detach(); - this.dropInstance.destroy(); - } - - onDropOpen() { - $(this.dropInstance.drop).css('z-index', zIndexManager.getNext()); - this.$dom.trigger('vjDropdownShow'); - } - - onDropClose() { - this.$dom.trigger('vjDropdownHide'); - } -} - -_.assign(Dropdown, DOMAttachedObject); diff --git a/ui/components/dropdown/dropdown.page.js b/ui/components/dropdown/dropdown.page.js deleted file mode 100644 index a1d2587b1..000000000 --- a/ui/components/dropdown/dropdown.page.js +++ /dev/null @@ -1,8 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; -import Dropdown from './Dropdown'; - -const dropdownPage = new AutoloadPage('dropdownPage', () => { - Dropdown.registerLifeCycleHooks(); -}); - -export default dropdownPage; diff --git a/ui/components/dropdown/dropdown.page.styl b/ui/components/dropdown/dropdown.page.styl deleted file mode 100644 index 6cac894cc..000000000 --- a/ui/components/dropdown/dropdown.page.styl +++ /dev/null @@ -1,24 +0,0 @@ -.dropdown-target - display: none - -.drop .dropdown-target - display: block - -.dropdown - .menu - min-width: rem(150px) - box-shadow: $menu-drop-shadow - background: $menu-drop-bg-color - font-size: rem($font-size-small) - - .menu__link - padding: rem(8px 10px) - -.dropdown - .drop-content - transition: transform .1s ease-in-out - transform: scale(0.9) translateZ(0) - - &.drop-after-open - .drop-content - transform: scale(1) translateZ(0) diff --git a/ui/components/emoji/emoji.page.styl b/ui/components/emoji/emoji.page.styl deleted file mode 100644 index 7991fd566..000000000 --- a/ui/components/emoji/emoji.page.styl +++ /dev/null @@ -1,6 +0,0 @@ -.emoji - width: 1em - height: 1em - display: inline-block - background-size: contain - vertical-align: baseline diff --git a/ui/components/emoji/emojify.page.js b/ui/components/emoji/emojify.page.js deleted file mode 100644 index 5187acef0..000000000 --- a/ui/components/emoji/emojify.page.js +++ /dev/null @@ -1,21 +0,0 @@ -import emojify from 'emojify.js'; - -import { AutoloadPage } from 'vj/misc/PageLoader'; - -function runEmojify($container) { - if ($container.is('[data-emoji-enabled]')) { - emojify.run($container[0]); - return; - } - $container.find('[data-emoji-enabled]').get().forEach((element) => emojify.run(element)); -} - -const emojifyPage = new AutoloadPage('emojifyPage', () => { - emojify.setConfig({ - img_dir: `${UiContext.cdn_prefix}img/emoji`, - }); - runEmojify($('body')); - $(document).on('vjContentNew', (e) => runEmojify($(e.target))); -}); - -export default emojifyPage; diff --git a/ui/components/footer/footer.page.js b/ui/components/footer/footer.page.js deleted file mode 100644 index 0019581d8..000000000 --- a/ui/components/footer/footer.page.js +++ /dev/null @@ -1,31 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; -import { isBelow } from 'vj/utils/mediaQuery'; -import { slideUp, slideDown } from 'vj/utils/slide'; -import responsiveCutoff from 'vj/breakpoints.json'; - -const footerPage = new AutoloadPage('footerPage', () => { - if ($('.footer').length === 0) { - return; - } - $('.footer__category.expandable > h1').click(async (ev) => { - if (!isBelow(responsiveCutoff.mobile)) { - return; - } - const $category = $(ev.currentTarget).closest('.footer__category'); - const $list = $category.find('.footer__category__expander'); - if ($category.hasClass('animating')) { - return; - } - $category.addClass('animating'); - if ($category.hasClass('expanded')) { - $category.removeClass('expanded'); - await slideUp($list, 300, { opacity: 1 }, { opacity: 0 }); - } else { - $category.addClass('expanded'); - await slideDown($list, 300, { opacity: 0 }, { opacity: 1 }); - } - $category.removeClass('animating'); - }); -}); - -export default footerPage; diff --git a/ui/components/footer/footer.page.styl b/ui/components/footer/footer.page.styl deleted file mode 100644 index 517bf4b9b..000000000 --- a/ui/components/footer/footer.page.styl +++ /dev/null @@ -1,94 +0,0 @@ -$footer-extra-link-gap = 20px - -.footer - background: $footer-bg-color - color: $text-1-color - - h1 - font-size: rem($font-size-title) - - +mobile() - padding: rem($grid-padding) - cursor: pointer - font-size: rem($font-size-secondary) - -.footer__category - padding-top: rem(30px) - padding-bottom: rem(50px) - - +mobile() - padding-top: rem($grid-padding) - padding-bottom: rem($grid-padding) - border-bottom: 1px solid darken($footer-bg-color, 5%) - - &.expandable - padding: 0 - - .expand-icon - display: none - - +mobile() - display: inline-block - - &.expanded .expand-icon - transform: rotate(180deg) - -.footer__category__list - margin-top: rem(10px) - margin-bottom: rem(10px) - font-size: rem($font-size-small) - line-height: 2 - - +mobile() - margin-top: 0 - padding-left: rem($grid-padding) - padding-right: rem($grid-padding) - -.footer__category__expander - +mobile() - display: none - - +above(rupture.mobile-cutoff) - display: block !important - -.footer__extra-left - float: left - -.footer__extra-right - float: right - -.footer__extra-link - padding: rem(20px 0) - font-size: rem($font-size-small) - -.footer__extra-link-item - float: left - line-height: 2 - -.footer__extra-left .footer__extra-link-item - margin-right: rem($footer-extra-link-gap) - -.footer__extra-right .footer__extra-link-item - margin-left: rem($footer-extra-link-gap) - -.layout--immersive - .footer - background: none - color: #FFF - - a, a:visited, a:active - color: #FFF - - a:hover - color: #FFF - -+mobile() - .footer__extra-left, .footer__extra-right - float: none - display: block - - .footer__extra-link-item - float: none - display: inline-block - margin-left: 0 - margin-right: $footer-extra-link-gap diff --git a/ui/components/form/button.page.styl b/ui/components/form/button.page.styl deleted file mode 100644 index fc4064cd0..000000000 --- a/ui/components/form/button.page.styl +++ /dev/null @@ -1,82 +0,0 @@ -@import './var.inc.styl' - -.button - font-size: rem($font-size-secondary) - line-height: rem($form-control-height - 2) - background-color: lighten($default-color, 50%) - border: 1px solid $default-color - height: rem($form-control-height) - padding: rem(0 25px) - display: inline-block - outline: 0 - margin: 0 0 1rem - transition: background-color .2s, box-shadow .5s - transition-timing-function: ease-out-sine - cursor: pointer - user-select: none - - &, &:visited, &:hover - &, .typo & - color: $text-1-color - - &:hover, &:focus - text-decoration: none - - &:not(.disabled) - &:hover, &:focus - background-color: darken($default-color, 5%) - border-color: darken($default-color, 5%) - box-shadow: 0 4px 15px rgba(darken($default-color, 10%), 0.5) - - &:focus - border-color: $input-focus-color - - &.expanded - padding-left: 0 - padding-right: 0 - text-align: center - width: 100% - - &.rounded - border-radius: rem($form-control-height / 2) - - &.primary - border-color: $primary-color - background-color: $primary-color - - &, &:visited, &:hover - &, .typo & - color: #FFF - - &:not(.disabled) - &:hover - background-color: lighten(saturate($primary-color, 30%), 10%) - border-color: saturate($primary-color, 70%) - box-shadow: 0 4px 15px rgba(saturate($primary-color, 70%), 0.3) - - &.disabled - cursor: default - opacity: 0.5 - - &.compact - margin-bottom: 0 - height: rem($compact-control-height) - line-height: rem($compact-control-height - 2) - padding: rem(0 15px) - -.button.inverse - line-height: rem($form-control-height - 4) - border: 2px solid #FFF - background: none - - &, &:visited - &, .typo & - color: #FFF - - &:not(.disabled) - &:hover, &:focus - border-color: $immersive-primary-color - background: none - box-shadow: none - &, .typo & - color: $immersive-primary-color diff --git a/ui/components/form/checkbox.page.styl b/ui/components/form/checkbox.page.styl deleted file mode 100644 index 889e85591..000000000 --- a/ui/components/form/checkbox.page.styl +++ /dev/null @@ -1,90 +0,0 @@ -@import './var.inc.styl' - -$checkbox-size = 16px - -.checkbox - font-size: rem($font-size-secondary) - line-height: 1.4 - border: 0 - display: inline-block - vertical-align: middle - cursor: pointer - user-select: none - color: lighten($text-1-color, 30%) - margin: $input-margin - padding: ((rem($form-control-height) - (@line-height * @font-size)) / 2) 0 - - &:hover - color: $text-1-color - - input - appearance: none - border: 1px solid #D0D0D0 - display: inline-block - border-radius: 1px - vertical-align: top - width: rem($checkbox-size) - height: rem($checkbox-size) - margin: ((@line-height * @font-size - rem($checkbox-size)) / 2) 0 - margin-right: rem(5px) - position: relative - outline: 0 - transition: background .1s linear - background: none - cursor: pointer - - &:after - content: '' - width: rem(10px) - height: rem(6px) - position: absolute - top: rem(3px) - left: rem(2px) - border: 2px solid #FFF - border-top: none - border-right: none - background: transparent - transform: rotate(-65deg) scale(0.5) - opacity: 0 - - &:enabled:hover - border-color: rgba($secondary-color, 0.7) - - &:checked - background: $secondary-color - border-color: $secondary-color - - &:checked:after - opacity: 1 - transform: rotate(-45deg) - - &:disabled - opacity: 0.5 - - &.compact - margin-bottom: 0 - line-height: 1.2 - padding: ((rem($compact-control-height) - (@line-height * @font-size)) / 2) 0 - - input - margin: ((@line-height * @font-size - rem($checkbox-size)) / 2) 0 - margin-right: rem(5px) - -label.checkbox.inverse - color: rgba($immersive-text-color, 0.8) - - input - color: $immersive-text-color - border-color: rgba($immersive-text-color, 0.4) - - &:disabled, &:read-only - color: rgba($immersive-text-color, 0.6) - - &:enabled:read-write:hover - border-color: rgba($immersive-text-color, 0.8) - - &:enabled:read-write:focus - border-color: $immersive-primary-color - - &.focus - color: $immersive-primary-color diff --git a/ui/components/form/form.page.js b/ui/components/form/form.page.js deleted file mode 100644 index 1e78cf45b..000000000 --- a/ui/components/form/form.page.js +++ /dev/null @@ -1,15 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -const formPage = new AutoloadPage('formPage', () => { - $(document).on('vjFormDisableUpdate', 'input, select, textarea', (ev) => { - const $input = $(ev.currentTarget); - const $formItem = $input.closest('.form__item'); - if ($input.prop('disabled')) { - $formItem.addClass('is--disabled'); - } else { - $formItem.removeClass('is--disabled'); - } - }); -}); - -export default formPage; diff --git a/ui/components/form/form.page.styl b/ui/components/form/form.page.styl deleted file mode 100644 index 8ab99bd06..000000000 --- a/ui/components/form/form.page.styl +++ /dev/null @@ -1,199 +0,0 @@ -// Extracted from Foundation framework - -/** - * Known limitation: by default, Chrome and Safari on OS X allow very limited - * styling of `select`, unless a `border` property is set. - */ -/** - * 1. Correct color not being inherited. - * Known issue: affects color of disabled elements. - * 2. Correct font properties not being inherited. - * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. - */ -button, -input, -optgroup, -select, -textarea - color: inherit - /* 1 */ - font: inherit - /* 2 */ - margin: 0 - /* 3 */ - -/** - * Address `overflow` set to `hidden` in IE 8/9/10/11. - */ -button - overflow: visible - -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. - * Correct `select` style inheritance in Firefox. - */ -button, -select - text-transform: none - -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] - appearance: button - /* 2 */ - cursor: pointer - /* 3 */ - -/** - * Re-set default cursor for disabled elements. - */ -button[disabled], -html input[disabled] - cursor: not-allowed - -/** - * Remove inner padding and border in Firefox 4+. - */ -button::-moz-focus-inner, -input::-moz-focus-inner - border: 0 - padding: 0 - -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ -input - line-height: normal - -/** - * It's recommended that you don't attempt to style these elements. - * Firefox's implementation doesn't respect box-sizing, padding, or width. - * - * 1. Address box sizing set to `content-box` in IE 8/9/10. - * 2. Remove excess padding in IE 8/9/10. - */ -input[type="checkbox"], -input[type="radio"] - box-sizing: border-box - /* 1 */ - padding: 0 - /* 2 */ - -/** - * Fix the cursor style for Chrome's increment/decrement buttons. For certain - * `font-size` values of the `input`, it causes the cursor style of the - * decrement button to change from `default` to `text`. - */ -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button - height: auto - -/** - * 1. Address `appearance` set to `searchfield` in Safari and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. - */ -input[type="search"] - appearance: textfield - /* 1 */ - box-sizing: content-box - /* 2 */ - -/** - * Remove inner padding and search cancel button in Safari and Chrome on OS X. - * Safari (but not Chrome) clips the cancel button when the search input has - * padding (and `textfield` appearance). - */ -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration - -webkit-appearance: none - -/** - * Define consistent border, margin, and padding. - * [NOTE] We don't enable this ruleset in Foundation, because we want the
    element to have plain styling. - */ -/* fieldset - border: 1px solid #c0c0c0 - margin: 0 2px - padding: 0.35em 0.625em 0.75em - */ -/** - * 1. Correct `color` not being inherited in IE 8/9/10/11. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ -legend - border: 0 - /* 1 */ - padding: 0 - /* 2 */ - -/** - * Remove default vertical scrollbar in IE 8/9/10/11. - */ -textarea - overflow: auto - -/** - * Don't inherit the `font-weight` (applied by a rule above). - * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. - */ -optgroup - font-weight: bold - -.form--inline - display: inline - -textarea - height: auto - border-radius: 0px - resize: vertical - -select - width: 100% - border-radius: 0px - -button - appearance: none - background: transparent - padding: 0 - border: 0 - border-radius: 0px - line-height: 1 - -[data-whatinput='mouse'] button - outline: 0 - -label - display: block - margin: 0 - font-size: rem($font-size-secondary) - line-height: 1.8 - color: $label-color - - input, textarea, select - display: block - -.help-text - margin-top: -0.5rem - margin-bottom: 1rem - font-size: rem($font-size-small) - color: $help-text-color - -.form__item - transition: opacity .1s linear - - &.is--disabled - opacity: 0.3 - - :disabled - opacity: 1 diff --git a/ui/components/form/multiSelectCheckbox.page.js b/ui/components/form/multiSelectCheckbox.page.js deleted file mode 100644 index e1128370d..000000000 --- a/ui/components/form/multiSelectCheckbox.page.js +++ /dev/null @@ -1,45 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -const lastSelectionByGroup = {}; - -function isInGroup(group) { - return (idx, element) => element.getAttribute('data-checkbox-group') === group; -} - -function onRangeRoleCheckboxClick(ev) { - const $current = $(ev.currentTarget); - const targetGroup = $current.attr('data-checkbox-group'); - const $targets = $('[type="checkbox"]') - .filter(isInGroup(targetGroup)) - .filter(':visible') - .filter(':not(:disabled)'); - if (ev.shiftKey && lastSelectionByGroup[targetGroup]) { - const destCheck = lastSelectionByGroup[targetGroup].prop('checked'); - const from = $targets.index(lastSelectionByGroup[targetGroup]); - const to = $targets.index($current); - const start = Math.min(from, to); - const end = Math.max(from, to) + 1; - $targets - .slice(start, end) - .prop('checked', destCheck); - } - lastSelectionByGroup[targetGroup] = $current; -} - -function onToggleRoleCheckboxClick(ev) { - const $current = $(ev.currentTarget); - const targetGroup = $current.attr('data-checkbox-toggle'); - const $targets = $('[type="checkbox"]') - .filter(isInGroup(targetGroup)) - .filter(':visible') - .filter(':not(:disabled)'); - const destCheck = $current.prop('checked'); - $targets.prop('checked', destCheck); -} - -const multiSelectCheckboxPage = new AutoloadPage('multiSelectCheckboxPage', () => { - $(document).on('click', '[data-checkbox-range]', onRangeRoleCheckboxClick); - $(document).on('click', '[data-checkbox-toggle]', onToggleRoleCheckboxClick); -}); - -export default multiSelectCheckboxPage; diff --git a/ui/components/form/radiobox.page.styl b/ui/components/form/radiobox.page.styl deleted file mode 100644 index e4a9ff6ff..000000000 --- a/ui/components/form/radiobox.page.styl +++ /dev/null @@ -1,62 +0,0 @@ -@import './var.inc.styl' - -$radiobox-size = 16px - -.radiobox - display: block - font-size: rem($font-size-secondary) - line-height: 1.4 - border: 0 - cursor: pointer - user-select: none - color: lighten($text-1-color, 30%) - margin: $input-multi-line-margin - - &:hover - color: $text-1-color - - input - display: inline-block - -.radiobox-container - margin: $input-margin - -// Image Radio Box - -.radiobox.with-image - margin: $input-image-radio-margin - - .radiobox__image-container - cursor: pointer - border: 2px solid #DDD - transition: border-color .1s linear, opacity .1s linear - padding: 1px - overflow: hidden - position: relative - opacity: 0.7 - - &:hover - border: 2px solid $primary-color - opacity: 1 - - &:after - position: absolute - width: rem(20px) - height: rem(20px) - right: rem(-10px) - top: rem(-10px) - transform: translateX(10px) translateY(-10px) rotate(45deg) - background: $highlight-color - content: '' - transition: transform .1s ease-out-cubic - - input - visibility: hidden - position: absolute - - &:checked + .radiobox__image-container - border: 2px solid $highlight-color - opacity: 1 - - &:after - transform: rotate(45deg) diff --git a/ui/components/form/select.page.styl b/ui/components/form/select.page.styl deleted file mode 100644 index 125cdf03f..000000000 --- a/ui/components/form/select.page.styl +++ /dev/null @@ -1,29 +0,0 @@ -@import './var.inc.styl' - -.select - form-styles() - padding-right: 1.1rem - background-color: $input-background-color - background-image: url('~vj/misc/icons/' + $icon-expand_more-file) - background-size: 16px 16px - background-position: right -1rem center - background-origin: content-box - background-repeat: no-repeat - outline: $input-outline - transition: outline-color .2s, border-color .2s - transition-timing-function: ease-out-cubic - - &:focus - border-color: $input-focus-border-color - outline: $input-focus-outline - outline-offset: 0 - - &:disabled - opacity: 0.5 - -.select-container.compact .select, .select.compact - margin-bottom: 0 - height: rem($compact-control-height) - line-height: rem($compact-control-height - 2) - padding-top: 0 - padding-bottom: 0 diff --git a/ui/components/form/textbox.page.js b/ui/components/form/textbox.page.js deleted file mode 100644 index 83207946a..000000000 --- a/ui/components/form/textbox.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -const textboxPage = new AutoloadPage('textboxPage', () => { - $(document).on('focusin', '.textbox.material input', (ev) => { - $(ev.currentTarget).parent().addClass('focus'); - }); - - $(document).on('focusout', '.textbox.material input', (ev) => { - $(ev.currentTarget).parent().removeClass('focus'); - }); - - const $focusElement = $(document.activeElement); - if ($focusElement.prop('tagName') === 'INPUT' - && $focusElement.parent().is('.textbox.material') - ) { - $focusElement.focusin(); - } -}); - -export default textboxPage; diff --git a/ui/components/form/textbox.page.styl b/ui/components/form/textbox.page.styl deleted file mode 100644 index feda6bc00..000000000 --- a/ui/components/form/textbox.page.styl +++ /dev/null @@ -1,72 +0,0 @@ -@import './var.inc.styl' - -// Normal Textbox -input.textbox, textarea.textbox - form-styles() - background-color: $input-background-color - outline: $input-outline - transition: outline-color .2s, border-color .2s - transition-timing-function: ease-out-cubic - - &:focus - border-color: $input-focus-border-color - outline: $input-focus-outline - outline-offset: 0 - - // Overwrite, for IE compatibility - &:read-only - outline: 0 - - &:disabled - opacity: 0.5 - -textarea.textbox - height: rem(200px) - -.data-table - input.textbox, textarea.textbox - margin-bottom: 0 - -// Material Textbox -label.textbox.material - input - form-styles() - border: 0 - background: none - outline: 0 - padding-left: 0 - padding-right: 0 - color: $text-1-color - border-bottom: 2px solid rgba($text-1-color, 0.4) - transition: border-color .2s ease-out-cubic - - &:disabled, &:read-only - color: rgba($text-1-color, 0.6) - - &:enabled:read-write:hover - border-color: rgba($text-1-color, 0.8) - - &:enabled:read-write:focus - border-color: $input-focus-border-color - - &.focus - color: $input-focus-border-color - -label.textbox.material.inverse - color: rgba($immersive-text-color, 0.8) - - input - color: $immersive-text-color - border-color: rgba($immersive-text-color, 0.4) - - &:disabled, &:read-only - color: rgba($immersive-text-color, 0.6) - - &:enabled:read-write:hover - border-color: rgba($immersive-text-color, 0.8) - - &:enabled:read-write:focus - border-color: $immersive-primary-color - - &.focus - color: $immersive-primary-color diff --git a/ui/components/form/var.inc.styl b/ui/components/form/var.inc.styl deleted file mode 100644 index 65117e174..000000000 --- a/ui/components/form/var.inc.styl +++ /dev/null @@ -1,14 +0,0 @@ -form-styles() - appearance: none - display: block - width: 100% - font-size: rem($font-size-secondary) - margin: $input-margin - height: rem($form-control-height) - line-height: 1.2 - padding: rem(5px) - border: $input-border - - &.inline - display: inline-block - width: auto diff --git a/ui/components/header/header-background.png b/ui/components/header/header-background.png deleted file mode 100644 index ae4c578af..000000000 Binary files a/ui/components/header/header-background.png and /dev/null differ diff --git a/ui/components/header/header-background@2x.png b/ui/components/header/header-background@2x.png deleted file mode 100644 index 8d6d2692e..000000000 Binary files a/ui/components/header/header-background@2x.png and /dev/null differ diff --git a/ui/components/header/header-logo-summer.png b/ui/components/header/header-logo-summer.png deleted file mode 100644 index eaf4fda4c..000000000 Binary files a/ui/components/header/header-logo-summer.png and /dev/null differ diff --git a/ui/components/header/header-logo-summer@2x.png b/ui/components/header/header-logo-summer@2x.png deleted file mode 100644 index e4ab84b54..000000000 Binary files a/ui/components/header/header-logo-summer@2x.png and /dev/null differ diff --git a/ui/components/header/header-logo.png b/ui/components/header/header-logo.png deleted file mode 100644 index 13dab1386..000000000 Binary files a/ui/components/header/header-logo.png and /dev/null differ diff --git a/ui/components/header/header-logo@2x.png b/ui/components/header/header-logo@2x.png deleted file mode 100755 index 9e032946c..000000000 Binary files a/ui/components/header/header-logo@2x.png and /dev/null differ diff --git a/ui/components/header/header.page.styl b/ui/components/header/header.page.styl deleted file mode 100644 index c28d262f6..000000000 --- a/ui/components/header/header.page.styl +++ /dev/null @@ -1,176 +0,0 @@ -$header-gap-mini = ($header-bg-height-mini - $nav-item-height - $header-logo-height) / 2 -$header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-logo-domain-lh - $header-logo-system-lh) / 2 - -.global-notification - padding: rem(20px 0) - background: #FFF - - &.warning - background: #ffe1a6 - -.header__options - background-image: url(./header-background.png) - background-image: url(./header-background@2x.png) - -.header - position: relative - height: rem($header-bg-height) - background: #5eafca no-repeat center top - background-size: cover - transition: height .5s - transition-timing-function: ease-out-cubic - overflow: hidden - +retina() - background-size: rem($header-bg-width $header-bg-height) - - &.mini - height: rem($header-bg-height-mini) - - +mobile() - background: #FFF - height: auto !important - .header__content - display: none - -.header__logo__options - background-image: url(./header-logo.png) - background-image: url(./header-logo@2x.png) - background-image: url(./header-logo-summer.png) - background-image: url(./header-logo-summer@2x.png) - -.header__logo - display: block - width: rem($header-logo-width-real) - height: rem($header-logo-height + $header-gap-mini + $nav-item-height) - background: no-repeat - background-size: rem($header-logo-width-real) rem($header-logo-height-real) - background-position: 0 rem($header-gap-mini + $nav-item-height - ($header-logo-height-real - $header-logo-height)) - transition: transform .5s, opacity .5s - transition-timing-function: ease-out-cubic - transform-origin: 0 0 - opacity: 1 - +mobile() - display: inline-block - -.header__logo--domain - font-size: rem(40px) - margin-top: rem($header-gap-mini-domain + $nav-item-height) - height: rem($header-logo-domain-lh) - line-height: rem($header-logo-domain-lh) - - a - text-shadow: 1px 1px 5px rgba(#000, 0.4) - - &, &:hover, &:visited, &:link - color: #FFF - text-decoration: none - -.header__logo--system - line-height: rem($header-logo-system-lh) - color: #FFF - font-size: rem($font-size-small) - - a - opacity: 0.8 - - &, &:hover, &:visited, &:link - text-decoration: none - color: #FFF - - &:hover - opacity: 1 - text-decoration: underline - -.header--collapsed - - #panel - padding-top: $header-bg-height - - .header - position: absolute - left: 0 - top: 0 - width: 100% - height: 0 - - .header__logo - transform: scale(0) - opacity: 0 - -.location - position: absolute - left: 0 - bottom: 0 - width: 100% - height: rem($header-layer-height) - background: rgba(#000, 0.5) - - +mobile() - position: relative - height: auto - - .header__hamburger - display: none - - +mobile() - display: block - -.header.mini .location - display: none - -.location-path - color: rgba(#FFF, 0.3) - line-height: rem(30px) - margin-top: rem(10px) - a, a:visited - color: rgba(#FFF, 0.6) - a:hover - color: rgba(#FFF, 0.9) - -.location-current - color: #9ad4f1 - line-height: rem(40px) - white-space: nowrap - overflow: hidden - text-overflow: ellipsis - - +mobile() - color: #91fcff - font-size: 1.7rem - white-space: normal - -.header--mobile - display: none - -.header--mobile, .location - +mobile() - display: block - background: #56758f - color: #FFF - line-height: rem($nav-item-height) - font-size: rem($font-size-title) - padding: rem(10px 0) - - a, a:visited, a:hover - color: #FFF - -.header--mobile__domain.is--system - display: block - width: $nav-logo-width // no rem - height: $nav-logo-height // no rem - margin-top: rem(10px) - +retina() - background-size: $nav-logo-width $nav-logo-height // no rem - -.header--mobile__system - margin-left: rem(10px) - font-size: rem($font-size-secondary) - - &, &:hover, &:visited - color: $supplementary-color - -.header__hamburger - background: none - border: 0 - border-radius: 0 - outline: 0 diff --git a/ui/components/highlighter/codemirror.page.styl b/ui/components/highlighter/codemirror.page.styl deleted file mode 100644 index c18494ba8..000000000 --- a/ui/components/highlighter/codemirror.page.styl +++ /dev/null @@ -1,12 +0,0 @@ -@css { -span.cm-comment { color: #008000; } -span.cm-keyword, span.cm-variable-3 { line-height: 1em; color: #00f; } -span.cm-string { color: #a31515; } -span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; } -span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; } -span.cm-variable { color: black; } -span.cm-meta { color: #2b91af; } -span.cm-link { color: #3a3; } -.CodeMirror-activeline-background { background: #e8f2ff; } -.CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; } -} diff --git a/ui/components/highlighter/highlighter.page.js b/ui/components/highlighter/highlighter.page.js deleted file mode 100644 index 43b693e1d..000000000 --- a/ui/components/highlighter/highlighter.page.js +++ /dev/null @@ -1,14 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -const highlighterPage = new AutoloadPage('highlighterPage', () => { - import('./prismjs').then((module) => { - const prismjs = module.default; - function runHighlight($container) { - prismjs.highlightBlocks($container); - } - runHighlight($('body')); - $(document).on('vjContentNew', (e) => runHighlight($(e.target))); - }); -}); - -export default highlighterPage; diff --git a/ui/components/highlighter/highlighter.page.styl b/ui/components/highlighter/highlighter.page.styl deleted file mode 100644 index b083d40ba..000000000 --- a/ui/components/highlighter/highlighter.page.styl +++ /dev/null @@ -1,122 +0,0 @@ -/* - -VS theme by Andrew Lock - -https://github.com/PrismJS/prism-themes/blob/master/themes/prism-vs.css - -*/ - -pre, code - background: $syntax-hl-bg-color - -pre > code - background: none - line-height: 1.5 - -pre - position: relative - padding: rem(5px) - -code - color: $code-color - font-size: rem($font-size-secondary) - -pre > code - color: black - text-align: left - white-space: pre-wrap - word-spacing: normal - word-break: normal - tab-size: 4 - hyphens: none - -[data-syntax-hl-show-line-number] pre.line-numbers > code - white-space: pre - -.syntax-hl - .token - &.comment, &.prolog, &.doctype, &.cdata - color: #008000 - font-style: italic - &.namespace - opacity: 0.7 - &.string - color: #a31515 - &.punctuation, &.operator - color: #393A34 - &.url, &.symbol, &.number, &.boolean, &.inserted - color: #36acaa - &.atrule, &.keyword, &.attr-value, .language-autohotkey&.selector, .language-json&.boolean, .language-json&.number - color: #0000ff - &.function - color: #393A34 - &.deleted, .language-autohotkey&.tag - color: #9a050f - &.selector, .language-autohotkey&.keyword - color: #00009f - &.important, &.bold - font-weight: bold - &.italic - font-style: italic - &.class-name, .language-json&.property - color: #2B91AF - &.tag, &.selector - color: #800000 - &.attr-name, &.property, &.regex, &.entity - color: #ff0000 - &.directive.tag .tag - background: #ffff00 - color: #393A34 - &.macro - &, .token - color: #2b91af - &.builtin - color: #077 - - .toolbar - position: absolute - white-space: no-wrap - right: 0 - top: 0 - padding: rem(5px) - background: rgba($syntax-hl-bg-color, 0.8) - font-family: $primary-font-family - font-size: rem($font-size-small) - opacity: 0 - pointer-events: none - transition: opacity .1s linear - user-select: none - - &:hover .toolbar - opacity: 1 - pointer-events: all - -[data-syntax-hl-show-line-number] - pre - padding-left: 3.8em - counter-reset: linenumber - - pre > code - position: relative - - .line-numbers-rows - position: absolute - pointer-events: none - top: 0 - font-size: 100% - left: -3.8em - width: 3em - letter-spacing: -1px - user-select: none - - span - pointer-events: none - display: block - counter-increment: linenumber - - &:before - content: counter(linenumber) - color: #ddd - display: block - padding-right: 0.8em - text-align: right diff --git a/ui/components/highlighter/meta.js b/ui/components/highlighter/meta.js deleted file mode 100644 index 3939c94a8..000000000 --- a/ui/components/highlighter/meta.js +++ /dev/null @@ -1,15 +0,0 @@ -// The following content is extracted from: -// https://github.com/codemirror/CodeMirror/blob/master/mode/meta.js - -export default [ - { name: 'C', ext: ['c', 'h'] }, - { name: 'C++', ext: ['cpp', 'c++', 'cc', 'cxx', 'hpp', 'h++', 'hh', 'hxx'] }, - { name: 'Go', ext: ['go'] }, - { name: 'Haskell', ext: ['hs'] }, - { name: 'Java', ext: ['java'] }, - { name: 'JavaScript', ext: ['js'] }, - { name: 'Pascal', ext: ['p', 'pas'] }, - { name: 'PHP', ext: ['php', 'php3', 'php4', 'php5', 'php7', 'phtml'] }, - { name: 'Python', ext: ['BUILD', 'bzl', 'py', 'pyw'] }, - { name: 'Rust', ext: ['rs'] }, -]; diff --git a/ui/components/highlighter/prismjs.js b/ui/components/highlighter/prismjs.js deleted file mode 100644 index da1d0e3a3..000000000 --- a/ui/components/highlighter/prismjs.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - -To add a new language to highlight: -1. Add language in babel in package.json -2. Add new import statement in `components/cmeditor/vjcmeditor.js` -3. Add new import statement in `components/scratchpad/ScratchpadEditorContainer.js` -4. Add new meta data in `components/highlighter/meta.js` - - */ - -import Prism from 'prismjs'; - -import Clipboard from 'clipboard'; -import Notification from 'vj/components/notification'; -import i18n from 'vj/utils/i18n'; - -import languageMeta from './meta'; - -const languageExtMap = {}; - -// Map possible language names to Prism language name -languageMeta.forEach((meta) => { - for (let i = 0; i < meta.ext.length; ++i) { - if (Prism.languages[meta.ext[i]] !== undefined) { - // eslint-disable-next-line no-param-reassign - meta.target = meta.ext[i]; - break; - } - } - meta.ext.forEach((ext) => { - languageExtMap[ext] = meta.target; - }); -}); - -// Copy to Clipboard -Prism.plugins.toolbar.registerButton('copy-to-clipboard', (env) => { - const linkCopy = document.createElement('a'); - linkCopy.href = 'javascript:;'; // eslint-disable-line no-script-url - linkCopy.textContent = 'Copy'; - const clip = new Clipboard(linkCopy, { text: () => env.code }); - clip.on('success', () => { - Notification.success(i18n('Code copied to clipboard!'), 1000); - }); - clip.on('error', () => { - Notification.error(i18n('Copy failed :(')); - }); - return linkCopy; -}); - -const prismjsApiWrap = { - highlightBlocks: ($dom) => { - $dom.find('pre code').get().forEach((code) => { - const $pre = $(code).parent(); - $pre.addClass('syntax-hl'); - if ($pre.closest('[data-syntax-hl-show-line-number]')) { - $pre.addClass('line-numbers'); - } - // try to map the language name - const language = $(code).attr('class'); - const m = (language || '').trim().match(/^language-(.+)$/); - if (m && m[1]) { - const languageName = m[1].toLowerCase(); - if (languageExtMap[languageName]) { - $(code).attr('class', `language-${languageExtMap[languageName]}`); - } - } - Prism.highlightElement(code); - }); - }, -}; - -export default prismjsApiWrap; diff --git a/ui/components/hotkey/hotkey.page.js b/ui/components/hotkey/hotkey.page.js deleted file mode 100644 index ce99a2cb2..000000000 --- a/ui/components/hotkey/hotkey.page.js +++ /dev/null @@ -1,80 +0,0 @@ -import _ from 'lodash'; -import { AutoloadPage } from 'vj/misc/PageLoader'; - -const KEY_MAP = { - 10: 'enter', - 13: 'enter', - 27: 'esc', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 112: 'f1', - 113: 'f2', - 114: 'f3', - 115: 'f4', - 116: 'f5', - 117: 'f6', - 118: 'f7', - 119: 'f8', - 120: 'f9', - 121: 'f10', - 122: 'f11', - 123: 'f12', -}; - -function isHotkeyMatch(sortedHotkeyArr, hotkeyStr) { - const hotkeyDefined = hotkeyStr.toLowerCase().split('+'); - return _.isEqual(sortedHotkeyArr, hotkeyDefined.sort()); -} - -function testElementHotkey(hotkey, $element, attr) { - if (!$element.is(':visible')) { - return; - } - String($element.attr(attr)) - .split(',') - .forEach((singleDef) => { - const [defStr, trigger] = singleDef.split(':'); - if (isHotkeyMatch(hotkey, defStr)) { - switch (trigger) { - case 'submit': - $element.closest('form').trigger('submit'); - break; - case undefined: - $element.trigger('click'); - break; - default: - $element.trigger(trigger); - break; - } - } - }); -} - -const hotkeyPage = new AutoloadPage('hotkeyPage', () => { - $(document).on('keydown', (ev) => { - const hotkey = ['alt', 'ctrl', 'shift'].filter((modifyKey) => ev[`${modifyKey}Key`]); - if (ev.metaKey && !ev.ctrlKey) { - hotkey.push('ctrl'); - } - if (KEY_MAP[ev.which] !== undefined) { - hotkey.push(KEY_MAP[ev.which]); - } else { - hotkey.push(String.fromCharCode(ev.which).toLowerCase()); - } - hotkey.sort(); - - // Find all global hotkeys - $('[data-global-hotkey]').get().forEach((element) => { - testElementHotkey(hotkey, $(element), 'data-global-hotkey'); - }); - - // Find all local hotkeys - $(ev.target).parents('[data-hotkey]').get().forEach((element) => { - testElementHotkey(hotkey, $(element), 'data-hotkey'); - }); - }); -}); - -export default hotkeyPage; diff --git a/ui/components/katex/katex.page.js b/ui/components/katex/katex.page.js deleted file mode 100644 index c55a84b5f..000000000 --- a/ui/components/katex/katex.page.js +++ /dev/null @@ -1,16 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -import 'katex/dist/katex.min.css'; -import './katex.styl'; - -const katexPage = new AutoloadPage('katexPage', () => { - import('katex/dist/contrib/auto-render.min.js').then(({ default: katex }) => { - function runKatex($containers) { - $containers.get().forEach((container) => katex(container)); - } - runKatex($('.typo')); - $(document).on('vjContentNew', (e) => runKatex($(e.target).find('.typo'))); - }); -}); - -export default katexPage; diff --git a/ui/components/katex/katex.styl b/ui/components/katex/katex.styl deleted file mode 100644 index 9d6fb1608..000000000 --- a/ui/components/katex/katex.styl +++ /dev/null @@ -1,2 +0,0 @@ -.katex - font-size: 1.2em diff --git a/ui/components/loader/loader.page.styl b/ui/components/loader/loader.page.styl deleted file mode 100644 index a243a9239..000000000 --- a/ui/components/loader/loader.page.styl +++ /dev/null @@ -1,29 +0,0 @@ -.loader-container - position: absolute - left: 0 - width: 100% - top: 0 - height: 100% - -.loader - text-indent: -9999em - border: rem(5px) solid rgba($primary-color, .2) - border-left: rem(5px) solid $primary-color - animation: load8 1.1s infinite linear - border-radius: 50% - width: rem(50px) - height: rem(50px) - margin-left: rem(-25px) - margin-top: rem(-25px) - left: 50% - top: 50% - position: absolute - -@keyframes load8 - 0% - transform: rotate(0deg) - - 100% - transform: rotate(360deg) - - diff --git a/ui/components/marker/Marker.js b/ui/components/marker/Marker.js deleted file mode 100644 index c701edbaa..000000000 --- a/ui/components/marker/Marker.js +++ /dev/null @@ -1,193 +0,0 @@ -import _ from 'lodash'; -import delay from 'vj/utils/delay'; -import Tooltip from 'vj/components/tooltip/Tooltip'; -import i18n from 'vj/utils/i18n'; - -const MARKER_ID = `marker_${Math.floor(Math.random() * 0xFFFFFF).toString(16)}`; -const MARKER_OFFSET = 20; -const MARKER_MAX_DISTANCE = 60; - -let markerInstance = null; - -function distanceToRect(px, py, rect) { - const cx = (rect.left + rect.right) / 2; - const cy = (rect.top + rect.bottom) / 2; - const dx = Math.max(Math.abs(px - cx) - (rect.right - rect.left) / 2, 0); - const dy = Math.max(Math.abs(py - cy) - (rect.bottom - rect.top) / 2, 0); - return Math.sqrt(dx * dx + dy * dy); -} - -class Marker { - static exists() { - return markerInstance && document.getElementById(MARKER_ID); - } - - constructor() { - if (Marker.exists()) { - return markerInstance; - } - if (markerInstance) { - markerInstance.destroy(); - } - this.$dom = $(` -
    -
    -
    -
    -
    - `) - .appendTo('body'); - this.$dom.find('.marker__toolbar').on('click', '.marker__action', this.onMarkerActionClick.bind(this)); - this.$dom.on('mousedown', this.onMarkerMouseDown.bind(this)); - this.isOpen = false; - this.bindedHandlers = false; - this._onKeyDown = this.onKeyDown.bind(this); - this._onScroll = _.throttle(this.onScroll.bind(this), 50); - this._onMouseDown = this.onMouseDown.bind(this); - this._onMouseMove = _.throttle(this.onMouseMove.bind(this), 50); - markerInstance = this; - } - - markSelection(color) { - const selection = window.getSelection(); - const range = selection.getRangeAt(0); - this.$container.attr('contentEditable', true); - if (range) { - // recover ranges - selection.removeAllRanges(); - selection.addRange(range); - } - if (!document.execCommand('HiliteColor', false, color)) { - document.execCommand('BackColor', false, color); - } - this.$container.removeAttr('contentEditable'); - if (range) { - // remove ranges after marking - selection.removeAllRanges(); - } - } - - showAtPosition($container, x, y) { - this.$container = $container; - this.$dom.css({ - display: 'block', - opacity: 0, - }); - const rect = this.$dom[0].getBoundingClientRect(); - this.$dom.css({ - left: x + MARKER_OFFSET, - top: y - (rect.bottom - rect.top) - MARKER_OFFSET, - }); - this.isOpen = true; - this.$dom.addClass('open'); - this.updateMarkerOpacity(x, y); - this.bindEventHandlersForClosing(); - } - - async close() { - if (!this.isOpen) { - return; - } - this.isOpen = false; - this.closeTooltipsImmediately(); - this.unbindEventHandlers(); - this.$dom - .css({ opacity: 0 }) - .removeClass('open'); - await delay(200); - if (!this.isOpen) { - this.$dom.hide(); - } - } - - closeTooltipsImmediately() { - const $elements = this.$dom.find('[data-tooltip]'); - $elements.get().forEach((element) => { - const $el = $(element); - const tooltipInstance = Tooltip.get($el); - if (tooltipInstance) { - tooltipInstance.detach(); - } - }); - } - - bindEventHandlersForClosing() { - if (this.bindedHandlers) { - return; - } - $(document).on('keydown', this._onKeyDown); - $(window).on('scroll', this._onScroll); - $(document).on('mousedown', this._onMouseDown); - $(document).on('mousemove', this._onMouseMove); - this.bindedHandlers = true; - } - - unbindEventHandlers() { - if (!this.bindedHandlers) { - return; - } - $(document).off('keydown', this._onKeyDown); - $(window).off('scroll', this._onScroll); - $(document).off('mousedown', this._onMouseDown); - $(document).off('mousemove', this._onMouseMove); - this.bindedHandlers = false; - } - - onMarkerActionClick(ev) { - const color = $(ev.currentTarget).attr('data-color'); - this.markSelection(color); - this.close(); - } - - onMarkerMouseDown(ev) { - ev.stopPropagation(); - ev.preventDefault(); - } - - onKeyDown() { - this.close(); - } - - onScroll() { - this.close(); - } - - onMouseMove(ev) { - this.updateMarkerOpacity(ev.clientX, ev.clientY); - } - - onMouseDown() { - this.close(); - } - - updateMarkerOpacity(x, y) { - const markerRect = this.$dom[0].getBoundingClientRect(); - const distance = distanceToRect(x, y, markerRect); - if (distance > MARKER_MAX_DISTANCE) { - this.close(); - return; - } - this.$dom.css('opacity', 1 - (distance / MARKER_MAX_DISTANCE)); - } - - destroy() { - this.close(); - this.$dom.remove(); - markerInstance = null; - } -} - -const MarkerInterface = { - close() { - if (!markerInstance) { - return; - } - markerInstance.close(); - }, - showAtPosition($container, x, y) { - const marker = new Marker(); - marker.showAtPosition($container, x, y); - }, -}; - -export default MarkerInterface; diff --git a/ui/components/marker/MarkerReactive.js b/ui/components/marker/MarkerReactive.js deleted file mode 100644 index 897c7cca5..000000000 --- a/ui/components/marker/MarkerReactive.js +++ /dev/null @@ -1,58 +0,0 @@ -import _ from 'lodash'; - -import delay from 'vj/utils/delay'; -import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import Marker from './Marker'; - -export default class MarkerReactive extends DOMAttachedObject { - static DOMAttachKey = 'vjMarkerReactiveInstance'; - - static initFromDOM($dom) { - return MarkerReactive.getOrConstruct($dom); - } - - static initAll() { - $('[data-marker-enabled]').get().forEach((dom) => MarkerReactive.initFromDOM($(dom))); - } - - constructor($target) { - super($target); - this._onMouseUp = this.onMouseUp.bind(this); - this.bindEventHandlers(); - } - - bindEventHandlers() { - this.$dom.on('mouseup', this._onMouseUp); - } - - unbindEventHandlers() { - this.$dom.off('mouseup', this._onMouseUp); - } - - async onMouseUp(ev) { - await delay(1); - if (!window.getSelection) { - return; - } - const selection = window.getSelection(); - if (!selection || selection.rangeCount === 0) { - return; - } - const range = selection.getRangeAt(0); - if (range.collapsed) { - return; - } - Marker.showAtPosition(this.$dom, ev.clientX, ev.clientY); - } - - detach() { - if (this.detached) { - return; - } - this.unbindEventHandlers(); - Marker.close(); - super.detach(); - } -} - -_.assign(MarkerReactive, DOMAttachedObject); diff --git a/ui/components/marker/marker.page.js b/ui/components/marker/marker.page.js deleted file mode 100644 index dab92ff96..000000000 --- a/ui/components/marker/marker.page.js +++ /dev/null @@ -1,8 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; -import MarkerReactive from './MarkerReactive'; - -const markerPage = new AutoloadPage('markerPage', () => { - MarkerReactive.initAll(); -}); - -export default markerPage; diff --git a/ui/components/marker/marker.page.styl b/ui/components/marker/marker.page.styl deleted file mode 100644 index 33fc3c3c1..000000000 --- a/ui/components/marker/marker.page.styl +++ /dev/null @@ -1,54 +0,0 @@ -[data-marker-enabled] - outline: 0 - -.marker - position: fixed - z-index: 100 - display: none - box-shadow: 0 2px 7px rgba(#000, 0.3) - background: #FFF - font-size: rem($font-size-small) - transition: transform .1s ease-out, opacity .1s ease-out - transform: scale(0.8) - transform-origin: 0 100% - pointer-events: none - background: #FFF - padding: 1px - border: 1px solid #AAA - - &.open - transform: none - pointer-events: auto - - &__toolbar - padding: 1px - background: $toolbar-bg - white-space: nowrap - font-size: 0 - - &__action - display: inline-block - text-align: center - font-size: rem($font-size-icon) - vertical-align: top - cursor: pointer - padding: rem(8px) - color: $toolbar-fore - position: relative - - &:hover - background: $toolbar-bg-hover - color: $toolbar-fore-hover - - &__icon - display: block - margin: rem(2px) - width: rem(12px) - height: rem(12px) - border-radius: 50% - - &.icon-yellow - background: #ffc100 - - &.icon-green - background: #54d651 diff --git a/ui/components/menu/menu-heading.page.js b/ui/components/menu/menu-heading.page.js deleted file mode 100644 index 03384ca1f..000000000 --- a/ui/components/menu/menu-heading.page.js +++ /dev/null @@ -1,30 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; - -import tpl from 'vj/utils/tpl'; - -const menuHeadingPage = new AutoloadPage('menuHeadingPage', null, () => { - $('[data-heading-extract-to]').get().forEach((container) => { - const $container = $(container); - const $target = $('body').find($container.attr('data-heading-extract-to')); - if ($target.length === 0) { - return; - } - let $menu = $target.children('.menu'); - if ($menu.length === 0) { - $menu = $(tpl``).appendTo($target); - $target.children('.menu__link').addClass('expandable'); - } - $container.find('[data-heading]').get().forEach((heading) => { - const $heading = $(heading); - $(tpl` - - `).appendTo($menu); - }); - }); -}); - -export default menuHeadingPage; diff --git a/ui/components/menu/menu.page.js b/ui/components/menu/menu.page.js deleted file mode 100644 index 7c781b91f..000000000 --- a/ui/components/menu/menu.page.js +++ /dev/null @@ -1,18 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; -import { slideDown } from 'vj/utils/slide'; -import delay from 'vj/utils/delay'; - -function expandMenu($menu) { - slideDown($menu, 500, { opacity: 0 }, { opacity: 1 }); -} - -async function expandAllMenus() { - await delay(200); - $('.menu.collapsed').get().forEach((menu) => expandMenu($(menu))); -} - -const menuPage = new AutoloadPage('menuPage', () => { - expandAllMenus(); -}); - -export default menuPage; diff --git a/ui/components/menu/menu.page.styl b/ui/components/menu/menu.page.styl deleted file mode 100644 index 4b301cc37..000000000 --- a/ui/components/menu/menu.page.styl +++ /dev/null @@ -1,65 +0,0 @@ -.menu - padding: rem(5px 0) - font-size: rem($font-size-secondary) - - .icon - font-size: rem($font-size-icon) - display: inline-block - margin-right: rem(5px) - line-height: rem(18px) - vertical-align: top - - .hasjs &.collapsed - display: none - -.menu__item .menu - padding: 0 - -.menu__link - outline: 0 - text-align: left - display: block - width: 100% - padding: rem(12px $section-gap-h) - line-height: rem(18px) - color: $text-1-color - border-left: 3px solid transparent - position: relative - - &.highlight - background: mix($highlight-color, #FFF, 10%) - - &.expandable:after - font-family: $icon-font-name - font-size: rem($font-size-icon) - content: $icon-expand_more - position: absolute - right: $section-gap-h - top: 50% - line-height: 1 - margin-top: rem($font-size-icon / -2) - transition: transform .2s ease-out-cubic - opacity: 0.85 - -a.menu__link, button.menu__link - &.disabled - cursor: default - opacity: 0.5 - &:hover - text-decoration: none - &:not(.disabled):hover - color: $text-1-color - background: $menu-item-bg-color-hover - border-color: darken($primary-color, 10%) - &.active, &.active:hover - border-color: darken($secondary-color, 30%) - background: $secondary-color - color: #FFF - -.menu__item .menu .menu__link - padding-left: rem($section-gap-h + 10px) - -.menu__seperator - height: 1px - border-top: 1px solid #EEE - margin: rem(5px 0) diff --git a/ui/components/message/index.page.js b/ui/components/message/index.page.js deleted file mode 100644 index 6601b4434..000000000 --- a/ui/components/message/index.page.js +++ /dev/null @@ -1,38 +0,0 @@ -import { AutoloadPage } from 'vj/misc/PageLoader'; -import { InfoDialog } from 'vj/components/dialog'; -import Notification from 'vj/components/notification'; -import { FLAG_ALERT } from 'vj/constant/message'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; - -const messagePage = new AutoloadPage('messagePage', () => { - async function init() { - const { default: SockJs } = await import('../socket'); - - const sock = new SockJs(`/home/messages-conn?token=${UiContext.token}`); - sock.onmessage = (message) => { - const msg = JSON.parse(message.data); - if (msg.mdoc.flag | FLAG_ALERT) { - // Is alert - const dialog = new InfoDialog({ - $body: tpl` -
    -

    ${i18n(msg.udoc.content)}

    -
    `, - }); - dialog.open(); - } else { - // Is message - Notification.show({ - title: msg.udoc.uname, - avatar: msg.udoc.gravatar, - message: msg.mdoc.content, - }); - } - }; - } - - if (UserContext._id !== 0) init(); -}); - -export default messagePage; diff --git a/ui/components/messagepad/DialogueListItem.page.styl b/ui/components/messagepad/DialogueListItem.page.styl deleted file mode 100644 index f8ef155ed..000000000 --- a/ui/components/messagepad/DialogueListItem.page.styl +++ /dev/null @@ -1,28 +0,0 @@ -$stripe-width = 2px - -.messagepad__list-item - display: block - padding: rem(20px $section-gap-h 20px ($section-gap-h - $stripe-width)) - border-left: $stripe-width solid transparent - cursor: pointer - transition: border-color .1s linear, background-color .1s linear - overflow: hidden - text-overflow: ellipsis - - &:hover - border-color: #DDD - background: #F4F4F4 - text-decoration: none - - &.active - border-color: $secondary-color - background: rgba($secondary-color, 0.07) - -.messagepad__desc - font-size: rem($font-size-small) - color: #888 - max-height: 2em - margin-top: rem(5px) - overflow: hidden - text-overflow: ellipsis - word-wrap() diff --git a/ui/components/messagepad/DialogueListItemComponent.js b/ui/components/messagepad/DialogueListItemComponent.js deleted file mode 100644 index b2777109c..000000000 --- a/ui/components/messagepad/DialogueListItemComponent.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -export default function DialogueListItemComponent(props) { - const { - userName, - summary, - faceUrl, - active, - onClick, - className, - ...rest - } = props; - const cn = classNames(className, 'messagepad__list-item media', { - active, - }); - return ( -
  • - -
    - avatar -
    -
    -

    {userName}

    -
    {summary}
    -
    -
    -
  • - ); -} - -DialogueListItemComponent.propTypes = { - userName: PropTypes.string.isRequired, - summary: PropTypes.string.isRequired, - faceUrl: PropTypes.string.isRequired, - active: PropTypes.bool, - onClick: PropTypes.func, - className: PropTypes.string, -}; diff --git a/ui/components/messagepad/Message.page.styl b/ui/components/messagepad/Message.page.styl deleted file mode 100644 index fc443afe9..000000000 --- a/ui/components/messagepad/Message.page.styl +++ /dev/null @@ -1,64 +0,0 @@ -$corner-size = 10px - -.messagepad__message - overflow: hidden - display: flex - margin-bottom: rem(30px) - - &:last-child - margin-bottom: 0 - - &.side--self - justify-content: flex-end - -.messagepad__message__body - font-size: rem($font-size-secondary) - line-height: 1.5 - padding: rem(15px) - margin-bottom: rem(8px) - position: relative - min-width: rem(100px) - white-space: pre-wrap - word-wrap() - - time - opacity: 0.6 - font-size: rem($font-size-small) - - &:after - position: absolute - width: 0 - height: 0 - content: '' - top: 0 - -.side--other - .messagepad__message__body - margin-left: rem($corner-size + 10px) - background: $primary-color - color: #FFF - order: 1 - - &:after - left: rem(-($corner-size)) - border-top: $corner-size solid $primary-color - border-right: 0 solid transparent - border-bottom: 0 solid transparent - border-left: $corner-size solid transparent - -.side--self - .messagepad__message__body - margin-right: rem($corner-size + 10px) - background: #F0F0F0 - color: #222 - order: -1 - - time - text-align: right - - &:after - right: rem(-($corner-size)) - border-top: $corner-size solid #F0F0F0 - border-right: $corner-size solid transparent - border-bottom: 0 solid transparent - border-left: 0 solid transparent diff --git a/ui/components/messagepad/MessageComponent.js b/ui/components/messagepad/MessageComponent.js deleted file mode 100644 index de0c3ceb8..000000000 --- a/ui/components/messagepad/MessageComponent.js +++ /dev/null @@ -1,33 +0,0 @@ -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -export default function MessageComponent(props) { - const { - faceUrl, - isSelf, - className, - children, - ...rest - } = props; - const cn = classNames(className, 'messagepad__message', { - 'side--self': isSelf, - 'side--other': !isSelf, - }); - return ( -
  • -
    - avatar -
    -
    - {children} -
    -
  • - ); -} - -MessageComponent.propTypes = { - isSelf: PropTypes.bool, - faceUrl: PropTypes.string.isRequired, - className: PropTypes.string, - children: PropTypes.node, -}; diff --git a/ui/components/messagepad/MessagePad.page.styl b/ui/components/messagepad/MessagePad.page.styl deleted file mode 100644 index f6b9e787d..000000000 --- a/ui/components/messagepad/MessagePad.page.styl +++ /dev/null @@ -1,89 +0,0 @@ -$list-height = 500px -$sidebar-width = 250px -$input-height = 80px -$input-padding = 10px -$send-button-width = 50px - -#messagePad - min-height: rem($list-height) - position: relative - -.messagepad - position: relative - -.messagepad__sidebar - border-right: 1px solid #EEE - float: left - width: rem($sidebar-width) - -.messagepad__list - overflow: auto - height: rem($list-height) - -.messagepad__content - position: absolute - left: rem($sidebar-width) - right: 0 - top: 0 - bottom: rem($input-height) - overflow: auto - padding: rem(30px) - -.messagepad__input - display: none - position: absolute - left: rem($sidebar-width) - right: 0 - bottom: 0 - height: $input-height - border-top: 1px solid #EEE - - &.visible - display: block - - button - display: block - appearance: none - position: absolute - right: 0 - width: rem($send-button-width) - top: 0 - height: 100% - border: 0 - background: none - text-align: center - line-height: rem($input-height) - font-size: rem($font-size-icon) - transition: background-color .1s, color .1s, opacity .1s - transition-timing-function: linear - outline: 0 - - &:enabled - cursor: pointer - background-color: $primary-color - color: #FFF - - &:hover - background-color: lighten($primary-color, 10%) - - &:disabled - color: $text-1-color - opacity: 0.3 - -.messagepad__textarea-container - position: absolute - left: rem($input-padding) - right: rem($send-button-width + $input-padding) - top: rem($input-padding) - bottom: rem($input-padding) - - textarea - display: block - border: 0 - background: none - padding: 0 - width: 100% - height: 100% - font-size: rem($font-size-secondary) - resize: none - outline: 0 diff --git a/ui/components/messagepad/MessagePadDialogueContentContainer.js b/ui/components/messagepad/MessagePadDialogueContentContainer.js deleted file mode 100644 index ac16f459b..000000000 --- a/ui/components/messagepad/MessagePadDialogueContentContainer.js +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import _ from 'lodash'; -import { connect } from 'react-redux'; -import TimeAgo from 'timeago-react'; -import moment from 'moment'; -import 'jquery-scroll-lock'; -import 'jquery.easing'; - -import i18n from 'vj/utils/i18n'; -import { parse as parseMongoId } from 'vj/utils/mongoId'; -import Message from './MessageComponent'; - -const mapStateToProps = (state) => ({ - activeId: state.activeId, - item: state.activeId !== null - ? state.ues[state.activeId] - : null, -}); - -@connect(mapStateToProps, null) -export default class MessagePadDialogueContentContainer extends React.PureComponent { - componentDidMount() { - $(this.refs.list).scrollLock({ strict: true }); - } - - componentDidUpdate(prevProps) { - const node = this.refs.list; - if (this.props.activeId !== prevProps.activeId) { - this.scrollToBottom = true; - this.scrollWithAnimation = false; - } else if (node.scrollTop + node.offsetHeight === node.scrollHeight) { - this.scrollToBottom = true; - this.scrollWithAnimation = true; - } else this.scrollToBottom = false; - - if (this.scrollToBottom) { - const targetScrollTop = node.scrollHeight - node.offsetHeight; - if (this.scrollWithAnimation) { - $(node).stop().animate({ scrollTop: targetScrollTop }, 200, 'easeOutCubic'); - } else { - node.scrollTop = targetScrollTop; - } - } - } - - renderInner() { - if (this.props.activeId === null) { - return []; - } - return _.map(this.props.item.messages, (reply) => ( - -
    {reply.content}
    - -
    - )); - } - - render() { - return ( -
      - {this.renderInner()} -
    - ); - } -} diff --git a/ui/components/messagepad/MessagePadDialogueListContainer.js b/ui/components/messagepad/MessagePadDialogueListContainer.js deleted file mode 100644 index 20f7a6cc5..000000000 --- a/ui/components/messagepad/MessagePadDialogueListContainer.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import _ from 'lodash'; -import { connect } from 'react-redux'; -import 'jquery-scroll-lock'; - -import ListItem from './DialogueListItemComponent'; - -const mapStateToProps = (state) => ({ - activeId: state.activeId, - dialogues: state.dialogues, -}); - -const mapDispatchToProps = (dispatch) => ({ - handleClick(id) { - dispatch({ - type: 'DIALOGUES_SWITCH_TO', - payload: id, - }); - }, -}); - -@connect(mapStateToProps, mapDispatchToProps) -export default class MessagePadDialogueListContainer extends React.PureComponent { - componentDidMount() { - $(this.refs.list).scrollLock({ strict: true }); - } - - render() { - const orderedDialogues = _.orderBy( - _.values(this.props.dialogues), - (dialogue) => (_.maxBy(dialogue.messages, '_id') - ? _.maxBy(dialogue.messages, '_id')._id - : Number.POSITIVE_INFINITY), - 'desc', - ); - return ( -
      - {_.map(orderedDialogues, (dialogue) => ( - this.props.handleClick(dialogue._id)} - /> - ))} -
    - ); - } -} diff --git a/ui/components/messagepad/MessagePadInputContainer.js b/ui/components/messagepad/MessagePadInputContainer.js deleted file mode 100644 index 624733187..000000000 --- a/ui/components/messagepad/MessagePadInputContainer.js +++ /dev/null @@ -1,105 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import classNames from 'classnames'; -import Icon from 'vj/components/react/IconComponent'; - -import request from 'vj/utils/request'; - -const mapStateToProps = (state) => ({ - activeId: state.activeId, - isPosting: state.activeId !== null - ? state.isPosting[state.activeId] - : false, - inputValue: state.activeId !== null - ? state.inputs[state.activeId] - : '', -}); - -const mapDispatchToProps = (dispatch) => ({ - handleChange(id, value) { - if (id === null) return; - dispatch({ - type: 'DIALOGUES_INPUT_CHANGED', - payload: value, - meta: { - dialogueId: id, - }, - }); - }, - postSend(uid, value) { - const req = request.post('', { - operation: 'send', - uid, - content: value, - }); - dispatch({ - type: 'DIALOGUES_POST_SEND', - payload: req, - meta: { - dialogueId: uid, - }, - }); - }, -}); - -@connect(mapStateToProps, mapDispatchToProps) -export default class MessagePadInputContainer extends React.PureComponent { - static contextTypes = { - store: PropTypes.object, - }; - - UNSAFE_componentWillUpdate(nextProps) { // eslint-disable-line camelcase - this.focusInput = ( - nextProps.activeId !== this.props.activeId - || this.props.isPosting !== nextProps.isPosting && nextProps.isPosting === false - ); - } - - componentDidUpdate() { - if (this.focusInput) { - const { scrollX, scrollY } = window; - this.refs.input.focus(); - window.scrollTo(scrollX, scrollY); - } - } - - handleKeyDown(ev) { - if (ev.keyCode === 13 && (ev.ctrlKey || ev.metaKey)) { - this.submit(); - } - } - - submit() { - this.props.postSend( - this.props.activeId, - this.props.inputValue, - ); - } - - render() { - const cn = classNames('messagepad__input', { - visible: this.props.activeId !== null, - }); - return ( -
    -
    -