2026-07-07 09:41:15 -03:00
|
|
|
/*
|
|
|
|
|
* OrganonTweaks -- enhance Kanboard's native "Edit recurrence" dialog.
|
|
|
|
|
*
|
|
|
|
|
* That template (task_recurrence/edit.php) has no hook, so we transform the form in JS when
|
|
|
|
|
* the modal opens:
|
|
|
|
|
* 1. Replace the "Generate recurrent task" Yes/No dropdown with two radio buttons (one
|
|
|
|
|
* click instead of open + select). The original <select> is kept, hidden, and stays in
|
|
|
|
|
* sync, so the form still submits recurrence_status normally.
|
|
|
|
|
* 2. Add a small [+7] button next to the "Factor" input that adds 7 (a week of days).
|
|
|
|
|
*
|
|
|
|
|
* Detection: the modal is injected into #modal-box dynamically, so we watch the document for
|
|
|
|
|
* an un-enhanced recurrence form appearing. Loaded only when the setting is enabled.
|
|
|
|
|
*/
|
|
|
|
|
(function () {
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
function enhanceStatus(select) {
|
|
|
|
|
var group = document.createElement("span");
|
|
|
|
|
group.className = "ot-recurrence-status";
|
|
|
|
|
|
2026-07-07 09:51:38 -03:00
|
|
|
var radios = [];
|
|
|
|
|
|
2026-07-07 09:41:15 -03:00
|
|
|
for (var i = 0; i < select.options.length; i++) {
|
|
|
|
|
var opt = select.options[i];
|
|
|
|
|
|
|
|
|
|
var label = document.createElement("label");
|
2026-07-07 09:43:54 -03:00
|
|
|
// Kanboard styles form labels as display:block; force inline so Yes/No sit
|
|
|
|
|
// side by side instead of stacked.
|
|
|
|
|
label.style.display = "inline-block";
|
2026-07-07 09:41:15 -03:00
|
|
|
label.style.marginRight = "14px";
|
|
|
|
|
label.style.cursor = "pointer";
|
|
|
|
|
|
|
|
|
|
var radio = document.createElement("input");
|
|
|
|
|
radio.type = "radio";
|
2026-07-07 09:51:38 -03:00
|
|
|
// No name attribute on purpose: a named radio would be SUBMITTED with the form,
|
|
|
|
|
// and Kanboard rejects the extra key ("invalid identifier"). These are display
|
|
|
|
|
// only -- the hidden <select> carries the value -- so we enforce the mutual
|
|
|
|
|
// exclusivity ourselves.
|
2026-07-07 09:41:15 -03:00
|
|
|
radio.value = opt.value;
|
|
|
|
|
radio.checked = opt.value === select.value;
|
|
|
|
|
radio.style.marginRight = "4px";
|
2026-07-07 09:51:38 -03:00
|
|
|
radios.push(radio);
|
|
|
|
|
|
2026-07-07 09:41:15 -03:00
|
|
|
radio.addEventListener("change", function () {
|
2026-07-07 09:51:38 -03:00
|
|
|
for (var k = 0; k < radios.length; k++) {
|
|
|
|
|
radios[k].checked = (radios[k] === this);
|
2026-07-07 09:41:15 -03:00
|
|
|
}
|
2026-07-07 09:51:38 -03:00
|
|
|
select.value = this.value;
|
|
|
|
|
select.dispatchEvent(new Event("change", { bubbles: true }));
|
2026-07-07 09:41:15 -03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
label.appendChild(radio);
|
|
|
|
|
label.appendChild(document.createTextNode(opt.text));
|
|
|
|
|
group.appendChild(label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select.style.display = "none";
|
|
|
|
|
select.parentNode.insertBefore(group, select.nextSibling);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function enhanceFactor(input) {
|
|
|
|
|
var btn = document.createElement("button");
|
|
|
|
|
btn.type = "button";
|
|
|
|
|
btn.className = "btn";
|
|
|
|
|
btn.textContent = "+7";
|
|
|
|
|
btn.title = "Add 7 (one week of days)";
|
|
|
|
|
btn.style.marginLeft = "6px";
|
|
|
|
|
btn.addEventListener("click", function () {
|
|
|
|
|
input.value = (parseInt(input.value, 10) || 0) + 7;
|
|
|
|
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
input.parentNode.insertBefore(btn, input.nextSibling);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function enhance() {
|
|
|
|
|
var select = document.querySelector('select[name="recurrence_status"]:not([data-ot-recurrence])');
|
|
|
|
|
if (!select) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select.setAttribute("data-ot-recurrence", "1");
|
|
|
|
|
enhanceStatus(select);
|
|
|
|
|
|
|
|
|
|
var factor = document.querySelector('input[name="recurrence_factor"]:not([data-ot-recurrence])');
|
|
|
|
|
if (factor) {
|
|
|
|
|
factor.setAttribute("data-ot-recurrence", "1");
|
|
|
|
|
enhanceFactor(factor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
|
enhance();
|
|
|
|
|
|
|
|
|
|
if (window.MutationObserver) {
|
|
|
|
|
new MutationObserver(enhance).observe(document.body, { childList: true, subtree: true });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (document.readyState === "loading") {
|
|
|
|
|
document.addEventListener("DOMContentLoaded", init);
|
|
|
|
|
} else {
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
})();
|