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 }