nutools/legacy/twinc/TODO.js

441 lines
14 KiB
JavaScript

// -*- coding: utf-8 -*-
//@require base.js
/* convention de nommage:
funcTodo: fonction qui agit sur ou retourne une instance de Tiddler
funcTODO: fonction qui agit sur ou affiche un élément visuel.
*/
//////////////////////////////////////////////////
// Gestion des TODOs
function getAllTodos(types, expectedTags) {
// Lister les todos des types spécifiés ("TODO" ou "DONE"), et qui contiennent
// tous les tags de expectedTags.
if (!expectedTags) expectedTags = [];
var tiddlersMatchingTags = function(tiddlers, exceptedTags, sortBy, reverse) {
// retourner les tiddlers de tiddlers qui contiennent tous les tags de expectedTags
var result = [];
for(var i = 0; i < tiddlers.length; i++) {
if (expectedTags.length) {
var allTagsOk = true;
for (var j = 0; j < expectedTags.length; j++) {
if (! tiddlers[i].tags.containsTag(expectedTags[j])) {
allTagsOk = false;
break;
}
}
if (!allTagsOk) continue;
}
result.push(tiddlers[i]);
}
if (!sortBy) sortBy = "title";
result.sortBy(sortBy, reverse);
return result;
}
var result = [];
for (var i = 0; i < types.length; i++) {
var type = types[i];
var reverse = false;
if (type.charAt(0) == "+") {
type = type.substr(1);
} else if (type.charAt(0) == "-") {
type = type.substr(1);
var reverse = true;
}
result = result.concat(tiddlersMatchingTags(store.getTaggedTiddlers(type), expectedTags, "title", reverse));
}
return result;
}
function getTodoText(tiddler) {
// Retourner "[[todoTitle]] - firstLine"
if (!(tiddler instanceof Tiddler)) tiddler = store.tiddlers[tiddler];
if (!(tiddler instanceof Tiddler)) return;
var title = tiddler.title;
var text = "[[" + title + "]]";
var line = Tiddlers.getFirstLine(tiddler);
if (line != "") text += " - " + line;
return text;
}
//////////////////////////////////////////////////
// Les macros pour gérer les TODOs
// Créer un nouveau TODO
config.macros.newTODO = {label: "new TODO", prompt: "Create a new TODO tiddler", accessKey: "T"};
config.macros.newTODO.handler = function(place, macroName, params) {
if (!readOnly) {
createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
}
}
config.macros.newTODO.onClick = function(e) {
e = ClickHandler.getEvent(e);
var now = new Date();
var todoname = now.formatString("TODO-YYYY0MM0DD");
var title = todoname + "-" + (store.getTaggedTiddlers(todoname).length + 1);
clearMessage();
displayTiddler(null, title, 2, null, null, false, false);
var body = document.getElementById("editorBody" + title);
if (body) body.focus();
var tagsBox = document.getElementById("editorTags" + title);
if(tagsBox) {
var tags = tagsBox.value.splitTags();
tags.removeTag("DONE").addTag("TODO").addTag(todoname);
tagsBox.value = tags.joinTags();
}
return ClickHandler.end(e);
}
// Créer un nouveau bug
config.macros.newBUG = {label: "new BUG", prompt: "Create a new BUG tiddler", accessKey: "B"};
config.macros.newBUG.handler = function(place, macroName, params) {
if (!readOnly) {
createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
}
}
config.macros.newBUG.onClick = function(e) {
e = ClickHandler.getEvent(e);
var now = new Date();
var bugname = now.formatString("BUG-YYYY0MM0DD");
var title = bugname + "-" + (store.getTaggedTiddlers(bugname).length + 1);
clearMessage();
displayTiddler(null, title, 2, null, null, false, false);
var body = document.getElementById("editorBody" + title);
if (body) body.focus();
var tagsBox = document.getElementById("editorTags" + title);
if(tagsBox) {
var tags = tagsBox.value.splitTags();
tags.removeTag("DONE").addTag("BUG").addTag(bugname);
tagsBox.value = tags.joinTags();
}
return ClickHandler.end(e);
}
// marquer un TODO comme étant effectué
config.macros.markDONE = {label: "mark as DONE", prompt: "Mark a TODO/BUG tiddler as DONE", accessKey: "D"};
config.macros.markDONE.handler = function(place, macroName, params) {
if (!readOnly) {
createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
}
}
config.macros.markDONE.onClick = function(e) {
e = ClickHandler.getEvent(e);
if (currentTiddler) {
var title = currentTiddler;
var tags = Tiddlers.valueOf(title).tags;
if (tags.containsTag("TODO") || tags.containsTag("BUG")) {
clearMessage();
displayTiddler(null, title, 2, null, null, false, false);
var tagsBox = document.getElementById("editorTags" + title);
if(tagsBox) {
var tags = tagsBox.value.splitTags();
tags.removeTag("TODO").removeTag("BUG").addTag("DONE");
tagsBox.value = tags.joinTags();
var body = document.getElementById("editorBody" + title);
var text = body.value;
if (text.length > 0 && text.charAt(text.length - 1) != "\n") text += "\n";
text += "\nFAIT le " + new Date().formatString("0DD/0MM/YYYY");
body.value = text;
body.focus();
}
}
}
return ClickHandler.end(e);
}
// Afficher un lien vers un TODO et une description du TODO
config.macros.todoLink = {};
config.macros.todoLink.handler = function(place, macroName, params) {
if (!params.length) return;
wikify(getTodoText(params[0]), place);
}
// Lister tous les TODOs, ceux à faire et ceux qui sont faits
config.macros.listTODOs = {};
config.macros.listTODOs.handler = function(place, macroName, params) {
var todos = getAllTodos(["-TODO", "BUG", "DONE"], params);
if (todos.length) {
var list = document.createElement("ul");
place.appendChild(list);
for(var i = 0; i < todos.length; i++) {
item = document.createElement("li");
list.appendChild(item);
var element = item;
var tags = todos[i].tags;
if (tags.containsTag("DONE")) {
striked = createTiddlyElement(element, "strike");
element = striked;
}
wikify(getTodoText(todos[i]), element);
}
} else {
place.appendChild(document.createTextNode("Il n'y a pas de TODOs en cours"));
}
}
function refreshTODOs(hint) {
refreshTiddler("TODOs");
}
config.notifyTiddlers.push({name: null, notify: refreshTODOs});
config.shadowTiddlers.TODOs = "<<listTODOs>>";
// Créer une nouvelle release
config.macros.newRelease = {label: "new release", prompt: "Create a new release", accessKey: "R"};
config.macros.newRelease.handler = function(place, macroName, params) {
if (!readOnly) {
createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
}
}
config.macros.newRelease.onClick = function(e) {
e = ClickHandler.getEvent(e);
var now = new Date();
var releasename = now.formatString("Release-YYYY0MM0DD");
var title = releasename;
var releases = store.getTaggedTiddlers(releasename);
if (releases.length > 0) {
title += "-" + releases.length;
}
clearMessage();
displayTiddler(null, title, 2, null, null, false, false);
var body = document.getElementById("editorBody" + title);
if (body && body.value == "") {
// ne créer le contenu que la première fois.
var text = "release du " + now.formatString("0DD/0MM/YYYY") + "\nversion: \n";
// lister tous les TODOs faits en cours
var todos = getAllTodos(["DONE"]);
for (var i = 0; i < todos.length; i++) {
if (i == 0) text += "\n";
text += "* <<todoLink " + todos[i].title + ">>\n";
}
body.value = text;
body.focus();
}
var tagsBox = document.getElementById("editorTags" + title);
if(tagsBox) {
var tags = tagsBox.value.splitTags();
tags.addTag("release").addTag(releasename);
tagsBox.value = tags.joinTags();
}
return ClickHandler.end(e);
}
TiddlyWiki.prototype.saveTiddler_patchedByTODO = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title, newTitle, newBody, modifier, modified, tags) {
var tiddler = this.saveTiddler_patchedByTODO(title, newTitle, newBody, modifier, modified, tags);
if (tiddler.tags.containsTag("release")) {
var todos = store.getTaggedTiddlers("DONE");
for (var i = 0; i < todos.length; i++) {
var todo = todos[i];
todo.tags.removeTag("DONE").addTag("RELEASED");
store.saveTiddler(todo.title, todo.title, todo.text, todo.modifier, undefined, todo.tags);
refreshTiddler(todos[i].title);
}
}
return tiddler;
}
function getVersionText(tiddler) {
var version = "";
var tiddler = Tiddlers.valueOf(tiddler);
if (tiddler) {
var re_version = new RegExp("^version:(.*)$", "gm");
var matched = re_version.exec(tiddler.text);
if (matched) {
version = matched[1].trim();
}
}
return version;
}
function getReleaseText(tiddler) {
// Retourner "[[versionText|releaseTitle]] - firstLine"
var text = "";
var tiddler = Tiddlers.valueOf(tiddler);
if (tiddler) {
var title = tiddler.title;
var version = getVersionText(tiddler);
if (version != "") {
text = "[[" + version + "|" + title + "]]";
} else {
text = "[[" + title + "]]";
}
var line = Tiddlers.getFirstLine(tiddler);
if (line != "") text += " - " + line;
}
return text;
}
config.macros.listReleases = {};
config.macros.listReleases.handler = function(place, macroName, params) {
var releases = store.getTaggedTiddlers("release");
releases.sortBy("title", true);
if (releases.length) {
var list = document.createElement("ul");
place.appendChild(list);
var len = releases.length;
if (len > 7) len = 7;
for(var i = 0; i < len; i++) {
item = document.createElement("li");
list.appendChild(item);
wikify(getReleaseText(releases[i]), item);
}
} else {
place.appendChild(document.createTextNode("Il n'y a pas de releases en cours"));
}
}
function refreshReleases(hint) {
refreshTiddler("Releases");
}
config.notifyTiddlers.push({name: null, notify: refreshReleases});
config.shadowTiddlers.Releases = "<<listReleases>>";
//////////////////////////////////////////////////
// Le formatter todo
// ce formatter affiche une case à cocher qu'il est possible de cocher pour signifier que l'action
// associée à la case à cocher est effectuée
// format: [] pour les actions non faites
// [x] pour les actions faites
var lastTodoId;
var displayTiddler_patchedByTodo = window.displayTiddler;
window.displayTiddler = function(src, title, state, highlightText, highlightCaseSensitive, animate, slowly) {
// réinitialiser le numéro du dernier todo à chaque affichage
lastTodoId = 0;
displayTiddler_patchedByTodo(src, title, state, highlightText, highlightCaseSensitive, animate, slowly);
}
config.formatters.push({
name: "todo",
match: "\\[\\]|\\[[xX\\*]\\]",
terminator: "\\n|\\|",
handler: function(w) {
var formatter = this;
var place = w.output;
var tiddler = findContainingTiddler(place);
if (!tiddler) return;
var title = tiddler.id.substr(7);
var todoId = lastTodoId++;
var cbId = "todo" + todoId + "@" + title;
var wrapperId = "todoWrapper" + todoId + "@:" + title;
var matched = w.source.substr(w.matchStart, w.matchLength);
var done = matched != "[]";
var onClick = function(e) {
e = ClickHandler.getEvent(e);
if (!readOnly) {
// lire les infos sur le tiddler
var body = store.getTiddlerText(title);
var tags = Tiddlers.getTags(title);
// trouver le texte [] qui correspond à todoId
var id = 0;
var re_macro = new RegExp(formatter.match, "mg");
do {
var mo = re_macro.exec(body);
if (mo) {
if (id++ == todoId) break;
}
} while (mo);
// changer la valeur
if (mo) {
var done = mo[0] == "[]";
body = body.substr(0, mo.index) + (done? "[x]": "[]") + body.substr(mo.index + mo[0].length);
lastTodoId = 0;
store.saveTiddler(title, title, body, config.options.txtUserName, new Date(), tags);
if (config.options.chkAutoSave) saveChanges();
refreshTiddler(title);
}
}
return ClickHandler.end(e);
};
var span = createTiddlyElement(place, "span");
var cb = createTiddlyElement(span, "input");
cb.type = "checkbox";
cb.id = cbId;
cb.checked = done;
cb.onclick = onClick;
var label = createTiddlyElement(span, "label");
label.setAttribute("for", cbId);
var wrapper = createTiddlyElement(label, "span", wrapperId);
if (done) {
wrapper.setAttribute("style", "text-decoration: line-through;");
}
w.subWikify(wrapper, this.terminator);
w.nextMatch--; // ne pas manger le terminator
}
});
//////////////////////////////////////////////////
// L'interface par défaut
config.shadowTiddlers.MainMenu = "\
<<calendar>>\n\
<<newTiddler>>\n\
----\n\
[[Releases]]\n\
<<newRelease>>\n\
----\n\
[[TODOs]]\n\
<<newTODO>>\n\
<<newBUG>>\n\
<<markDONE>>\
";
config.shadowTiddlers.DefaultTiddlers = "\
Releases\n\
TODOs\n\
EnCours\
";
config.shadowTiddlers.SiteTitle = "TODO";
config.shadowTiddlers.SiteSubtitle = "Gestion de tâches";
config.shadowTiddlers.KeyShortcuts += "\
|Alt + T|Créer un nouveau TODO|\n\
|Alt + B|Créer un nouveau BUG|\n\
|Alt + D|Terminer un TODO|\n\
|Alt + R|Créer une nouvelle release|\n\
";