As per https://github.com/go-gitea/gitea/pull/30115#discussion_r1626060164, prefer `querySelector` by enabling [`unicorn/prefer-query-selector`](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-query-selector.md) and autofixing all except 10 issues. According to [this](https://old.reddit.com/r/learnjavascript/comments/i0f5o8/performance_of_getelementbyid_vs_queryselector/), querySelector may be faster as well, so it's a win-win. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
		
			
				
	
	
		
			164 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import $ from 'jquery';
 | |
| import {htmlEscape} from 'escape-goat';
 | |
| import {createCodeEditor} from './codeeditor.js';
 | |
| import {hideElem, queryElems, 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('.repo-editor-menu');
 | |
|   $tabMenu.find('.item').tab();
 | |
|   const $previewTab = $tabMenu.find('a[data-tab="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="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="preview"]');
 | |
|         if ($previewPanel.length) {
 | |
|           renderPreviewPanelContent($previewPanel, data);
 | |
|         }
 | |
|       } catch (error) {
 | |
|         console.error('Error:', error);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| }
 | |
| 
 | |
| export function initRepoEditor() {
 | |
|   const $editArea = $('.repository.editor textarea#edit_area');
 | |
|   if (!$editArea.length) return;
 | |
| 
 | |
|   for (const el of queryElems('.js-quick-pull-choice-option')) {
 | |
|     el.addEventListener('input', () => {
 | |
|       if (el.value === 'commit-to-new-branch') {
 | |
|         showElem('.quick-pull-branch-name');
 | |
|         document.querySelector('.quick-pull-branch-name input').required = true;
 | |
|       } else {
 | |
|         hideElem('.quick-pull-branch-name');
 | |
|         document.querySelector('.quick-pull-branch-name input').required = false;
 | |
|       }
 | |
|       document.querySelector('#commit-button').textContent = el.getAttribute('data-button-text');
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   const filenameInput = document.querySelector('#file-name');
 | |
|   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('#tree_path').value = parts.join('/');
 | |
|   }
 | |
|   filenameInput.addEventListener('input', function () {
 | |
|     const parts = filenameInput.value.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) {
 | |
|             $(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(filenameInput));
 | |
|             $('<div class="breadcrumb-divider">/</div>').insertBefore($(filenameInput));
 | |
|           }
 | |
|         } else {
 | |
|           filenameInput.value = value;
 | |
|         }
 | |
|         this.setSelectionRange(0, 0);
 | |
|       }
 | |
|     }
 | |
|     joinTreePath();
 | |
|   });
 | |
|   filenameInput.addEventListener('keydown', function (e) {
 | |
|     const sections = queryElems('.breadcrumb span.section');
 | |
|     const dividers = queryElems('.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();
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   const $form = $('.repository.editor .edit.form');
 | |
|   initEditPreviewTab($form);
 | |
| 
 | |
|   (async () => {
 | |
|     const editor = await createCodeEditor($editArea[0], filenameInput);
 | |
| 
 | |
|     // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
 | |
|     // to enable or disable the commit button
 | |
|     const commitButton = document.querySelector('#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.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($form) {
 | |
|         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', (e) => {
 | |
|       // A modal which asks if an empty file should be committed
 | |
|       if (!$editArea.val()) {
 | |
|         $('#edit-empty-content-modal').modal({
 | |
|           onApprove() {
 | |
|             $('.edit.form').trigger('submit');
 | |
|           },
 | |
|         }).modal('show');
 | |
|         e.preventDefault();
 | |
|       }
 | |
|     });
 | |
|   })();
 | |
| }
 | |
| 
 | |
| export function renderPreviewPanelContent($previewPanel, data) {
 | |
|   $previewPanel.html(data);
 | |
|   initMarkupContent();
 | |
| 
 | |
|   const $refIssues = $previewPanel.find('p .ref-issue');
 | |
|   attachRefIssueContextPopup($refIssues);
 | |
| }
 |