mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 14:31:02 +00:00 
			
		
		
		
	Extend API MarkupOptions to contain branch path.
The `api.MarkupOptions{}`  to have `BranchPath` which contains the
current branch. The `RenderMarkup` function utilizes a struct since there
are too many variables passed as arguments and that is not a good sign
for readability.
And `repo-editor.js` will contain a new form data which is `branch-path`
which will then be utilized by `edit.tmpl` as `data-branch-path`.
Closes: #4510
## Checklist
The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).
### Tests
- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [x] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).
### Documentation
- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [ ] I did not document these changes and I do not expect someone else to do it.
### Release notes
- [ ] I do not want this change to show in the release notes.
- [x] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5781
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Nirmal Kumar R <tildezero@gmail.com>
Co-committed-by: Nirmal Kumar R <tildezero@gmail.com>
		
	
			
		
			
				
	
	
		
			204 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import $ from 'jquery';
 | |
| import {htmlEscape} from 'escape-goat';
 | |
| import {createCodeEditor} from './codeeditor.js';
 | |
| import {hideElem, showElem, createElementFromHTML} 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('branch_path', $this.data('branch-path'));
 | |
|       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() {
 | |
|   const $form = $('.repository .edit.form');
 | |
|   if (!$form) return;
 | |
|   initEditPreviewTab($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');
 | |
|       document.querySelector('.quick-pull-branch-name input').required = true;
 | |
|     } else {
 | |
|       hideElem('.quick-pull-branch-name');
 | |
|       document.querySelector('.quick-pull-branch-name input').required = false;
 | |
|     }
 | |
|     $('#commit-button').text(this.getAttribute('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) {
 | |
|             $editFilename[0].before(
 | |
|               createElementFromHTML(
 | |
|                 `<span class="section"><a href="#">${htmlEscape(value)}</a></span>`,
 | |
|               ),
 | |
|             );
 | |
|             $editFilename[0].before(
 | |
|               createElementFromHTML(`<div class="breadcrumb-divider">/</div>`),
 | |
|             );
 | |
|           }
 | |
|         } 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 = document.getElementById('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($panelPreviewer, data) {
 | |
|   $panelPreviewer.html(data);
 | |
|   initMarkupContent();
 | |
| 
 | |
|   const $refIssues = $panelPreviewer.find('p .ref-issue');
 | |
|   attachRefIssueContextPopup($refIssues);
 | |
| }
 |