title: $:/plugins/local/org-gtd/agenda-widget.js type: application/javascript module-type: widget /*\ title: $:/plugins/local/org-gtd/agenda-widget.js type: application/javascript module-type: widget \*/ (function() { "use strict"; var Widget = require("$:/core/modules/widgets/widget.js").widget, org = require("$:/plugins/local/org-gtd/library.js"), JUMP_STATE_TITLE = "$:/state/org-gtd/jump"; var OrgGtdAgendaWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); this.parseCache = Object.create(null); }; OrgGtdAgendaWidget.prototype = new Widget(); OrgGtdAgendaWidget.prototype.constructor = OrgGtdAgendaWidget; function startOfDay(date) { return new Date(date.getFullYear(),date.getMonth(),date.getDate()).getTime(); } function earliestDate(task) { var values = [task.scheduled,task.deadline].filter(function(value) { return !isNaN(org.dateToTime(value)); }); values.sort(); return values[0] || ""; } OrgGtdAgendaWidget.prototype.render = function(parent,nextSibling) { this.parentDomNode = parent; this.computeAttributes(); this.execute(); var container = this.document.createElement("div"); container.className = "tc-org-agenda"; parent.insertBefore(container,nextSibling); this.domNodes.push(container); this.containerNode = container; this.renderAgenda(); }; OrgGtdAgendaWidget.prototype.execute = function() { this.days = Math.max(1,Number(this.getAttribute("days","7")) || 7); }; OrgGtdAgendaWidget.prototype.createButton = function(label,className,handler) { var button = this.document.createElement("button"); button.type = "button"; button.className = "tc-btn-invisible " + className; button.textContent = label; button.addEventListener("click",handler); return button; }; OrgGtdAgendaWidget.prototype.getTasks = function() { var self = this, tasks = [], activeTitles = Object.create(null); this.wiki.allTitles().forEach(function(title) { var tiddler = self.wiki.getTiddler(title), text, cached, parsed; if(!tiddler || tiddler.fields.type !== "text/x-org" || tiddler.fields["draft.of"]) { return; } activeTitles[title] = true; text = tiddler.fields.text || ""; cached = self.parseCache[title]; parsed = cached && cached.text === text ? cached.parsed : org.parse(text); self.parseCache[title] = {text: text,parsed: parsed}; parsed.headings.forEach(function(heading) { if(heading.state && heading.state !== "DONE" && heading.title) { tasks.push({ tiddler: title, title: heading.title, state: heading.state, level: heading.level, line: heading.sourceLine, scheduled: heading.scheduled, deadline: heading.deadline, date: earliestDate(heading) }); } }); }); Object.keys(this.parseCache).forEach(function(title) { if(!activeTitles[title]) { delete self.parseCache[title]; } }); return tasks; }; OrgGtdAgendaWidget.prototype.openTask = function(task,event) { this.wiki.setText(JUMP_STATE_TITLE,"text",null,JSON.stringify({title: task.tiddler,line: task.line})); this.dispatchEvent({ type: "tm-edit-tiddler", param: task.tiddler, tiddlerTitle: task.tiddler, navigateFromTitle: this.getVariable("storyTiddler"), event: event }); }; OrgGtdAgendaWidget.prototype.renderTask = function(parent,task) { var self = this, button = this.createButton(task.title,"tc-org-agenda-task",function(event) { self.openTask(task,event); }), state = this.document.createElement("span"), source = this.document.createElement("span"), meta = this.document.createElement("span"), row = this.document.createElement("div"), parts = []; row.className = "tc-org-agenda-row"; state.className = "tc-org-state tc-org-state-" + task.state.toLowerCase(); state.textContent = task.state; source.className = "tc-org-agenda-source"; source.textContent = task.tiddler; if(task.scheduled) { parts.push("计划 " + task.scheduled); } if(task.deadline) { parts.push("截止 " + task.deadline); } meta.className = "tc-org-agenda-meta"; meta.textContent = parts.join(" · "); row.appendChild(state); row.appendChild(button); row.appendChild(source); if(parts.length) { row.appendChild(meta); } parent.appendChild(row); }; OrgGtdAgendaWidget.prototype.renderSection = function(title,tasks,className) { var self = this, section = this.document.createElement("section"), heading = this.document.createElement("h2"), count = this.document.createElement("span"); section.className = "tc-org-agenda-section " + className; heading.className = "tc-org-agenda-section-title"; heading.textContent = title; count.className = "tc-org-agenda-count"; count.textContent = String(tasks.length); heading.appendChild(count); section.appendChild(heading); if(tasks.length) { tasks.forEach(function(task) { self.renderTask(section,task); }); } else { var empty = this.document.createElement("p"); empty.className = "tc-org-agenda-empty"; empty.textContent = "没有事项"; section.appendChild(empty); } this.containerNode.appendChild(section); }; OrgGtdAgendaWidget.prototype.renderAgenda = function() { var self = this, today = startOfDay(new Date()), horizonDate = new Date(today), horizon, groups = {overdue: [],today: [],upcoming: [],next: []}, toolbar = this.document.createElement("div"), newButton; horizonDate.setDate(horizonDate.getDate() + this.days); horizon = horizonDate.getTime(); newButton = this.createButton("+ 新建 Org 文档","tc-org-agenda-new",function(event) { var title = self.wiki.generateNewTitle("GTD Inbox"); self.dispatchEvent({ type: "tm-new-tiddler", paramObject: {title: title,type: "text/x-org",text: "* TODO "}, navigateFromTitle: self.getVariable("storyTiddler"), event: event }); }); toolbar.className = "tc-org-agenda-toolbar"; toolbar.appendChild(newButton); this.containerNode.appendChild(toolbar); this.getTasks().forEach(function(task) { var time = org.dateToTime(task.date); if(!isNaN(time)) { if(time < today) { groups.overdue.push(task); } else if(time === today) { groups.today.push(task); } else if(time <= horizon) { groups.upcoming.push(task); } } else if(task.state === "NEXT") { groups.next.push(task); } }); Object.keys(groups).forEach(function(key) { groups[key].sort(function(left,right) { return (left.date || "9999").localeCompare(right.date || "9999") || left.title.localeCompare(right.title,"zh-Hans"); }); }); this.renderSection("逾期",groups.overdue,"tc-org-agenda-overdue"); this.renderSection("今天",groups.today,"tc-org-agenda-today"); this.renderSection("未来 " + this.days + " 天",groups.upcoming,"tc-org-agenda-upcoming"); this.renderSection("无日期的下一步",groups.next,"tc-org-agenda-next"); }; OrgGtdAgendaWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); if(Object.keys(changedAttributes).length || Object.keys(changedTiddlers).length) { this.refreshSelf(); return true; } return false; }; exports["org-gtd-agenda"] = OrgGtdAgendaWidget; })();