import Clipboard from 'clipboard'; import $ from 'jquery'; import { nanoid } from 'nanoid'; import { ActionDialog, InfoDialog } from 'vj/components/dialog/index'; import Notification from 'vj/components/notification'; import { AutoloadPage } from 'vj/misc/Page'; import i18n from 'vj/utils/i18n'; import pjax from 'vj/utils/pjax'; import request from 'vj/utils/request'; import tpl from 'vj/utils/tpl'; async function startEdit(filename, value, fileCategory = 'file') { const { default: Editor } = await import('vj/components/editor/index'); const promise = new ActionDialog({ $body: tpl`
`, width: `${window.innerWidth - 200}px`, height: `${window.innerHeight - 100}px`, cancelByEsc: false, }).open(); const languages = [ ['yaml', ['yaml', 'yml']], ['cpp', ['c', 'cc', 'cpp', 'h', 'hpp']], ['json', ['json']], ['plain', ['in', 'out', 'ans']], ]; const language = languages.filter((i) => i[1].includes(filename.split('.').pop()))[0]?.[0] || 'auto'; const editor = new Editor($('[name="fileContent"]'), { value, autoResize: false, autoLayout: false, language: (language as string), model: `hydro://problem/${fileCategory}/${filename}`, }); const action = await promise; value = (editor.value() as string).replace(/\r\n/g, '\n'); editor.destory(); if (action !== 'ok') return null; return value; } const dialogAction = (id) => [ tpl``, tpl``, tpl``, ]; function bindCopyLink(id, src: string) { const url = !(window.location.href.endsWith('file') || window.location.href.endsWith('files')) || window.location.href.match('contest/.*/file') ? `file://${src.substring(src.lastIndexOf('/') + 1)}` : src; const clip = new Clipboard(`#copy-${id}`, { text: () => `${url}` }); clip.on('success', () => Notification.success(i18n(`${url.startsWith('file://') ? 'Reference' : 'Download'} link copied to clipboard!`))); clip.on('error', () => Notification.error(i18n('Copy failed :('))); } async function previewImage(link) { const id = nanoid(); const dialog = new InfoDialog({ $body: tpl`
`, $action: dialogAction(id), }); bindCopyLink(id, link); const action = await dialog.open(); if (action === 'download') window.open(link); } async function previewPDF(link) { const uuidURL = URL.createObjectURL(new Blob()); const uuid = uuidURL.toString(); URL.revokeObjectURL(uuidURL); const id = nanoid(); const dialog = new InfoDialog({ $body: tpl`
`, width: `${window.innerWidth - 200}px`, height: `${window.innerHeight - 100}px`, $action: dialogAction(id), }); bindCopyLink(id, link); const action = await dialog.open(); if (action === 'download') window.open(link); } async function previewOffice(link, src) { const id = nanoid(); const dialog = new InfoDialog({ $body: tpl`
`, width: `${window.innerWidth - 200}px`, height: `${window.innerHeight - 100}px`, $action: dialogAction(id), }); bindCopyLink(id, link); const action = await dialog.open(); if (action === 'download') window.open(link); } export async function previewFile(ev, type = '') { if (ev?.metaKey || ev?.ctrlKey || ev?.shiftKey) return null; if (ev) ev.preventDefault(); const filename = ev ? ev.currentTarget.closest('[data-filename]').getAttribute('data-filename') // eslint-disable-next-line no-alert : prompt('Filename'); if (!filename) return null; const filesize = ev ? +ev.currentTarget.closest('[data-size]').getAttribute('data-size') : 0; let content = ''; if (ev) { const link = $(ev.currentTarget).find('a').attr('href'); if (!link) return null; if (!type) type = ev.currentTarget.getAttribute('data-preview'); const ext = filename.split('.').pop(); if (['zip', 'rar', '7z'].includes(ext) || filesize > 8 * 1024 * 1024) { const action = await new ActionDialog({ $body: tpl.typoMsg(i18n('Cannot preview this file. Download now?')), }).open(); if (action === 'ok') window.open(link); return null; } if (['png', 'jpeg', 'jpg', 'gif', 'webp', 'bmp'].includes(ext)) return previewImage(link); if (ext === 'pdf') return previewPDF(`${link}${link.includes('?') ? '&' : '?'}noDisposition=1`); Notification.info(i18n('Loading file...')); try { const { url } = await request.get(link); if (/^(doc|xls|ppt)x?$/.test(ext)) return previewOffice(link, url); content = await request.get(url, undefined, { dataType: 'text' }); } catch (e) { Notification.error(i18n('Failed to load file: {0}', e.message)); throw e; } } else Notification.info(i18n('Loading editor...')); const val = await startEdit(filename, content, type || 'file'); if (typeof val !== 'string') return null; Notification.info(i18n('Saving file...')); const data = new FormData(); data.append('filename', filename); data.append('file', new Blob([val], { type: 'text/plain' })); if (type) data.append('type', type); data.append('operation', 'upload_file'); const postUrl = !window.location.href.endsWith('/files') ? `${window.location.href.substring(0, window.location.href.lastIndexOf('/'))}/files` : ''; await request.postFile(postUrl, data); Notification.success(i18n('File saved.')); return pjax.request({ push: false }); } const dataPreviewPage = new AutoloadPage('dataPreview', () => { $(document).on('click', '[data-preview]', previewFile); }); window.Hydro.components.preview = { startEdit, previewFile }; export default dataPreviewPage;