mirror of
https://github.com/go-gitea/gitea
synced 2025-03-10 12:44:28 +00:00
1. Add some "render-content" classes to "markup" elements when the content is rendered 2. Use correct "markup" wrapper for "preview" (but not set that class on the tab) 3. Remove incorrect "markup" class from LFS file view, because there is no markup content * "edit-diff" is also removed because it does nothing 5. Use "initPdfViewer" for PDF viewer 6. Remove incorrect "content" class from milestone markup 7. Init all ".markup" elements by new init framework --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
204 lines
8.7 KiB
TypeScript
204 lines
8.7 KiB
TypeScript
import {htmlEscape} from 'escape-goat';
|
|
import {createCodeEditor} from './codeeditor.ts';
|
|
import {hideElem, queryElems, showElem, createElementFromHTML} from '../utils/dom.ts';
|
|
import {attachRefIssueContextPopup} from './contextpopup.ts';
|
|
import {POST} from '../modules/fetch.ts';
|
|
import {initDropzone} from './dropzone.ts';
|
|
import {confirmModal} from './comp/ConfirmModal.ts';
|
|
import {applyAreYouSure, ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
|
|
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
|
|
|
function initEditPreviewTab(elForm: HTMLFormElement) {
|
|
const elTabMenu = elForm.querySelector('.repo-editor-menu');
|
|
fomanticQuery(elTabMenu.querySelectorAll('.item')).tab();
|
|
|
|
const elPreviewTab = elTabMenu.querySelector('a[data-tab="preview"]');
|
|
const elPreviewPanel = elForm.querySelector('.tab[data-tab="preview"]');
|
|
if (!elPreviewTab || !elPreviewPanel) return;
|
|
|
|
elPreviewTab.addEventListener('click', async () => {
|
|
const elTreePath = elForm.querySelector<HTMLInputElement>('input#tree_path');
|
|
const previewUrl = elPreviewTab.getAttribute('data-preview-url');
|
|
const previewContextRef = elPreviewTab.getAttribute('data-preview-context-ref');
|
|
let previewContext = `${previewContextRef}/${elTreePath.value}`;
|
|
previewContext = previewContext.substring(0, previewContext.lastIndexOf('/'));
|
|
const formData = new FormData();
|
|
formData.append('mode', 'file');
|
|
formData.append('context', previewContext);
|
|
formData.append('text', elForm.querySelector<HTMLTextAreaElement>('.tab[data-tab="write"] textarea').value);
|
|
formData.append('file_path', elTreePath.value);
|
|
const response = await POST(previewUrl, {data: formData});
|
|
const data = await response.text();
|
|
renderPreviewPanelContent(elPreviewPanel, data);
|
|
});
|
|
}
|
|
|
|
export function initRepoEditor() {
|
|
const dropzoneUpload = document.querySelector<HTMLElement>('.page-content.repository.editor.upload .dropzone');
|
|
if (dropzoneUpload) initDropzone(dropzoneUpload);
|
|
|
|
for (const el of queryElems<HTMLInputElement>(document, '.js-quick-pull-choice-option')) {
|
|
el.addEventListener('input', () => {
|
|
if (el.value === 'commit-to-new-branch') {
|
|
showElem('.quick-pull-branch-name');
|
|
document.querySelector<HTMLInputElement>('.quick-pull-branch-name input').required = true;
|
|
} else {
|
|
hideElem('.quick-pull-branch-name');
|
|
document.querySelector<HTMLInputElement>('.quick-pull-branch-name input').required = false;
|
|
}
|
|
document.querySelector('#commit-button').textContent = el.getAttribute('data-button-text');
|
|
});
|
|
}
|
|
|
|
const filenameInput = document.querySelector<HTMLInputElement>('#file-name');
|
|
if (!filenameInput) return;
|
|
function joinTreePath() {
|
|
const parts = [];
|
|
for (const el of document.querySelectorAll('.breadcrumb span.section')) {
|
|
const link = el.querySelector('a');
|
|
parts.push(link ? link.textContent : el.textContent);
|
|
}
|
|
if (filenameInput.value) {
|
|
parts.push(filenameInput.value);
|
|
}
|
|
document.querySelector<HTMLInputElement>('#tree_path').value = parts.join('/');
|
|
}
|
|
filenameInput.addEventListener('input', function () {
|
|
const parts = filenameInput.value.split('/');
|
|
const links = Array.from(document.querySelectorAll('.breadcrumb span.section'));
|
|
const dividers = Array.from(document.querySelectorAll('.breadcrumb .breadcrumb-divider'));
|
|
let warningDiv = document.querySelector<HTMLDivElement>('.ui.warning.message.flash-message.flash-warning.space-related');
|
|
let containSpace = false;
|
|
if (parts.length > 1) {
|
|
for (let i = 0; i < parts.length; ++i) {
|
|
const value = parts[i];
|
|
const trimValue = value.trim();
|
|
if (trimValue === '..') {
|
|
// remove previous tree path
|
|
if (links.length > 0) {
|
|
const link = links.pop();
|
|
const divider = dividers.pop();
|
|
link.remove();
|
|
divider.remove();
|
|
}
|
|
continue;
|
|
}
|
|
if (i < parts.length - 1) {
|
|
if (trimValue.length) {
|
|
const linkElement = createElementFromHTML(
|
|
`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`,
|
|
);
|
|
const dividerElement = createElementFromHTML(
|
|
`<div class="breadcrumb-divider">/</div>`,
|
|
);
|
|
links.push(linkElement);
|
|
dividers.push(dividerElement);
|
|
filenameInput.before(linkElement);
|
|
filenameInput.before(dividerElement);
|
|
}
|
|
} else {
|
|
filenameInput.value = value;
|
|
}
|
|
this.setSelectionRange(0, 0);
|
|
containSpace = containSpace || (trimValue !== value && trimValue !== '');
|
|
}
|
|
}
|
|
containSpace = containSpace || Array.from(links).some((link) => {
|
|
const value = link.querySelector('a').textContent;
|
|
return value.trim() !== value;
|
|
});
|
|
containSpace = containSpace || parts[parts.length - 1].trim() !== parts[parts.length - 1];
|
|
if (containSpace) {
|
|
if (!warningDiv) {
|
|
warningDiv = document.createElement('div');
|
|
warningDiv.classList.add('ui', 'warning', 'message', 'flash-message', 'flash-warning', 'space-related');
|
|
warningDiv.innerHTML = '<p>File path contains leading or trailing whitespace.</p>';
|
|
// Add display 'block' because display is set to 'none' in formantic\build\semantic.css
|
|
warningDiv.style.display = 'block';
|
|
const inputContainer = document.querySelector('.repo-editor-header');
|
|
inputContainer.insertAdjacentElement('beforebegin', warningDiv);
|
|
}
|
|
showElem(warningDiv);
|
|
} else if (warningDiv) {
|
|
hideElem(warningDiv);
|
|
}
|
|
joinTreePath();
|
|
});
|
|
filenameInput.addEventListener('keydown', function (e) {
|
|
const sections = queryElems(document, '.breadcrumb span.section');
|
|
const dividers = queryElems(document, '.breadcrumb .breadcrumb-divider');
|
|
// Jump back to last directory once the filename is empty
|
|
if (e.code === 'Backspace' && filenameInput.selectionStart === 0 && sections.length > 0) {
|
|
e.preventDefault();
|
|
const lastSection = sections[sections.length - 1];
|
|
const lastDivider = dividers.length ? dividers[dividers.length - 1] : null;
|
|
const value = lastSection.querySelector('a').textContent;
|
|
filenameInput.value = value + filenameInput.value;
|
|
this.setSelectionRange(value.length, value.length);
|
|
lastDivider?.remove();
|
|
lastSection.remove();
|
|
joinTreePath();
|
|
}
|
|
});
|
|
|
|
// on the upload page, there is no editor(textarea)
|
|
const editArea = document.querySelector<HTMLTextAreaElement>('.page-content.repository.editor textarea#edit_area');
|
|
if (!editArea) return;
|
|
|
|
const elForm = document.querySelector<HTMLFormElement>('.repository.editor .edit.form');
|
|
initEditPreviewTab(elForm);
|
|
|
|
(async () => {
|
|
const editor = await createCodeEditor(editArea, filenameInput);
|
|
|
|
// Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
|
|
// to enable or disable the commit button
|
|
const commitButton = document.querySelector<HTMLButtonElement>('#commit-button');
|
|
const dirtyFileClass = 'dirty-file';
|
|
|
|
// Disabling the button at the start
|
|
if (document.querySelector<HTMLInputElement>('input[name="page_has_posted"]').value !== 'true') {
|
|
commitButton.disabled = true;
|
|
}
|
|
|
|
// Registering a custom listener for the file path and the file content
|
|
// FIXME: it is not quite right here (old bug), it causes double-init, the global areYouSure "dirty" class will also be added
|
|
applyAreYouSure(elForm, {
|
|
silent: true,
|
|
dirtyClass: dirtyFileClass,
|
|
fieldSelector: ':input:not(.commit-form-wrapper :input)',
|
|
change($form: any) {
|
|
const dirty = $form[0]?.classList.contains(dirtyFileClass);
|
|
commitButton.disabled = !dirty;
|
|
},
|
|
});
|
|
|
|
// Update the editor from query params, if available,
|
|
// only after the dirtyFileClass initialization
|
|
const params = new URLSearchParams(window.location.search);
|
|
const value = params.get('value');
|
|
if (value) {
|
|
editor.setValue(value);
|
|
}
|
|
|
|
commitButton?.addEventListener('click', async (e) => {
|
|
// A modal which asks if an empty file should be committed
|
|
if (!editArea.value) {
|
|
e.preventDefault();
|
|
if (await confirmModal({
|
|
header: elForm.getAttribute('data-text-empty-confirm-header'),
|
|
content: elForm.getAttribute('data-text-empty-confirm-content'),
|
|
})) {
|
|
ignoreAreYouSure(elForm);
|
|
elForm.submit();
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
}
|
|
|
|
export function renderPreviewPanelContent(previewPanel: Element, content: string) {
|
|
previewPanel.innerHTML = `<div class="render-content markup">${content}</div>`;
|
|
attachRefIssueContextPopup(previewPanel.querySelectorAll('p .ref-issue'));
|
|
}
|