code.gitea.io/gitea@v1.21.7/web_src/js/markup/tasklist.js (about)

     1  import $ from 'jquery';
     2  
     3  const preventListener = (e) => e.preventDefault();
     4  
     5  /**
     6   * Attaches `input` handlers to markdown rendered tasklist checkboxes in comments.
     7   *
     8   * When a checkbox value changes, the corresponding [ ] or [x] in the markdown string
     9   * is set accordingly and sent to the server. On success it updates the raw-content on
    10   * error it resets the checkbox to its original value.
    11   */
    12  export function initMarkupTasklist() {
    13    for (const el of document.querySelectorAll(`.markup[data-can-edit=true]`) || []) {
    14      const container = el.parentNode;
    15      const checkboxes = el.querySelectorAll(`.task-list-item input[type=checkbox]`);
    16  
    17      for (const checkbox of checkboxes) {
    18        if (checkbox.hasAttribute('data-editable')) {
    19          return;
    20        }
    21  
    22        checkbox.setAttribute('data-editable', 'true');
    23        checkbox.addEventListener('input', async () => {
    24          const checkboxCharacter = checkbox.checked ? 'x' : ' ';
    25          const position = parseInt(checkbox.getAttribute('data-source-position')) + 1;
    26  
    27          const rawContent = container.querySelector('.raw-content');
    28          const oldContent = rawContent.textContent;
    29  
    30          const encoder = new TextEncoder();
    31          const buffer = encoder.encode(oldContent);
    32          // Indexes may fall off the ends and return undefined.
    33          if (buffer[position - 1] !== '['.codePointAt(0) ||
    34            buffer[position] !== ' '.codePointAt(0) && buffer[position] !== 'x'.codePointAt(0) ||
    35            buffer[position + 1] !== ']'.codePointAt(0)) {
    36            // Position is probably wrong.  Revert and don't allow change.
    37            checkbox.checked = !checkbox.checked;
    38            throw new Error(`Expected position to be space or x and surrounded by brackets, but it's not: position=${position}`);
    39          }
    40          buffer.set(encoder.encode(checkboxCharacter), position);
    41          const newContent = new TextDecoder().decode(buffer);
    42  
    43          if (newContent === oldContent) {
    44            return;
    45          }
    46  
    47          // Prevent further inputs until the request is done. This does not use the
    48          // `disabled` attribute because it causes the border to flash on click.
    49          for (const checkbox of checkboxes) {
    50            checkbox.addEventListener('click', preventListener);
    51          }
    52  
    53          try {
    54            const editContentZone = container.querySelector('.edit-content-zone');
    55            const updateUrl = editContentZone.getAttribute('data-update-url');
    56            const context = editContentZone.getAttribute('data-context');
    57  
    58            await $.post(updateUrl, {
    59              ignore_attachments: true,
    60              _csrf: window.config.csrfToken,
    61              content: newContent,
    62              context
    63            });
    64  
    65            rawContent.textContent = newContent;
    66          } catch (err) {
    67            checkbox.checked = !checkbox.checked;
    68            console.error(err);
    69          }
    70  
    71          // Enable input on checkboxes again
    72          for (const checkbox of checkboxes) {
    73            checkbox.removeEventListener('click', preventListener);
    74          }
    75        });
    76      }
    77  
    78      // Enable the checkboxes as they are initially disabled by the markdown renderer
    79      for (const checkbox of checkboxes) {
    80        checkbox.disabled = false;
    81      }
    82    }
    83  }