Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions web_src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import initHeatmap from './features/heatmap.js';
import initProject from './features/projects.js';
import initServiceWorker from './features/serviceworker.js';
import initMarkdownAnchors from './markdown/anchors.js';
import initMarkdownCheckboxes from './markdown/checkboxes.js';
import renderMarkdownContent from './markdown/content.js';
import attachTribute from './features/tribute.js';
import createColorPicker from './features/colorpicker.js';
Expand Down Expand Up @@ -2571,6 +2572,8 @@ $(document).ready(async () => {
renderMarkdownContent(),
initGithook(),
]);

initMarkdownCheckboxes();
});

function changeHash(hash) {
Expand Down
70 changes: 70 additions & 0 deletions web_src/js/markdown/checkboxes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const checkboxMarkdownPattern = /\[[ x]]/g;

/**
* Attaches `change` handlers to markdown rendered checkboxes in comments.
* When a checkbox value changes, the corresponding [ ] or [x] in the markdown string is set accordingly and sent to the server.
* On success it updates the raw-content on error it resets the checkbox to its original value.
*/
export default function initMarkdownCheckboxes() {
$('.comment .segment').each((_, segment) => {
const $segment = $(segment);
const $checkboxes = $segment.find('.render-content.markdown input:checkbox');

const onChange = async (ev, cbIndex) => {
const $cb = $(ev.target);
const checkboxMarkdown = $cb.is(':checked') ? '[x]' : '[ ]';

const $rawContent = $segment.find('.raw-content');
const oldContent = $rawContent.text();
const newContent = oldContent.replace(checkboxMarkdownPattern, replaceNthMatchWith(cbIndex, checkboxMarkdown));

if (newContent !== oldContent) {
disableAll($checkboxes);

try {
const url = $segment.find('.edit-content-zone').data('update-url');
const context = $segment.find('.edit-content-zone').data('context');

await submit(newContent, url, context);
$rawContent.text(newContent);
} catch (e) {
$cb.prop('checked', !$cb.is(':checked'));

console.error(e);
} finally {
enableAll($checkboxes);
}
}
};

enableAll($checkboxes);
$checkboxes.each((cbIndex, cb) => {
$(cb).on('change', (ev) => onChange(ev, cbIndex));
});
});
}

function enableAll ($checkboxes) { $checkboxes.removeAttr('disabled') }
function disableAll ($checkboxes) { $checkboxes.attr('disabled', 'disabled') }

function submit (content, url, context) {
const csrf = window.config.csrf;

return $.post(url, {
_csrf: csrf,
context,
content,
});
}

function replaceNthMatchWith(n, replaceWith) {
let matchIndex = 0;

return (match) => {
if (n === matchIndex++) {
return replaceWith;
}

return match;
};
}