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; })();