const TPL = `<div style="padding: 10px; border-top: 1px solid var(--main-border-color); contain: none;">
<strong>Todo Highlighting Applied</strong>
</div>`;
class MoodlogHighlighterWidget extends api.NoteContextAwareWidget {
static get parentWidget() {
//console.log("MDLG: Getting parent widget: center-pane");
return 'center-pane';
}
get position() {
//console.log("MDLG: Setting position: 100");
return 100;
}
isEnabled() {
const enabled = super.isEnabled()
&& this.note.type === 'text'
&& this.note.hasLabel('todotxt');
//console.log(`MDLG: isEnabled: ${enabled}`);
return enabled;
}
doRender() {
//console.log("MDLG: Rendering widget...");
this.$widget = $(TPL);
//console.log("MDLG: Widget rendered:", this.$widget);
return this.$widget;
}
async refreshWithNote(note) {
//console.log("MDLG: Refreshing with note:", note);
const { content } = await note.getNoteComplement();
//console.log("MDLG: Note content fetched:", content);
// Check if content is fetched properly
if (!content) {
//console.log("MDLG: No content fetched. Exiting refresh.");
return;
}
const highlightedContent = this.applyHighlighting(content);
//console.log("MDLG: Highlighted content:", highlightedContent);
if (highlightedContent !== content) {
//console.log("MDLG: Updating note content with highlighted version");
// Use the setNoteContent function to update the note content
try {
await setNoteContent(note.noteId, highlightedContent);
//console.log("MDLG: Content updated with highlighted version");
} catch (error) {
//console.error("MDLG: Error updating content:", error);
}
} else {
//console.log("MDLG: No changes to content, skipping update.");
}
}
applyHighlighting(content) {
// Replace <br> with \n, then remove all other HTML tags
const plainTextContent = content.replace(/<br\s*\/?\>/gi, '\n').replace(/<[^>]*>/g, ' ');
// Regular expressions for Todo.txt syntax
const datePattern = /\b(0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([1-2]\d{3})\b/g;
const completedTaskPattern = /^\s*x\s+.*/g;
const priorityPattern = /^(\s*\([A-E]\))\s+/;
const projectPattern = /(?:\s|^)(\+\S+)/g;
const contextPattern = /(?:\s|^)(@\S+)/g;
const waitPattern = /\bWAIT\b/gi;
const customAttributePattern = /(\s+[^\s:]+:[^\s:]+)+\s*$/g;
function escapeHtml(str) {
return str.replace(/[&<>"']/g, char => ({
'&': '&', '<': '<', '>': '>',
'"': '"', "'": '''
})[char]);
}
let highlightedLines = plainTextContent.split("\n").map(line => {
// If line starts with 'x', make the whole line grey
if (/^\s*x\s+/.test(line)) {
return `<span style="color: grey;">${line}</span>`;
}
// Highlight priority with different colours based on level
line = line.replace(priorityPattern, (match, priority) => {
const level = priority.match(/[A-E]/)?.[0];
const colors = {
'A': 'rgb(255, 0, 0)', // Red
'B': 'rgb(255, 102, 0)', // Orange
'C': 'rgb(255, 204, 0)', // Yellow
'D': 'rgb(0, 153, 255)', // Blue
'E': 'rgb(102, 204, 102)' // Green
};
const color = colors[level] || 'black';
return `<span style="color: ${color}; font-weight: bold;">${escapeHtml(priority)}</span> `;
});
// Highlight date
line = line.replace(datePattern, (match) => {
return `<span style="color: rgb(188, 55, 237);">${escapeHtml(match)}</span>`;
});
// Highlight project tags
line = line.replace(projectPattern, (_, tag) => `<span style="color: rgb(224, 218, 36);"> ${escapeHtml(tag)}</span>`);
// Highlight context tags
line = line.replace(contextPattern, (_, tag) => `<span style="color: rgb(255, 165, 10);"> ${escapeHtml(tag)}</span>`);
// Highlight WAIT command
line = line.replace(waitPattern, `<span style="color: rgb(255, 69, 0);">WAIT</span>`);
// Highlight custom attributes
line = line.replace(customAttributePattern, `<span style="color: rgb(100, 149, 237);">$1</span>`);
// Remove leading space (if any) from the beginning of the line, without affecting spaces before + or @
line = line.replace(/^\s+/, '');
return line.trim();
});
return highlightedLines.join("<br>");
}
async entitiesReloadedEvent({ loadResults }) {
//console.log("MDLG: Entities reloaded event triggered");
if (loadResults.isNoteContentReloaded(this.noteId)) {
//console.log("MDLG: Note content reloaded, refreshing widget...");
//this.refresh();
} else {
//console.log("MDLG: No content reload detected.");
}
}
}
console.log("TodoHighlight: Starting todotxt Highlighter Widget");
module.exports = MoodlogHighlighterWidget;
function setNoteContent(noteId, content) {
/*const noteElement = document.querySelector(`note-detail-readonly-text-content`);
if (noteElement) {
noteElement.innerHTML = content; // Update the displayed content
console.log(`MDLG: Updated note ${noteId} in the DOM.`);
} else {
console.warn(`MDLG: Note element with data-note-id='${noteId}' not found.`);
}*/
return api.runOnBackend((id, data) => api.getNote(id).setContent(data), [noteId, content]);
}