code.gitea.io/gitea@v1.21.7/web_src/js/features/repo-editor.js (about)

     1  import $ from 'jquery';
     2  import {htmlEscape} from 'escape-goat';
     3  import {createCodeEditor} from './codeeditor.js';
     4  import {hideElem, showElem} from '../utils/dom.js';
     5  import {initMarkupContent} from '../markup/content.js';
     6  import {attachRefIssueContextPopup} from './contextpopup.js';
     7  
     8  const {csrfToken} = window.config;
     9  
    10  function initEditPreviewTab($form) {
    11    const $tabMenu = $form.find('.tabular.menu');
    12    $tabMenu.find('.item').tab();
    13    const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`);
    14    if ($previewTab.length) {
    15      $previewTab.on('click', function () {
    16        const $this = $(this);
    17        let context = `${$this.data('context')}/`;
    18        const mode = $this.data('markup-mode') || 'comment';
    19        const treePathEl = $form.find('input#tree_path');
    20        if (treePathEl.length > 0) {
    21          context += treePathEl.val();
    22        }
    23        context = context.substring(0, context.lastIndexOf('/'));
    24        $.post($this.data('url'), {
    25          _csrf: csrfToken,
    26          mode,
    27          context,
    28          text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(),
    29          file_path: treePathEl.val(),
    30        }, (data) => {
    31          const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`);
    32          renderPreviewPanelContent($previewPanel, data);
    33        });
    34      });
    35    }
    36  }
    37  
    38  function initEditDiffTab($form) {
    39    const $tabMenu = $form.find('.tabular.menu');
    40    $tabMenu.find('.item').tab();
    41    $tabMenu.find(`.item[data-tab="${$tabMenu.data('diff')}"]`).on('click', function () {
    42      const $this = $(this);
    43      $.post($this.data('url'), {
    44        _csrf: csrfToken,
    45        context: $this.data('context'),
    46        content: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(),
    47      }, (data) => {
    48        const $diffPreviewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('diff')}"]`);
    49        $diffPreviewPanel.html(data);
    50      });
    51    });
    52  }
    53  
    54  function initEditorForm() {
    55    if ($('.repository .edit.form').length === 0) {
    56      return;
    57    }
    58  
    59    initEditPreviewTab($('.repository .edit.form'));
    60    initEditDiffTab($('.repository .edit.form'));
    61  }
    62  
    63  
    64  function getCursorPosition($e) {
    65    const el = $e.get(0);
    66    let pos = 0;
    67    if ('selectionStart' in el) {
    68      pos = el.selectionStart;
    69    } else if ('selection' in document) {
    70      el.focus();
    71      const Sel = document.selection.createRange();
    72      const SelLength = document.selection.createRange().text.length;
    73      Sel.moveStart('character', -el.value.length);
    74      pos = Sel.text.length - SelLength;
    75    }
    76    return pos;
    77  }
    78  
    79  export function initRepoEditor() {
    80    initEditorForm();
    81  
    82    $('.js-quick-pull-choice-option').on('change', function () {
    83      if ($(this).val() === 'commit-to-new-branch') {
    84        showElem($('.quick-pull-branch-name'));
    85        $('.quick-pull-branch-name input').prop('required', true);
    86      } else {
    87        hideElem($('.quick-pull-branch-name'));
    88        $('.quick-pull-branch-name input').prop('required', false);
    89      }
    90      $('#commit-button').text($(this).attr('button_text'));
    91    });
    92  
    93    const joinTreePath = ($fileNameEl) => {
    94      const parts = [];
    95      $('.breadcrumb span.section').each(function () {
    96        const element = $(this);
    97        if (element.find('a').length) {
    98          parts.push(element.find('a').text());
    99        } else {
   100          parts.push(element.text());
   101        }
   102      });
   103      if ($fileNameEl.val()) parts.push($fileNameEl.val());
   104      $('#tree_path').val(parts.join('/'));
   105    };
   106  
   107    const $editFilename = $('#file-name');
   108    $editFilename.on('input', function () {
   109      const parts = $(this).val().split('/');
   110  
   111      if (parts.length > 1) {
   112        for (let i = 0; i < parts.length; ++i) {
   113          const value = parts[i];
   114          if (i < parts.length - 1) {
   115            if (value.length) {
   116              $(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(this));
   117              $('<div class="breadcrumb-divider">/</div>').insertBefore($(this));
   118            }
   119          } else {
   120            $(this).val(value);
   121          }
   122          this.setSelectionRange(0, 0);
   123        }
   124      }
   125  
   126      joinTreePath($(this));
   127    });
   128  
   129    $editFilename.on('keydown', function (e) {
   130      const $section = $('.breadcrumb span.section');
   131  
   132      // Jump back to last directory once the filename is empty
   133      if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) {
   134        e.preventDefault();
   135        const $divider = $('.breadcrumb .breadcrumb-divider');
   136        const value = $section.last().find('a').text();
   137        $(this).val(value + $(this).val());
   138        this.setSelectionRange(value.length, value.length);
   139        $section.last().remove();
   140        $divider.last().remove();
   141        joinTreePath($(this));
   142      }
   143    });
   144  
   145    const $editArea = $('.repository.editor textarea#edit_area');
   146    if (!$editArea.length) return;
   147  
   148    (async () => {
   149      const editor = await createCodeEditor($editArea[0], $editFilename[0]);
   150  
   151      // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
   152      // to enable or disable the commit button
   153      const $commitButton = $('#commit-button');
   154      const $editForm = $('.ui.edit.form');
   155      const dirtyFileClass = 'dirty-file';
   156  
   157      // Disabling the button at the start
   158      if ($('input[name="page_has_posted"]').val() !== 'true') {
   159        $commitButton.prop('disabled', true);
   160      }
   161  
   162      // Registering a custom listener for the file path and the file content
   163      $editForm.areYouSure({
   164        silent: true,
   165        dirtyClass: dirtyFileClass,
   166        fieldSelector: ':input:not(.commit-form-wrapper :input)',
   167        change() {
   168          const dirty = $(this).hasClass(dirtyFileClass);
   169          $commitButton.prop('disabled', !dirty);
   170        },
   171      });
   172  
   173      // Update the editor from query params, if available,
   174      // only after the dirtyFileClass initialization
   175      const params = new URLSearchParams(window.location.search);
   176      const value = params.get('value');
   177      if (value) {
   178        editor.setValue(value);
   179      }
   180  
   181      $commitButton.on('click', (event) => {
   182        // A modal which asks if an empty file should be committed
   183        if ($editArea.val().length === 0) {
   184          $('#edit-empty-content-modal').modal({
   185            onApprove() {
   186              $('.edit.form').trigger('submit');
   187            },
   188          }).modal('show');
   189          event.preventDefault();
   190        }
   191      });
   192    })();
   193  }
   194  
   195  export function renderPreviewPanelContent($panelPreviewer, data) {
   196    $panelPreviewer.html(data);
   197    initMarkupContent();
   198  
   199    const refIssues = $panelPreviewer.find('p .ref-issue');
   200    attachRefIssueContextPopup(refIssues);
   201  }