import $ from 'jquery'; import {htmlEscape} from 'escape-goat'; import {createCodeEditor} from './codeeditor.js'; import {hideElem, showElem} from '../utils/dom.js'; import {initMarkupContent} from '../markup/content.js'; import {attachRefIssueContextPopup} from './contextpopup.js'; import {POST} from '../modules/fetch.js'; function initEditPreviewTab($form) { const $tabMenu = $form.find('.tabular.menu'); $tabMenu.find('.item').tab(); const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`); if ($previewTab.length) { $previewTab.on('click', async function () { const $this = $(this); let context = `${$this.data('context')}/`; const mode = $this.data('markup-mode') || 'comment'; const treePathEl = $form.find('input#tree_path'); if (treePathEl.length > 0) { context += treePathEl.val(); } context = context.substring(0, context.lastIndexOf('/')); const formData = new FormData(); formData.append('mode', mode); formData.append('context', context); formData.append('text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()); formData.append('file_path', treePathEl.val()); try { const response = await POST($this.data('url'), {data: formData}); const data = await response.text(); const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); renderPreviewPanelContent($previewPanel, data); } catch (error) { console.error('Error:', error); } }); } } function initEditorForm() { if ($('.repository .edit.form').length === 0) { return; } initEditPreviewTab($('.repository .edit.form')); } function getCursorPosition($e) { const el = $e.get(0); let pos = 0; if ('selectionStart' in el) { pos = el.selectionStart; } else if ('selection' in document) { el.focus(); const Sel = document.selection.createRange(); const SelLength = document.selection.createRange().text.length; Sel.moveStart('character', -el.value.length); pos = Sel.text.length - SelLength; } return pos; } export function initRepoEditor() { initEditorForm(); $('.js-quick-pull-choice-option').on('change', function () { if ($(this).val() === 'commit-to-new-branch') { showElem($('.quick-pull-branch-name')); $('.quick-pull-branch-name input').prop('required', true); } else { hideElem($('.quick-pull-branch-name')); $('.quick-pull-branch-name input').prop('required', false); } $('#commit-button').text($(this).attr('button_text')); }); const joinTreePath = ($fileNameEl) => { const parts = []; $('.breadcrumb span.section').each(function () { const element = $(this); if (element.find('a').length) { parts.push(element.find('a').text()); } else { parts.push(element.text()); } }); if ($fileNameEl.val()) parts.push($fileNameEl.val()); $('#tree_path').val(parts.join('/')); }; const $editFilename = $('#file-name'); $editFilename.on('input', function () { const parts = $(this).val().split('/'); if (parts.length > 1) { for (let i = 0; i < parts.length; ++i) { const value = parts[i]; if (i < parts.length - 1) { if (value.length) { $(`${htmlEscape(value)}`).insertBefore($(this)); $('').insertBefore($(this)); } } else { $(this).val(value); } this.setSelectionRange(0, 0); } } joinTreePath($(this)); }); $editFilename.on('keydown', function (e) { const $section = $('.breadcrumb span.section'); // Jump back to last directory once the filename is empty if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) { e.preventDefault(); const $divider = $('.breadcrumb .breadcrumb-divider'); const value = $section.last().find('a').text(); $(this).val(value + $(this).val()); this.setSelectionRange(value.length, value.length); $section.last().remove(); $divider.last().remove(); joinTreePath($(this)); } }); const $editArea = $('.repository.editor textarea#edit_area'); if (!$editArea.length) return; (async () => { const editor = await createCodeEditor($editArea[0], $editFilename[0]); // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage // to enable or disable the commit button const $commitButton = $('#commit-button'); const $editForm = $('.ui.edit.form'); const dirtyFileClass = 'dirty-file'; // Disabling the button at the start if ($('input[name="page_has_posted"]').val() !== 'true') { $commitButton.prop('disabled', true); } // Registering a custom listener for the file path and the file content $editForm.areYouSure({ silent: true, dirtyClass: dirtyFileClass, fieldSelector: ':input:not(.commit-form-wrapper :input)', change() { const dirty = $(this).hasClass(dirtyFileClass); $commitButton.prop('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.on('click', (event) => { // A modal which asks if an empty file should be committed if ($editArea.val().length === 0) { $('#edit-empty-content-modal').modal({ onApprove() { $('.edit.form').trigger('submit'); }, }).modal('show'); event.preventDefault(); } }); })(); } export function renderPreviewPanelContent($panelPreviewer, data) { $panelPreviewer.html(data); initMarkupContent(); const refIssues = $panelPreviewer.find('p .ref-issue'); attachRefIssueContextPopup(refIssues); }