aboutsummaryrefslogtreecommitdiffstats
path: root/tiddlers/plugin-org-gtd-library.tid
diff options
context:
space:
mode:
Diffstat (limited to 'tiddlers/plugin-org-gtd-library.tid')
-rw-r--r--tiddlers/plugin-org-gtd-library.tid141
1 files changed, 141 insertions, 0 deletions
diff --git a/tiddlers/plugin-org-gtd-library.tid b/tiddlers/plugin-org-gtd-library.tid
new file mode 100644
index 0000000..af41028
--- /dev/null
+++ b/tiddlers/plugin-org-gtd-library.tid
@@ -0,0 +1,141 @@
+title: $:/plugins/local/org-gtd/library.js
+type: application/javascript
+module-type: library
+
+/*\
+title: $:/plugins/local/org-gtd/library.js
+type: application/javascript
+module-type: library
+\*/
+(function() {
+"use strict";
+
+var TODO_STATES = ["TODO","NEXT","WAIT","DONE"],
+ TODO_LOOKUP = Object.create(null),
+ HEADING_PATTERN = /^(\*+)\s+(?:(TODO|NEXT|WAIT|DONE)\s+)?(.*)$/,
+ TAG_PATTERN = /\s+:([A-Za-z0-9_@#%+.-]+(?::[A-Za-z0-9_@#%+.-]+)*):\s*$/,
+ DATE_PATTERN = /\b(SCHEDULED|DEADLINE):\s*<([0-9]{4}-[0-9]{2}-[0-9]{2})(?:\s+[^>]*)?>/g;
+
+TODO_STATES.forEach(function(state) {
+ TODO_LOOKUP[state] = true;
+});
+
+function parseHeading(line,lineIndex) {
+ var match = HEADING_PATTERN.exec(line),
+ tagMatch,
+ title,
+ tags = [];
+ if(!match) {
+ return null;
+ }
+ title = match[3];
+ tagMatch = TAG_PATTERN.exec(title);
+ if(tagMatch) {
+ tags = tagMatch[1].split(":");
+ title = title.substring(0,tagMatch.index).trim();
+ }
+ return {
+ level: match[1].length,
+ state: match[2] || "",
+ title: title,
+ tags: tags,
+ line: lineIndex,
+ sourceLine: lineIndex + 1,
+ endLine: lineIndex,
+ scheduled: "",
+ deadline: ""
+ };
+}
+
+function parse(text) {
+ var lines = (text || "").split("\n"),
+ headings = [],
+ current = null;
+ lines.forEach(function(line,index) {
+ var heading = parseHeading(line,index),
+ match;
+ if(heading) {
+ if(current) {
+ current.endLine = index - 1;
+ }
+ headings.push(heading);
+ current = heading;
+ return;
+ }
+ if(current) {
+ DATE_PATTERN.lastIndex = 0;
+ while((match = DATE_PATTERN.exec(line)) !== null) {
+ if(match[1] === "SCHEDULED") {
+ current.scheduled = match[2];
+ } else {
+ current.deadline = match[2];
+ }
+ }
+ }
+ });
+ if(current) {
+ current.endLine = lines.length - 1;
+ }
+ for(var index = 0; index < headings.length; index++) {
+ var heading = headings[index],
+ nextIndex = index + 1;
+ while(nextIndex < headings.length && headings[nextIndex].level > heading.level) {
+ nextIndex++;
+ }
+ heading.subtreeEndLine = nextIndex < headings.length ? headings[nextIndex].line - 1 : lines.length - 1;
+ }
+ return {lines: lines,headings: headings};
+}
+
+function headingAtLine(parsed,line) {
+ var result = null;
+ parsed.headings.forEach(function(heading) {
+ if(heading.line <= line && heading.subtreeEndLine >= line) {
+ if(!result || heading.level >= result.level) {
+ result = heading;
+ }
+ }
+ });
+ return result;
+}
+
+function cycleState(line) {
+ var heading = parseHeading(line,0),
+ stars,
+ nextState,
+ rest;
+ if(!heading) {
+ return null;
+ }
+ stars = new Array(heading.level + 1).join("*");
+ if(heading.state) {
+ nextState = TODO_STATES[(TODO_STATES.indexOf(heading.state) + 1) % TODO_STATES.length];
+ rest = line.replace(/^(\*+)\s+(?:TODO|NEXT|WAIT|DONE)\s+/,"$1 " + nextState + " ");
+ } else {
+ rest = line.replace(/^(\*+)\s+/,"$1 TODO ");
+ }
+ return {line: rest,state: nextState || "TODO",level: stars.length};
+}
+
+function dateToTime(value) {
+ var match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value || ""),
+ date;
+ if(!match) {
+ return NaN;
+ }
+ date = new Date(Number(match[1]),Number(match[2]) - 1,Number(match[3]));
+ if(date.getFullYear() !== Number(match[1]) || date.getMonth() !== Number(match[2]) - 1 || date.getDate() !== Number(match[3])) {
+ return NaN;
+ }
+ return date.getTime();
+}
+
+exports.TODO_STATES = TODO_STATES;
+exports.TODO_LOOKUP = TODO_LOOKUP;
+exports.parseHeading = parseHeading;
+exports.parse = parse;
+exports.headingAtLine = headingAtLine;
+exports.cycleState = cycleState;
+exports.dateToTime = dateToTime;
+
+})();